github.com/llir/llvm@v0.3.6/ir/terminator.go (about)

     1  package ir
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/llir/llvm/ir/constant"
     8  	"github.com/llir/llvm/ir/enum"
     9  	"github.com/llir/llvm/ir/types"
    10  	"github.com/llir/llvm/ir/value"
    11  )
    12  
    13  // === [ Terminators ] =========================================================
    14  
    15  // Terminator is an LLVM IR terminator instruction (a control flow instruction).
    16  //
    17  // A Terminator has one of the following underlying types.
    18  //
    19  // Terminators
    20  //
    21  // https://llvm.org/docs/LangRef.html#terminator-instructions
    22  //
    23  //    *ir.TermRet           // https://pkg.go.dev/github.com/llir/llvm/ir#TermRet
    24  //    *ir.TermBr            // https://pkg.go.dev/github.com/llir/llvm/ir#TermBr
    25  //    *ir.TermCondBr        // https://pkg.go.dev/github.com/llir/llvm/ir#TermCondBr
    26  //    *ir.TermSwitch        // https://pkg.go.dev/github.com/llir/llvm/ir#TermSwitch
    27  //    *ir.TermIndirectBr    // https://pkg.go.dev/github.com/llir/llvm/ir#TermIndirectBr
    28  //    *ir.TermInvoke        // https://pkg.go.dev/github.com/llir/llvm/ir#TermInvoke
    29  //    *ir.TermCallBr        // https://pkg.go.dev/github.com/llir/llvm/ir#TermCallBr
    30  //    *ir.TermResume        // https://pkg.go.dev/github.com/llir/llvm/ir#TermResume
    31  //    *ir.TermCatchSwitch   // https://pkg.go.dev/github.com/llir/llvm/ir#TermCatchSwitch
    32  //    *ir.TermCatchRet      // https://pkg.go.dev/github.com/llir/llvm/ir#TermCatchRet
    33  //    *ir.TermCleanupRet    // https://pkg.go.dev/github.com/llir/llvm/ir#TermCleanupRet
    34  //    *ir.TermUnreachable   // https://pkg.go.dev/github.com/llir/llvm/ir#TermUnreachable
    35  type Terminator interface {
    36  	LLStringer
    37  	// Succs returns the successor basic blocks of the terminator.
    38  	Succs() []*Block
    39  	// Terminator implements the value.User interface.
    40  	value.User
    41  }
    42  
    43  // --- [ ret ] -----------------------------------------------------------------
    44  
    45  // TermRet is an LLVM IR ret terminator.
    46  type TermRet struct {
    47  	// Return value; or nil if void return.
    48  	X value.Value
    49  
    50  	// extra.
    51  
    52  	// (optional) Metadata.
    53  	Metadata
    54  }
    55  
    56  // NewRet returns a new ret terminator based on the given return value. A nil
    57  // return value indicates a void return.
    58  func NewRet(x value.Value) *TermRet {
    59  	return &TermRet{X: x}
    60  }
    61  
    62  // Succs returns the successor basic blocks of the terminator.
    63  func (*TermRet) Succs() []*Block {
    64  	// no successors.
    65  	return nil
    66  }
    67  
    68  // Operands returns a mutable list of operands of the given terminator.
    69  func (term *TermRet) Operands() []*value.Value {
    70  	if term.X != nil {
    71  		return []*value.Value{&term.X}
    72  	}
    73  	return nil
    74  }
    75  
    76  // LLString returns the LLVM syntax representation of the terminator.
    77  //
    78  // Void return instruction.
    79  //
    80  //    'ret' XTyp=VoidType Metadata=(',' MetadataAttachment)+?
    81  //
    82  // Value return instruction.
    83  //
    84  //    'ret' XTyp=ConcreteType X=Value Metadata=(',' MetadataAttachment)+?
    85  func (term *TermRet) LLString() string {
    86  	buf := &strings.Builder{}
    87  	if term.X == nil {
    88  		buf.WriteString("ret void")
    89  	} else {
    90  		fmt.Fprintf(buf, "ret %s", term.X)
    91  	}
    92  	for _, md := range term.Metadata {
    93  		fmt.Fprintf(buf, ", %s", md)
    94  	}
    95  	return buf.String()
    96  }
    97  
    98  // --- [ br ] ------------------------------------------------------------------
    99  
   100  // TermBr is an unconditional LLVM IR br terminator.
   101  type TermBr struct {
   102  	// Target branch.
   103  	Target value.Value // *ir.Block
   104  
   105  	// extra.
   106  
   107  	// Successor basic blocks of the terminator.
   108  	Successors []*Block
   109  	// (optional) Metadata.
   110  	Metadata
   111  }
   112  
   113  // NewBr returns a new unconditional br terminator based on the given target
   114  // basic block.
   115  func NewBr(target *Block) *TermBr {
   116  	return &TermBr{Target: target}
   117  }
   118  
   119  // Succs returns the successor basic blocks of the terminator.
   120  func (term *TermBr) Succs() []*Block {
   121  	// Cache successors if not present.
   122  	if term.Successors == nil {
   123  		term.Successors = []*Block{term.Target.(*Block)}
   124  	}
   125  	return term.Successors
   126  }
   127  
   128  // Operands returns a mutable list of operands of the given terminator.
   129  func (term *TermBr) Operands() []*value.Value {
   130  	return []*value.Value{&term.Target}
   131  }
   132  
   133  // LLString returns the LLVM syntax representation of the terminator.
   134  //
   135  // 'br' Target=Label Metadata=(',' MetadataAttachment)+?
   136  func (term *TermBr) LLString() string {
   137  	buf := &strings.Builder{}
   138  	fmt.Fprintf(buf, "br %s", term.Target)
   139  	for _, md := range term.Metadata {
   140  		fmt.Fprintf(buf, ", %s", md)
   141  	}
   142  	return buf.String()
   143  }
   144  
   145  // --- [ conditional br ] ------------------------------------------------------
   146  
   147  // TermCondBr is a conditional LLVM IR br terminator.
   148  type TermCondBr struct {
   149  	// Branching condition.
   150  	Cond value.Value
   151  	// True condition target branch.
   152  	TargetTrue value.Value // *ir.Block
   153  	// False condition target branch.
   154  	TargetFalse value.Value // *ir.Block
   155  
   156  	// extra.
   157  
   158  	// Successor basic blocks of the terminator.
   159  	Successors []*Block
   160  	// (optional) Metadata.
   161  	Metadata
   162  }
   163  
   164  // NewCondBr returns a new conditional br terminator based on the given
   165  // branching condition and conditional target basic blocks.
   166  func NewCondBr(cond value.Value, targetTrue, targetFalse *Block) *TermCondBr {
   167  	return &TermCondBr{Cond: cond, TargetTrue: targetTrue, TargetFalse: targetFalse}
   168  }
   169  
   170  // Succs returns the successor basic blocks of the terminator.
   171  func (term *TermCondBr) Succs() []*Block {
   172  	// Cache successors if not present.
   173  	if term.Successors == nil {
   174  		term.Successors = []*Block{term.TargetTrue.(*Block), term.TargetFalse.(*Block)}
   175  	}
   176  	return term.Successors
   177  }
   178  
   179  // Operands returns a mutable list of operands of the given terminator.
   180  func (term *TermCondBr) Operands() []*value.Value {
   181  	return []*value.Value{&term.Cond, &term.TargetTrue, &term.TargetFalse}
   182  }
   183  
   184  // LLString returns the LLVM syntax representation of the terminator.
   185  //
   186  // 'br' CondTyp=IntType Cond=Value ',' TargetTrue=Label ',' TargetFalse=Label Metadata=(',' MetadataAttachment)+?
   187  func (term *TermCondBr) LLString() string {
   188  	buf := &strings.Builder{}
   189  	fmt.Fprintf(buf, "br %s, %s, %s", term.Cond, term.TargetTrue, term.TargetFalse)
   190  	for _, md := range term.Metadata {
   191  		fmt.Fprintf(buf, ", %s", md)
   192  	}
   193  	return buf.String()
   194  }
   195  
   196  // --- [ switch ] --------------------------------------------------------------
   197  
   198  // TermSwitch is an LLVM IR switch terminator.
   199  type TermSwitch struct {
   200  	// Control variable.
   201  	X value.Value
   202  	// Default target branch.
   203  	TargetDefault value.Value // *ir.Block
   204  	// Switch cases.
   205  	Cases []*Case
   206  
   207  	// extra.
   208  
   209  	// Successor basic blocks of the terminator.
   210  	Successors []*Block
   211  	// (optional) Metadata.
   212  	Metadata
   213  }
   214  
   215  // NewSwitch returns a new switch terminator based on the given control
   216  // variable, default target basic block and switch cases.
   217  func NewSwitch(x value.Value, targetDefault *Block, cases ...*Case) *TermSwitch {
   218  	return &TermSwitch{X: x, TargetDefault: targetDefault, Cases: cases}
   219  }
   220  
   221  // Succs returns the successor basic blocks of the terminator.
   222  func (term *TermSwitch) Succs() []*Block {
   223  	// Cache successors if not present.
   224  	if term.Successors == nil {
   225  		succs := make([]*Block, 0, 1+len(term.Cases))
   226  		succs = append(succs, term.TargetDefault.(*Block))
   227  		for _, c := range term.Cases {
   228  			succs = append(succs, c.Target.(*Block))
   229  		}
   230  		term.Successors = succs
   231  	}
   232  	return term.Successors
   233  }
   234  
   235  // Operands returns a mutable list of operands of the given terminator.
   236  func (term *TermSwitch) Operands() []*value.Value {
   237  	ops := make([]*value.Value, 0, 1+1+2*len(term.Cases))
   238  	ops = append(ops, &term.X)
   239  	ops = append(ops, &term.TargetDefault)
   240  	for i := range term.Cases {
   241  		ops = append(ops, &term.Cases[i].X)
   242  		ops = append(ops, &term.Cases[i].Target)
   243  	}
   244  	return ops
   245  }
   246  
   247  // LLString returns the LLVM syntax representation of the terminator.
   248  //
   249  // 'switch' X=TypeValue ',' Default=Label '[' Cases=Case* ']' Metadata=(',' MetadataAttachment)+?
   250  func (term *TermSwitch) LLString() string {
   251  	buf := &strings.Builder{}
   252  	fmt.Fprintf(buf, "switch %s, %s [\n", term.X, term.TargetDefault)
   253  	for _, c := range term.Cases {
   254  		fmt.Fprintf(buf, "\t\t%s\n", c)
   255  	}
   256  	buf.WriteString("\t]")
   257  	for _, md := range term.Metadata {
   258  		fmt.Fprintf(buf, ", %s", md)
   259  	}
   260  	return buf.String()
   261  }
   262  
   263  // ~~~ [ Switch case ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   264  
   265  // Case is a switch case.
   266  type Case struct {
   267  	// Case comparand.
   268  	X value.Value // constant.Constant (integer constant or integer constant expression)
   269  	// Case target branch.
   270  	Target value.Value // *ir.Block
   271  }
   272  
   273  // NewCase returns a new switch case based on the given case comparand and
   274  // target basic block.
   275  func NewCase(x constant.Constant, target *Block) *Case {
   276  	return &Case{X: x, Target: target}
   277  }
   278  
   279  // String returns the string representation of the switch case.
   280  func (c *Case) String() string {
   281  	// X=TypeConst ',' Target=Label
   282  	return fmt.Sprintf("%s, %s", c.X, c.Target)
   283  }
   284  
   285  // --- [ indirectbr ] ----------------------------------------------------------
   286  
   287  // TermIndirectBr is an LLVM IR indirectbr terminator.
   288  type TermIndirectBr struct {
   289  	// Target address.
   290  	Addr value.Value // blockaddress
   291  	// Set of valid target basic blocks.
   292  	ValidTargets []value.Value // slice of *ir.Block
   293  
   294  	// extra.
   295  
   296  	// Successor basic blocks of the terminator.
   297  	Successors []*Block
   298  	// (optional) Metadata.
   299  	Metadata
   300  }
   301  
   302  // NewIndirectBr returns a new indirectbr terminator based on the given target
   303  // address (derived from a blockaddress constant of type i8*) and set of valid
   304  // target basic blocks.
   305  func NewIndirectBr(addr value.Value, validTargets ...*Block) *TermIndirectBr {
   306  	// convert validTargets slice to []value.Value.
   307  	var targets []value.Value
   308  	for _, target := range validTargets {
   309  		targets = append(targets, target)
   310  	}
   311  	return &TermIndirectBr{Addr: addr, ValidTargets: targets}
   312  }
   313  
   314  // Succs returns the successor basic blocks of the terminator.
   315  func (term *TermIndirectBr) Succs() []*Block {
   316  	// Cache successors if not present.
   317  	if term.Successors == nil {
   318  		// convert ValidTargets slice to []*ir.Block.
   319  		for _, target := range term.ValidTargets {
   320  			term.Successors = append(term.Successors, target.(*Block))
   321  		}
   322  	}
   323  	return term.Successors
   324  }
   325  
   326  // Operands returns a mutable list of operands of the given terminator.
   327  func (term *TermIndirectBr) Operands() []*value.Value {
   328  	ops := make([]*value.Value, 0, 1+len(term.ValidTargets))
   329  	ops = append(ops, &term.Addr)
   330  	for i := range term.ValidTargets {
   331  		ops = append(ops, &term.ValidTargets[i])
   332  	}
   333  	return ops
   334  }
   335  
   336  // LLString returns the LLVM syntax representation of the terminator.
   337  //
   338  // 'indirectbr' Addr=TypeValue ',' '[' ValidTargets=(Label separator ',')* ']' Metadata=(',' MetadataAttachment)+?
   339  func (term *TermIndirectBr) LLString() string {
   340  	buf := &strings.Builder{}
   341  	fmt.Fprintf(buf, "indirectbr %s, [", term.Addr)
   342  	for i, target := range term.ValidTargets {
   343  		if i != 0 {
   344  			buf.WriteString(", ")
   345  		}
   346  		buf.WriteString(target.String())
   347  	}
   348  	buf.WriteString("]")
   349  	for _, md := range term.Metadata {
   350  		fmt.Fprintf(buf, ", %s", md)
   351  	}
   352  	return buf.String()
   353  }
   354  
   355  // --- [ invoke ] --------------------------------------------------------------
   356  
   357  // TermInvoke is an LLVM IR invoke terminator.
   358  type TermInvoke struct {
   359  	// Name of local variable associated with the result.
   360  	LocalIdent
   361  	// Invokee (callee function).
   362  	// TODO: specify the set of underlying types of Invokee.
   363  	Invokee value.Value
   364  	// Function arguments.
   365  	//
   366  	// Arg has one of the following underlying types:
   367  	//    value.Value
   368  	//    TODO: add metadata value?
   369  	Args []value.Value
   370  	// Normal control flow return point.
   371  	NormalRetTarget value.Value // *ir.Block
   372  	// Exception control flow return point.
   373  	ExceptionRetTarget value.Value // *ir.Block
   374  
   375  	// extra.
   376  
   377  	// Type of result produced by the terminator.
   378  	Typ types.Type
   379  	// Successor basic blocks of the terminator.
   380  	Successors []*Block
   381  	// (optional) Calling convention; zero if not present.
   382  	CallingConv enum.CallingConv
   383  	// (optional) Return attributes.
   384  	ReturnAttrs []ReturnAttribute
   385  	// (optional) Address space; zero if not present.
   386  	AddrSpace types.AddrSpace
   387  	// (optional) Function attributes.
   388  	FuncAttrs []FuncAttribute
   389  	// (optional) Operand bundles.
   390  	OperandBundles []*OperandBundle
   391  	// (optional) Metadata.
   392  	Metadata
   393  }
   394  
   395  // NewInvoke returns a new invoke terminator based on the given invokee,
   396  // function arguments and control flow return points for normal and exceptional
   397  // execution.
   398  //
   399  // TODO: specify the set of underlying types of invokee.
   400  func NewInvoke(invokee value.Value, args []value.Value, normalRetTarget, exceptionRetTarget *Block) *TermInvoke {
   401  	term := &TermInvoke{Invokee: invokee, Args: args, NormalRetTarget: normalRetTarget, ExceptionRetTarget: exceptionRetTarget}
   402  	// Compute type.
   403  	term.Type()
   404  	return term
   405  }
   406  
   407  // String returns the LLVM syntax representation of the terminator as a type-
   408  // value pair.
   409  func (term *TermInvoke) String() string {
   410  	return fmt.Sprintf("%s %s", term.Type(), term.Ident())
   411  }
   412  
   413  // Type returns the type of the terminator.
   414  func (term *TermInvoke) Type() types.Type {
   415  	// Cache type if not present.
   416  	if term.Typ == nil {
   417  		sig := term.Sig()
   418  		term.Typ = sig.RetType
   419  	}
   420  	return term.Typ
   421  }
   422  
   423  // Succs returns the successor basic blocks of the terminator.
   424  func (term *TermInvoke) Succs() []*Block {
   425  	// Cache successors if not present.
   426  	if term.Successors == nil {
   427  		term.Successors = []*Block{term.NormalRetTarget.(*Block), term.ExceptionRetTarget.(*Block)}
   428  	}
   429  	return term.Successors
   430  }
   431  
   432  // Operands returns a mutable list of operands of the given terminator.
   433  func (term *TermInvoke) Operands() []*value.Value {
   434  	ops := make([]*value.Value, 0, 1+len(term.Args)+1+1)
   435  	ops = append(ops, &term.Invokee)
   436  	for i := range term.Args {
   437  		ops = append(ops, &term.Args[i])
   438  	}
   439  	ops = append(ops, &term.NormalRetTarget)
   440  	ops = append(ops, &term.ExceptionRetTarget)
   441  	return ops
   442  }
   443  
   444  // LLString returns the LLVM syntax representation of the terminator.
   445  //
   446  // 'invoke' CallingConvopt ReturnAttrs=ReturnAttribute* AddrSpaceopt Typ=Type Invokee=Value '(' Args ')' FuncAttrs=FuncAttribute* OperandBundles=('[' (OperandBundle separator ',')+ ']')? 'to' NormalRetTarget=Label 'unwind' ExceptionRetTarget=Label Metadata=(',' MetadataAttachment)+?
   447  func (term *TermInvoke) LLString() string {
   448  	buf := &strings.Builder{}
   449  	if !term.Type().Equal(types.Void) {
   450  		fmt.Fprintf(buf, "%s = ", term.Ident())
   451  	}
   452  	buf.WriteString("invoke")
   453  	if term.CallingConv != enum.CallingConvNone {
   454  		fmt.Fprintf(buf, " %s", callingConvString(term.CallingConv))
   455  	}
   456  	for _, attr := range term.ReturnAttrs {
   457  		fmt.Fprintf(buf, " %s", attr)
   458  	}
   459  	// (optional) Address space.
   460  	if term.AddrSpace != 0 {
   461  		fmt.Fprintf(buf, " %s", term.AddrSpace)
   462  	}
   463  	// Use function signature instead of return type for variadic functions.
   464  	invokeeType := term.Type()
   465  	if sig := term.Sig(); sig.Variadic {
   466  		invokeeType = sig
   467  	}
   468  	fmt.Fprintf(buf, " %s %s(", invokeeType, term.Invokee.Ident())
   469  	for i, arg := range term.Args {
   470  		if i != 0 {
   471  			buf.WriteString(", ")
   472  		}
   473  		buf.WriteString(arg.String())
   474  	}
   475  	buf.WriteString(")")
   476  	for _, attr := range term.FuncAttrs {
   477  		fmt.Fprintf(buf, " %s", attr)
   478  	}
   479  	if len(term.OperandBundles) > 0 {
   480  		buf.WriteString(" [ ")
   481  		for i, operandBundle := range term.OperandBundles {
   482  			if i != 0 {
   483  				buf.WriteString(", ")
   484  			}
   485  			buf.WriteString(operandBundle.String())
   486  		}
   487  		buf.WriteString(" ]")
   488  	}
   489  	fmt.Fprintf(buf, "\n\t\tto %s unwind %s", term.NormalRetTarget, term.ExceptionRetTarget)
   490  	for _, md := range term.Metadata {
   491  		fmt.Fprintf(buf, ", %s", md)
   492  	}
   493  	return buf.String()
   494  }
   495  
   496  // Sig returns the function signature of the invokee.
   497  func (term *TermInvoke) Sig() *types.FuncType {
   498  	t, ok := term.Invokee.Type().(*types.PointerType)
   499  	if !ok {
   500  		panic(fmt.Errorf("invalid invokee type; expected *types.PointerType, got %T", term.Invokee.Type()))
   501  	}
   502  	sig, ok := t.ElemType.(*types.FuncType)
   503  	if !ok {
   504  		panic(fmt.Errorf("invalid invokee type; expected *types.FuncType, got %T", t.ElemType))
   505  	}
   506  	return sig
   507  }
   508  
   509  // --- [ callbr ] --------------------------------------------------------------
   510  
   511  // TermCallBr is an LLVM IR callbr terminator.
   512  type TermCallBr struct {
   513  	// Name of local variable associated with the result.
   514  	LocalIdent
   515  	// Callee function.
   516  	// TODO: specify the set of underlying types of Callee.
   517  	Callee value.Value
   518  	// Function arguments.
   519  	//
   520  	// Arg has one of the following underlying types:
   521  	//    value.Value
   522  	//    TODO: add metadata value?
   523  	Args []value.Value
   524  	// Normal control flow return point.
   525  	NormalRetTarget value.Value // *ir.Block
   526  	// Other control flow return points.
   527  	OtherRetTargets []value.Value // slice of *ir.Block
   528  
   529  	// extra.
   530  
   531  	// Type of result produced by the terminator.
   532  	Typ types.Type
   533  	// Successor basic blocks of the terminator.
   534  	Successors []*Block
   535  	// (optional) Calling convention; zero if not present.
   536  	CallingConv enum.CallingConv
   537  	// (optional) Return attributes.
   538  	ReturnAttrs []ReturnAttribute
   539  	// (optional) Address space; zero if not present.
   540  	AddrSpace types.AddrSpace
   541  	// (optional) Function attributes.
   542  	FuncAttrs []FuncAttribute
   543  	// (optional) Operand bundles.
   544  	OperandBundles []*OperandBundle
   545  	// (optional) Metadata.
   546  	Metadata
   547  }
   548  
   549  // NewCallBr returns a new callbr terminator based on the given callee, function
   550  // arguments and control flow return points for normal and exceptional
   551  // execution.
   552  //
   553  // TODO: specify the set of underlying types of callee.
   554  func NewCallBr(callee value.Value, args []value.Value, normalRetTarget *Block, otherRetTargets ...*Block) *TermCallBr {
   555  	// Convert otherRetTargets slice to []value.Value.
   556  	var otherRets []value.Value
   557  	for _, otherRetTarget := range otherRetTargets {
   558  		otherRets = append(otherRets, otherRetTarget)
   559  	}
   560  	term := &TermCallBr{Callee: callee, Args: args, NormalRetTarget: normalRetTarget, OtherRetTargets: otherRets}
   561  	// Compute type.
   562  	term.Type()
   563  	return term
   564  }
   565  
   566  // String returns the LLVM syntax representation of the terminator as a type-
   567  // value pair.
   568  func (term *TermCallBr) String() string {
   569  	return fmt.Sprintf("%s %s", term.Type(), term.Ident())
   570  }
   571  
   572  // Type returns the type of the terminator.
   573  func (term *TermCallBr) Type() types.Type {
   574  	// Cache type if not present.
   575  	if term.Typ == nil {
   576  		sig := term.Sig()
   577  		term.Typ = sig.RetType
   578  	}
   579  	return term.Typ
   580  }
   581  
   582  // Succs returns the successor basic blocks of the terminator.
   583  func (term *TermCallBr) Succs() []*Block {
   584  	// Cache successors if not present.
   585  	if term.Successors == nil {
   586  		term.Successors = []*Block{term.NormalRetTarget.(*Block)}
   587  		// Convert OtherRetTargets slice to []*ir.Block.
   588  		for _, otherRetTarget := range term.OtherRetTargets {
   589  			term.Successors = append(term.Successors, otherRetTarget.(*Block))
   590  		}
   591  	}
   592  	return term.Successors
   593  }
   594  
   595  // Operands returns a mutable list of operands of the given terminator.
   596  func (term *TermCallBr) Operands() []*value.Value {
   597  	ops := make([]*value.Value, 0, 1+len(term.Args)+1+len(term.OtherRetTargets))
   598  	ops = append(ops, &term.Callee)
   599  	for i := range term.Args {
   600  		ops = append(ops, &term.Args[i])
   601  	}
   602  	ops = append(ops, &term.NormalRetTarget)
   603  	for i := range term.OtherRetTargets {
   604  		ops = append(ops, &term.OtherRetTargets[i])
   605  	}
   606  	return ops
   607  }
   608  
   609  // LLString returns the LLVM syntax representation of the terminator.
   610  //
   611  // 'callbr' CallingConvopt ReturnAttrs=ReturnAttribute* AddrSpaceopt Typ=Type Callee=Value '(' Args ')' FuncAttrs=FuncAttribute* OperandBundles=('[' (OperandBundle separator ',')+ ']')? 'to' NormalRetTarget=Label '[' OtherRetTargets=(Label separator ',')* ']' Metadata=(',' MetadataAttachment)+?
   612  func (term *TermCallBr) LLString() string {
   613  	buf := &strings.Builder{}
   614  	if !term.Type().Equal(types.Void) {
   615  		fmt.Fprintf(buf, "%s = ", term.Ident())
   616  	}
   617  	buf.WriteString("callbr")
   618  	if term.CallingConv != enum.CallingConvNone {
   619  		fmt.Fprintf(buf, " %s", callingConvString(term.CallingConv))
   620  	}
   621  	for _, attr := range term.ReturnAttrs {
   622  		fmt.Fprintf(buf, " %s", attr)
   623  	}
   624  	// (optional) Address space.
   625  	if term.AddrSpace != 0 {
   626  		fmt.Fprintf(buf, " %s", term.AddrSpace)
   627  	}
   628  	// Use function signature instead of return type for variadic functions.
   629  	calleeType := term.Type()
   630  	if sig := term.Sig(); sig.Variadic {
   631  		calleeType = sig
   632  	}
   633  	fmt.Fprintf(buf, " %s %s(", calleeType, term.Callee.Ident())
   634  	for i, arg := range term.Args {
   635  		if i != 0 {
   636  			buf.WriteString(", ")
   637  		}
   638  		buf.WriteString(arg.String())
   639  	}
   640  	buf.WriteString(")")
   641  	for _, attr := range term.FuncAttrs {
   642  		fmt.Fprintf(buf, " %s", attr)
   643  	}
   644  	if len(term.OperandBundles) > 0 {
   645  		buf.WriteString(" [ ")
   646  		for i, operandBundle := range term.OperandBundles {
   647  			if i != 0 {
   648  				buf.WriteString(", ")
   649  			}
   650  			buf.WriteString(operandBundle.String())
   651  		}
   652  		buf.WriteString(" ]")
   653  	}
   654  	fmt.Fprintf(buf, "\n\t\tto %s [", term.NormalRetTarget)
   655  	for i, otherRetTarget := range term.OtherRetTargets {
   656  		if i != 0 {
   657  			buf.WriteString(", ")
   658  		}
   659  		buf.WriteString(otherRetTarget.String())
   660  	}
   661  	buf.WriteString("]")
   662  	for _, md := range term.Metadata {
   663  		fmt.Fprintf(buf, ", %s", md)
   664  	}
   665  	return buf.String()
   666  }
   667  
   668  // Sig returns the function signature of the callee.
   669  func (term *TermCallBr) Sig() *types.FuncType {
   670  	t, ok := term.Callee.Type().(*types.PointerType)
   671  	if !ok {
   672  		panic(fmt.Errorf("invalid callee type; expected *types.PointerType, got %T", term.Callee.Type()))
   673  	}
   674  	sig, ok := t.ElemType.(*types.FuncType)
   675  	if !ok {
   676  		panic(fmt.Errorf("invalid callee type; expected *types.FuncType, got %T", t.ElemType))
   677  	}
   678  	return sig
   679  }
   680  
   681  // --- [ resume ] --------------------------------------------------------------
   682  
   683  // TermResume is an LLVM IR resume terminator.
   684  type TermResume struct {
   685  	// Exception argument to propagate.
   686  	X value.Value
   687  
   688  	// extra.
   689  
   690  	// (optional) Metadata.
   691  	Metadata
   692  }
   693  
   694  // NewResume returns a new resume terminator based on the given exception
   695  // argument to propagate.
   696  func NewResume(x value.Value) *TermResume {
   697  	return &TermResume{X: x}
   698  }
   699  
   700  // Succs returns the successor basic blocks of the terminator.
   701  func (term *TermResume) Succs() []*Block {
   702  	// no successors.
   703  	return nil
   704  }
   705  
   706  // Operands returns a mutable list of operands of the given terminator.
   707  func (term *TermResume) Operands() []*value.Value {
   708  	return []*value.Value{&term.X}
   709  }
   710  
   711  // LLString returns the LLVM syntax representation of the terminator.
   712  //
   713  // 'resume' X=TypeValue Metadata=(',' MetadataAttachment)+?
   714  func (term *TermResume) LLString() string {
   715  	buf := &strings.Builder{}
   716  	fmt.Fprintf(buf, "resume %s", term.X)
   717  	for _, md := range term.Metadata {
   718  		fmt.Fprintf(buf, ", %s", md)
   719  	}
   720  	return buf.String()
   721  }
   722  
   723  // --- [ catchswitch ] ---------------------------------------------------------
   724  
   725  // TermCatchSwitch is an LLVM IR catchswitch terminator.
   726  type TermCatchSwitch struct {
   727  	// Name of local variable associated with the result.
   728  	LocalIdent
   729  	// Parent exception pad.
   730  	ParentPad value.Value // ir.ExceptionPad
   731  	// Exception handlers.
   732  	Handlers []value.Value // []*ir.Block
   733  	// Optional default target basic block to transfer control flow to; or nil to
   734  	// unwind to caller function.
   735  	DefaultUnwindTarget value.Value // *ir.Block or nil
   736  
   737  	// extra.
   738  
   739  	// Successor basic blocks of the terminator.
   740  	Successors []*Block
   741  	// (optional) Metadata.
   742  	Metadata
   743  }
   744  
   745  // NewCatchSwitch returns a new catchswitch terminator based on the given parent
   746  // exception pad, exception handlers and optional default unwind target. If
   747  // defaultUnwindTarget is nil, catchswitch unwinds to caller function.
   748  func NewCatchSwitch(parentPad ExceptionPad, handlers []*Block, defaultUnwindTarget *Block) *TermCatchSwitch {
   749  	// convert handlers slice to []value.Value.
   750  	var hs []value.Value
   751  	for _, handler := range handlers {
   752  		hs = append(hs, handler)
   753  	}
   754  	term := &TermCatchSwitch{ParentPad: parentPad, Handlers: hs}
   755  	if defaultUnwindTarget != nil {
   756  		// Note: since DefaultUnwindTarget is an interface we have to be careful
   757  		// with typed nil values (e.g. `(*ir.Block)(nil)`). This is to ensure that
   758  		// DefaultUnwindTarget is nil and not `{Type: ir.Block, Value: nil}`.
   759  		//
   760  		// ref: https://golang.org/doc/faq#nil_error
   761  		term.DefaultUnwindTarget = defaultUnwindTarget
   762  	}
   763  	return term
   764  }
   765  
   766  // String returns the LLVM syntax representation of the terminator as a type-
   767  // value pair.
   768  func (term *TermCatchSwitch) String() string {
   769  	return fmt.Sprintf("%s %s", term.Type(), term.Ident())
   770  }
   771  
   772  // Type returns the type of the terminator.
   773  func (term *TermCatchSwitch) Type() types.Type {
   774  	return types.Token
   775  }
   776  
   777  // Succs returns the successor basic blocks of the terminator.
   778  func (term *TermCatchSwitch) Succs() []*Block {
   779  	// Cache successors if not present.
   780  	if term.Successors == nil {
   781  		// convert Handlers slice to []*ir.Block.
   782  		for _, handler := range term.Handlers {
   783  			term.Successors = append(term.Successors, handler.(*Block))
   784  		}
   785  		if defaultUnwindTarget, ok := term.DefaultUnwindTarget.(*Block); ok {
   786  			term.Successors = append(term.Successors, defaultUnwindTarget)
   787  		}
   788  	}
   789  	return term.Successors
   790  }
   791  
   792  // Operands returns a mutable list of operands of the given terminator.
   793  func (term *TermCatchSwitch) Operands() []*value.Value {
   794  	ops := make([]*value.Value, 0, 1+len(term.Handlers)+1)
   795  	ops = append(ops, &term.ParentPad)
   796  	for i := range term.Handlers {
   797  		ops = append(ops, &term.Handlers[i])
   798  	}
   799  	if term.DefaultUnwindTarget != nil {
   800  		ops = append(ops, &term.DefaultUnwindTarget)
   801  	}
   802  	return ops
   803  }
   804  
   805  // LLString returns the LLVM syntax representation of the terminator.
   806  //
   807  // 'catchswitch' 'within' ParentPad=ExceptionPad '[' Handlers=Handlers ']' 'unwind' DefaultUnwindTarget=UnwindTarget Metadata=(',' MetadataAttachment)+?
   808  func (term *TermCatchSwitch) LLString() string {
   809  	buf := &strings.Builder{}
   810  	fmt.Fprintf(buf, "%s = ", term.Ident())
   811  	fmt.Fprintf(buf, "catchswitch within %s [", term.ParentPad.Ident())
   812  	for i, handler := range term.Handlers {
   813  		if i != 0 {
   814  			buf.WriteString(", ")
   815  		}
   816  		buf.WriteString(handler.String())
   817  	}
   818  	buf.WriteString("] unwind ")
   819  	if term.DefaultUnwindTarget != nil {
   820  		buf.WriteString(term.DefaultUnwindTarget.String())
   821  	} else {
   822  		buf.WriteString("to caller")
   823  	}
   824  	for _, md := range term.Metadata {
   825  		fmt.Fprintf(buf, ", %s", md)
   826  	}
   827  	return buf.String()
   828  }
   829  
   830  // --- [ catchret ] ------------------------------------------------------------
   831  
   832  // TermCatchRet is an LLVM IR catchret terminator, which catches an in-flight
   833  // exception from CatchPad and returns control flow to normal at Target.
   834  type TermCatchRet struct {
   835  	// Exit catchpad.
   836  	CatchPad value.Value // *ir.InstCatchPad
   837  	// Target basic block to transfer control flow to.
   838  	Target value.Value // *ir.Block
   839  
   840  	// extra.
   841  
   842  	// Successor basic blocks of the terminator.
   843  	Successors []*Block
   844  	// (optional) Metadata.
   845  	Metadata
   846  }
   847  
   848  // NewCatchRet returns a new catchret terminator based on the given exit
   849  // catchpad and target basic block.
   850  func NewCatchRet(catchPad *InstCatchPad, target *Block) *TermCatchRet {
   851  	return &TermCatchRet{CatchPad: catchPad, Target: target}
   852  }
   853  
   854  // Succs returns the successor basic blocks of the terminator.
   855  func (term *TermCatchRet) Succs() []*Block {
   856  	// Cache successors if not present.
   857  	if term.Successors == nil {
   858  		term.Successors = []*Block{term.Target.(*Block)}
   859  	}
   860  	return term.Successors
   861  }
   862  
   863  // Operands returns a mutable list of operands of the given terminator.
   864  func (term *TermCatchRet) Operands() []*value.Value {
   865  	return []*value.Value{&term.CatchPad, &term.Target}
   866  }
   867  
   868  // LLString returns the LLVM syntax representation of the terminator.
   869  //
   870  // 'catchret' 'from' CatchPad=Value 'to' Target=Label Metadata=(',' MetadataAttachment)+?
   871  func (term *TermCatchRet) LLString() string {
   872  	buf := &strings.Builder{}
   873  	fmt.Fprintf(buf, "catchret from %s to %s", term.CatchPad.Ident(), term.Target)
   874  	for _, md := range term.Metadata {
   875  		fmt.Fprintf(buf, ", %s", md)
   876  	}
   877  	return buf.String()
   878  }
   879  
   880  // --- [ cleanupret ] ----------------------------------------------------------
   881  
   882  // TermCleanupRet is an LLVM IR cleanupret terminator, which indicates that the
   883  // personality function of a cleanuppad has finished and transfers control flow
   884  // to an optional target basic block or unwinds to the caller function.
   885  type TermCleanupRet struct {
   886  	// Exit cleanuppad.
   887  	CleanupPad value.Value // *ir.InstCleanupPad
   888  	// Optional target basic block to transfer control flow to; or nil to unwind
   889  	// to caller function.
   890  	UnwindTarget value.Value // *ir.Block or nil
   891  
   892  	// extra.
   893  
   894  	// Successor basic blocks of the terminator.
   895  	Successors []*Block
   896  	// (optional) Metadata.
   897  	Metadata
   898  }
   899  
   900  // NewCleanupRet returns a new cleanupret terminator based on the given exit
   901  // cleanuppad and optional unwind target. If unwindTarget is nil, cleanupret
   902  // unwinds to caller function.
   903  func NewCleanupRet(cleanupPad *InstCleanupPad, unwindTarget *Block) *TermCleanupRet {
   904  	term := &TermCleanupRet{CleanupPad: cleanupPad}
   905  	if unwindTarget != nil {
   906  		// Note: since UnwindTarget is an interface we have to be careful
   907  		// with typed nil values (e.g. `(*ir.Block)(nil)`). This is to ensure that
   908  		// UnwindTarget is nil and not `{Type: ir.Block, Value: nil}`.
   909  		//
   910  		// ref: https://golang.org/doc/faq#nil_error
   911  		term.UnwindTarget = unwindTarget
   912  	}
   913  	return term
   914  }
   915  
   916  // Succs returns the successor basic blocks of the terminator.
   917  func (term *TermCleanupRet) Succs() []*Block {
   918  	// Cache successors if not present.
   919  	if term.Successors == nil {
   920  		if unwindTarget, ok := term.UnwindTarget.(*Block); ok {
   921  			term.Successors = []*Block{unwindTarget}
   922  		} else {
   923  			term.Successors = []*Block{}
   924  		}
   925  	}
   926  	return term.Successors
   927  }
   928  
   929  // Operands returns a mutable list of operands of the given terminator.
   930  func (term *TermCleanupRet) Operands() []*value.Value {
   931  	ops := []*value.Value{&term.CleanupPad}
   932  	if term.UnwindTarget != nil {
   933  		ops = append(ops, &term.UnwindTarget)
   934  	}
   935  	return ops
   936  }
   937  
   938  // LLString returns the LLVM syntax representation of the terminator.
   939  //
   940  // 'cleanupret' 'from' CleanupPad=Value 'unwind' UnwindTarget Metadata=(',' MetadataAttachment)+?
   941  func (term *TermCleanupRet) LLString() string {
   942  	buf := &strings.Builder{}
   943  	fmt.Fprintf(buf, "cleanupret from %s unwind ", term.CleanupPad.Ident())
   944  	if term.UnwindTarget != nil {
   945  		buf.WriteString(term.UnwindTarget.String())
   946  	} else {
   947  		buf.WriteString("to caller")
   948  	}
   949  	for _, md := range term.Metadata {
   950  		fmt.Fprintf(buf, ", %s", md)
   951  	}
   952  	return buf.String()
   953  }
   954  
   955  // --- [ unreachable ] ---------------------------------------------------------
   956  
   957  // TermUnreachable is an LLVM IR unreachable terminator.
   958  type TermUnreachable struct {
   959  	// extra.
   960  
   961  	// (optional) Metadata.
   962  	Metadata
   963  }
   964  
   965  // NewUnreachable returns a new unreachable terminator.
   966  func NewUnreachable() *TermUnreachable {
   967  	return &TermUnreachable{}
   968  }
   969  
   970  // Succs returns the successor basic blocks of the terminator.
   971  func (term *TermUnreachable) Succs() []*Block {
   972  	// no successors.
   973  	return nil
   974  }
   975  
   976  // Operands returns a mutable list of operands of the given terminator.
   977  func (term *TermUnreachable) Operands() []*value.Value {
   978  	return nil
   979  }
   980  
   981  // LLString returns the LLVM syntax representation of the terminator.
   982  //
   983  // 'unreachable' Metadata=(',' MetadataAttachment)+?
   984  func (term *TermUnreachable) LLString() string {
   985  	buf := &strings.Builder{}
   986  	buf.WriteString("unreachable")
   987  	for _, md := range term.Metadata {
   988  		fmt.Fprintf(buf, ", %s", md)
   989  	}
   990  	return buf.String()
   991  }