github.com/llir/llvm@v0.3.6/asm/term.go (about)

     1  package asm
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/llir/ll/ast"
     7  	"github.com/llir/llvm/ir"
     8  	"github.com/llir/llvm/ir/types"
     9  	"github.com/llir/llvm/ir/value"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // === [ Create IR ] ===========================================================
    14  
    15  // newTerm returns a new IR terminator (without body but with type) based on the
    16  // given AST terminator.
    17  func (fgen *funcGen) newTerm(old ast.Terminator) (ir.Terminator, error) {
    18  	switch old := old.(type) {
    19  	// Value terminators.
    20  	case *ast.LocalDefTerm:
    21  		ident := localIdent(old.Name())
    22  		return fgen.newValueTerm(ident, old.Term())
    23  	case ast.ValueTerminator:
    24  		unnamed := ir.LocalIdent{}
    25  		return fgen.newValueTerm(unnamed, old)
    26  	// Non-value terminators.
    27  	case *ast.RetTerm:
    28  		return &ir.TermRet{}, nil
    29  	case *ast.BrTerm:
    30  		return &ir.TermBr{}, nil
    31  	case *ast.CondBrTerm:
    32  		return &ir.TermCondBr{}, nil
    33  	case *ast.SwitchTerm:
    34  		return &ir.TermSwitch{}, nil
    35  	case *ast.IndirectBrTerm:
    36  		return &ir.TermIndirectBr{}, nil
    37  	case *ast.ResumeTerm:
    38  		return &ir.TermResume{}, nil
    39  	case *ast.CatchRetTerm:
    40  		return &ir.TermCatchRet{}, nil
    41  	case *ast.CleanupRetTerm:
    42  		return &ir.TermCleanupRet{}, nil
    43  	case *ast.UnreachableTerm:
    44  		return &ir.TermUnreachable{}, nil
    45  	default:
    46  		panic(fmt.Errorf("support for terminator %T not yet implemented", old))
    47  	}
    48  }
    49  
    50  // newValueTerm returns a new IR value terminator (without body but with type)
    51  // based on the given AST value terminator.
    52  func (fgen *funcGen) newValueTerm(ident ir.LocalIdent, old ast.ValueTerminator) (ir.Terminator, error) {
    53  	switch old := old.(type) {
    54  	case *ast.InvokeTerm:
    55  		return fgen.newInvokeTerm(ident, old)
    56  	case *ast.CallBrTerm:
    57  		return fgen.newCallBrTerm(ident, old)
    58  	case *ast.CatchSwitchTerm:
    59  		// Result type is always token.
    60  		return &ir.TermCatchSwitch{LocalIdent: ident}, nil
    61  	default:
    62  		panic(fmt.Errorf("support for value terminator %T not yet implemented", old))
    63  	}
    64  }
    65  
    66  // newInvokeTerm returns a new IR invoke terminator (without body but with type)
    67  // based on the given AST invoke terminator.
    68  func (fgen *funcGen) newInvokeTerm(ident ir.LocalIdent, old *ast.InvokeTerm) (*ir.TermInvoke, error) {
    69  	typ, err := fgen.gen.irType(old.Typ())
    70  	if err != nil {
    71  		return nil, errors.WithStack(err)
    72  	}
    73  	// Resolve return type of variadic functions.
    74  	if funcType, ok := typ.(*types.FuncType); ok {
    75  		typ = funcType.RetType
    76  	}
    77  	return &ir.TermInvoke{LocalIdent: ident, Typ: typ}, nil
    78  }
    79  
    80  // newCallBrTerm returns a new IR callbr terminator (without body but with type)
    81  // based on the given AST callbr terminator.
    82  func (fgen *funcGen) newCallBrTerm(ident ir.LocalIdent, old *ast.CallBrTerm) (*ir.TermCallBr, error) {
    83  	typ, err := fgen.gen.irType(old.Typ())
    84  	if err != nil {
    85  		return nil, errors.WithStack(err)
    86  	}
    87  	// Resolve return type of variadic functions.
    88  	if funcType, ok := typ.(*types.FuncType); ok {
    89  		typ = funcType.RetType
    90  	}
    91  	return &ir.TermCallBr{LocalIdent: ident, Typ: typ}, nil
    92  }
    93  
    94  // === [ Translate AST to IR ] =================================================
    95  
    96  // translateTerms translates the AST terminators of the given function to IR.
    97  func (fgen *funcGen) translateTerms(oldBlocks []ast.BasicBlock) error {
    98  	for i, oldBlock := range oldBlocks {
    99  		block := fgen.f.Blocks[i]
   100  		old := oldBlock.Term()
   101  		if err := fgen.irTerm(block.Term, old); err != nil {
   102  			return errors.WithStack(err)
   103  		}
   104  	}
   105  	return nil
   106  }
   107  
   108  // irTerm translates the AST terminator into an equivalent IR terminator.
   109  func (fgen *funcGen) irTerm(new ir.Terminator, old ast.Terminator) error {
   110  	switch old := old.(type) {
   111  	// Value terminators.
   112  	case *ast.LocalDefTerm:
   113  		return fgen.irValueTerm(new, old.Term())
   114  	case ast.ValueTerminator:
   115  		return fgen.irValueTerm(new, old)
   116  	// Non-value terminators.
   117  	case *ast.RetTerm:
   118  		return fgen.irRetTerm(new, old)
   119  	case *ast.BrTerm:
   120  		return fgen.irBrTerm(new, old)
   121  	case *ast.CondBrTerm:
   122  		return fgen.irCondBrTerm(new, old)
   123  	case *ast.SwitchTerm:
   124  		return fgen.irSwitchTerm(new, old)
   125  	case *ast.IndirectBrTerm:
   126  		return fgen.irIndirectBrTerm(new, old)
   127  	case *ast.ResumeTerm:
   128  		return fgen.irResumeTerm(new, old)
   129  	case *ast.CatchRetTerm:
   130  		return fgen.irCatchRetTerm(new, old)
   131  	case *ast.CleanupRetTerm:
   132  		return fgen.irCleanupRetTerm(new, old)
   133  	case *ast.UnreachableTerm:
   134  		return fgen.irUnreachableTerm(new, old)
   135  	default:
   136  		panic(fmt.Errorf("support for terminator %T not yet implemented", old))
   137  	}
   138  }
   139  
   140  // irValueTerm translates the AST value terminator into an equivalent IR value
   141  // terminator.
   142  func (fgen *funcGen) irValueTerm(new ir.Terminator, old ast.ValueTerminator) error {
   143  	switch old := old.(type) {
   144  	case *ast.InvokeTerm:
   145  		return fgen.irInvokeTerm(new, old)
   146  	case *ast.CallBrTerm:
   147  		return fgen.irCallBrTerm(new, old)
   148  	case *ast.CatchSwitchTerm:
   149  		return fgen.irCatchSwitchTerm(new, old)
   150  	default:
   151  		panic(fmt.Errorf("support for value terminator %T not yet implemented", old))
   152  	}
   153  }
   154  
   155  // --- [ ret ] -----------------------------------------------------------------
   156  
   157  // irRetTerm translates the AST ret terminator into an equivalent IR terminator.
   158  func (fgen *funcGen) irRetTerm(new ir.Terminator, old *ast.RetTerm) error {
   159  	term, ok := new.(*ir.TermRet)
   160  	if !ok {
   161  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermRet, got %T", new))
   162  	}
   163  	// Return type.
   164  	typ, err := fgen.gen.irType(old.XTyp())
   165  	if err != nil {
   166  		return errors.WithStack(err)
   167  	}
   168  	// Check if non-void return.
   169  	if n, ok := old.X(); ok {
   170  		// Return value.
   171  		x, err := fgen.irValue(typ, n)
   172  		if err != nil {
   173  			return errors.WithStack(err)
   174  		}
   175  		term.X = x
   176  	}
   177  	// (optional) Metadata.
   178  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   179  	if err != nil {
   180  		return errors.WithStack(err)
   181  	}
   182  	term.Metadata = md
   183  	return nil
   184  }
   185  
   186  // --- [ br ] ------------------------------------------------------------------
   187  
   188  // irBrTerm translates the AST br terminator into an equivalent IR terminator.
   189  func (fgen *funcGen) irBrTerm(new ir.Terminator, old *ast.BrTerm) error {
   190  	term, ok := new.(*ir.TermBr)
   191  	if !ok {
   192  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermBr, got %T", new))
   193  	}
   194  	// Target.
   195  	target, err := fgen.irBlock(old.Target())
   196  	if err != nil {
   197  		return errors.WithStack(err)
   198  	}
   199  	term.Target = target
   200  	// (optional) Metadata.
   201  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   202  	if err != nil {
   203  		return errors.WithStack(err)
   204  	}
   205  	term.Metadata = md
   206  	return nil
   207  }
   208  
   209  // --- [ condbr ] --------------------------------------------------------------
   210  
   211  // irCondBrTerm translates the AST condbr terminator into an equivalent IR
   212  // terminator.
   213  func (fgen *funcGen) irCondBrTerm(new ir.Terminator, old *ast.CondBrTerm) error {
   214  	term, ok := new.(*ir.TermCondBr)
   215  	if !ok {
   216  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermCondBr, got %T", new))
   217  	}
   218  	// Branching condition.
   219  	ct := old.CondTyp()
   220  	condType, err := fgen.gen.irType(&ct)
   221  	if err != nil {
   222  		return errors.WithStack(err)
   223  	}
   224  	cond, err := fgen.irValue(condType, old.Cond())
   225  	if err != nil {
   226  		return errors.WithStack(err)
   227  	}
   228  	term.Cond = cond
   229  	// Target true.
   230  	targetTrue, err := fgen.irBlock(old.TargetTrue())
   231  	if err != nil {
   232  		return errors.WithStack(err)
   233  	}
   234  	term.TargetTrue = targetTrue
   235  	// Target false.
   236  	targetFalse, err := fgen.irBlock(old.TargetFalse())
   237  	if err != nil {
   238  		return errors.WithStack(err)
   239  	}
   240  	term.TargetFalse = targetFalse
   241  	// (optional) Metadata.
   242  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   243  	if err != nil {
   244  		return errors.WithStack(err)
   245  	}
   246  	term.Metadata = md
   247  	return nil
   248  }
   249  
   250  // --- [ switch ] --------------------------------------------------------------
   251  
   252  // irSwitchTerm translates the AST switch terminator into an equivalent IR
   253  // terminator.
   254  func (fgen *funcGen) irSwitchTerm(new ir.Terminator, old *ast.SwitchTerm) error {
   255  	term, ok := new.(*ir.TermSwitch)
   256  	if !ok {
   257  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermSwitch, got %T", new))
   258  	}
   259  	// Control variable.
   260  	x, err := fgen.irTypeValue(old.X())
   261  	if err != nil {
   262  		return errors.WithStack(err)
   263  	}
   264  	term.X = x
   265  	// Default target.
   266  	targetDefault, err := fgen.irBlock(old.Default())
   267  	if err != nil {
   268  		return errors.WithStack(err)
   269  	}
   270  	term.TargetDefault = targetDefault
   271  	// Switch cases.
   272  	if oldCases := old.Cases(); len(oldCases) > 0 {
   273  		term.Cases = make([]*ir.Case, len(oldCases))
   274  		for i, oldCase := range oldCases {
   275  			c, err := fgen.irCase(oldCase)
   276  			if err != nil {
   277  				return errors.WithStack(err)
   278  			}
   279  			term.Cases[i] = c
   280  		}
   281  	}
   282  	// (optional) Metadata.
   283  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   284  	if err != nil {
   285  		return errors.WithStack(err)
   286  	}
   287  	term.Metadata = md
   288  	return nil
   289  }
   290  
   291  // --- [ indirectbr ] ----------------------------------------------------------
   292  
   293  // irIndirectBrTerm translates the AST indirectbr terminator into an equivalent
   294  // IR terminator.
   295  func (fgen *funcGen) irIndirectBrTerm(new ir.Terminator, old *ast.IndirectBrTerm) error {
   296  	term, ok := new.(*ir.TermIndirectBr)
   297  	if !ok {
   298  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermIndirectBr, got %T", new))
   299  	}
   300  	// Target address.
   301  	addr, err := fgen.irTypeValue(old.Addr())
   302  	if err != nil {
   303  		return errors.WithStack(err)
   304  	}
   305  	term.Addr = addr
   306  	// Valid targets.
   307  	if oldValidTargets := old.ValidTargets(); len(oldValidTargets) > 0 {
   308  		term.ValidTargets = make([]value.Value, len(oldValidTargets))
   309  		for i, oldValidTarget := range oldValidTargets {
   310  			validTarget, err := fgen.irBlock(oldValidTarget)
   311  			if err != nil {
   312  				return errors.WithStack(err)
   313  			}
   314  			term.ValidTargets[i] = validTarget
   315  		}
   316  	}
   317  	// (optional) Metadata.
   318  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   319  	if err != nil {
   320  		return errors.WithStack(err)
   321  	}
   322  	term.Metadata = md
   323  	return nil
   324  }
   325  
   326  // --- [ invoke ] --------------------------------------------------------------
   327  
   328  // irInvokeTerm translates the AST invoke terminator into an equivalent IR
   329  // terminator.
   330  func (fgen *funcGen) irInvokeTerm(new ir.Terminator, old *ast.InvokeTerm) error {
   331  	term, ok := new.(*ir.TermInvoke)
   332  	if !ok {
   333  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermInvoke, got %T", new))
   334  	}
   335  	// Function arguments.
   336  	if oldArgs := old.Args().Args(); len(oldArgs) > 0 {
   337  		term.Args = make([]value.Value, len(oldArgs))
   338  		for i, oldArg := range oldArgs {
   339  			arg, err := fgen.irArg(oldArg)
   340  			if err != nil {
   341  				return errors.WithStack(err)
   342  			}
   343  			term.Args[i] = arg
   344  		}
   345  	}
   346  	// Invokee.
   347  	typ, err := fgen.gen.irType(old.Typ())
   348  	if err != nil {
   349  		return errors.WithStack(err)
   350  	}
   351  	sig, ok := typ.(*types.FuncType)
   352  	if !ok {
   353  		// Preliminary function signature. Only used by fgen.irValue for inline
   354  		// assembly invokees and constrant expressions.
   355  		var paramTypes []types.Type
   356  		if len(term.Args) > 0 {
   357  			paramTypes = make([]types.Type, len(term.Args))
   358  			for i, arg := range term.Args {
   359  				paramTypes[i] = arg.Type()
   360  			}
   361  		}
   362  		sig = types.NewFunc(typ, paramTypes...)
   363  	}
   364  	// The invokee type is always pointer to function type.
   365  	ptrToSig := types.NewPointer(sig)
   366  	invokee, err := fgen.irValue(ptrToSig, old.Invokee())
   367  	if err != nil {
   368  		return errors.WithStack(err)
   369  	}
   370  	term.Invokee = invokee
   371  	// Normal control flow return point.
   372  	normalRetTarget, err := fgen.irBlock(old.NormalRetTarget())
   373  	if err != nil {
   374  		return errors.WithStack(err)
   375  	}
   376  	term.NormalRetTarget = normalRetTarget
   377  	// Exception control flow return point.
   378  	exceptionRetTarget, err := fgen.irBlock(old.ExceptionRetTarget())
   379  	if err != nil {
   380  		return errors.WithStack(err)
   381  	}
   382  	term.ExceptionRetTarget = exceptionRetTarget
   383  	// (optional) Calling convention.
   384  	if n, ok := old.CallingConv(); ok {
   385  		term.CallingConv = irCallingConv(n)
   386  	}
   387  	// (optional) Return attributes.
   388  	if oldReturnAttrs := old.ReturnAttrs(); len(oldReturnAttrs) > 0 {
   389  		term.ReturnAttrs = make([]ir.ReturnAttribute, len(oldReturnAttrs))
   390  		for i, oldRetAttr := range oldReturnAttrs {
   391  			retAttr := irReturnAttribute(oldRetAttr)
   392  			term.ReturnAttrs[i] = retAttr
   393  		}
   394  	}
   395  	// (optional) Address space.
   396  	if n, ok := old.AddrSpace(); ok {
   397  		term.AddrSpace = irAddrSpace(n)
   398  	}
   399  	// (optional) Function attributes.
   400  	if oldFuncAttrs := old.FuncAttrs(); len(oldFuncAttrs) > 0 {
   401  		term.FuncAttrs = make([]ir.FuncAttribute, len(oldFuncAttrs))
   402  		for i, oldFuncAttr := range oldFuncAttrs {
   403  			funcAttr := fgen.gen.irFuncAttribute(oldFuncAttr)
   404  			term.FuncAttrs[i] = funcAttr
   405  		}
   406  	}
   407  	// (optional) Operand bundles.
   408  	if oldOperandBundles := old.OperandBundles(); len(oldOperandBundles) > 0 {
   409  		term.OperandBundles = make([]*ir.OperandBundle, len(oldOperandBundles))
   410  		for i, oldOperandBundle := range oldOperandBundles {
   411  			operandBundle, err := fgen.irOperandBundle(oldOperandBundle)
   412  			if err != nil {
   413  				return errors.WithStack(err)
   414  			}
   415  			term.OperandBundles[i] = operandBundle
   416  		}
   417  	}
   418  	// (optional) Metadata.
   419  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   420  	if err != nil {
   421  		return errors.WithStack(err)
   422  	}
   423  	term.Metadata = md
   424  	return nil
   425  }
   426  
   427  // --- [ callbr ] --------------------------------------------------------------
   428  
   429  // irCallBrTerm translates the AST callbr terminator into an equivalent IR
   430  // terminator.
   431  func (fgen *funcGen) irCallBrTerm(new ir.Terminator, old *ast.CallBrTerm) error {
   432  	term, ok := new.(*ir.TermCallBr)
   433  	if !ok {
   434  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermCallBr, got %T", new))
   435  	}
   436  	// Function arguments.
   437  	if oldArgs := old.Args().Args(); len(oldArgs) > 0 {
   438  		term.Args = make([]value.Value, len(oldArgs))
   439  		for i, oldArg := range oldArgs {
   440  			arg, err := fgen.irArg(oldArg)
   441  			if err != nil {
   442  				return errors.WithStack(err)
   443  			}
   444  			term.Args[i] = arg
   445  		}
   446  	}
   447  	// Callee.
   448  	typ, err := fgen.gen.irType(old.Typ())
   449  	if err != nil {
   450  		return errors.WithStack(err)
   451  	}
   452  	sig, ok := typ.(*types.FuncType)
   453  	if !ok {
   454  		// Preliminary function signature. Only used by fgen.irValue for inline
   455  		// assembly callees and constrant expressions.
   456  		var paramTypes []types.Type
   457  		if len(term.Args) > 0 {
   458  			paramTypes = make([]types.Type, len(term.Args))
   459  			for i, arg := range term.Args {
   460  				paramTypes[i] = arg.Type()
   461  			}
   462  		}
   463  		sig = types.NewFunc(typ, paramTypes...)
   464  	}
   465  	// The callee type is always pointer to function type.
   466  	ptrToSig := types.NewPointer(sig)
   467  	callee, err := fgen.irValue(ptrToSig, old.Callee())
   468  	if err != nil {
   469  		return errors.WithStack(err)
   470  	}
   471  	term.Callee = callee
   472  	// Normal control flow return point.
   473  	normalRetTarget, err := fgen.irBlock(old.NormalRetTarget())
   474  	if err != nil {
   475  		return errors.WithStack(err)
   476  	}
   477  	term.NormalRetTarget = normalRetTarget
   478  	// Exception control flow return point.
   479  	for _, oldOtherRetTarget := range old.OtherRetTargets() {
   480  		otherRetTarget, err := fgen.irBlock(oldOtherRetTarget)
   481  		if err != nil {
   482  			return errors.WithStack(err)
   483  		}
   484  		term.OtherRetTargets = append(term.OtherRetTargets, otherRetTarget)
   485  	}
   486  	// (optional) Calling convention.
   487  	if n, ok := old.CallingConv(); ok {
   488  		term.CallingConv = irCallingConv(n)
   489  	}
   490  	// (optional) Return attributes.
   491  	if oldReturnAttrs := old.ReturnAttrs(); len(oldReturnAttrs) > 0 {
   492  		term.ReturnAttrs = make([]ir.ReturnAttribute, len(oldReturnAttrs))
   493  		for i, oldRetAttr := range oldReturnAttrs {
   494  			retAttr := irReturnAttribute(oldRetAttr)
   495  			term.ReturnAttrs[i] = retAttr
   496  		}
   497  	}
   498  	// (optional) Address space.
   499  	if n, ok := old.AddrSpace(); ok {
   500  		term.AddrSpace = irAddrSpace(n)
   501  	}
   502  	// (optional) Function attributes.
   503  	if oldFuncAttrs := old.FuncAttrs(); len(oldFuncAttrs) > 0 {
   504  		term.FuncAttrs = make([]ir.FuncAttribute, len(oldFuncAttrs))
   505  		for i, oldFuncAttr := range oldFuncAttrs {
   506  			funcAttr := fgen.gen.irFuncAttribute(oldFuncAttr)
   507  			term.FuncAttrs[i] = funcAttr
   508  		}
   509  	}
   510  	// (optional) Operand bundles.
   511  	if oldOperandBundles := old.OperandBundles(); len(oldOperandBundles) > 0 {
   512  		term.OperandBundles = make([]*ir.OperandBundle, len(oldOperandBundles))
   513  		for i, oldOperandBundle := range oldOperandBundles {
   514  			operandBundle, err := fgen.irOperandBundle(oldOperandBundle)
   515  			if err != nil {
   516  				return errors.WithStack(err)
   517  			}
   518  			term.OperandBundles[i] = operandBundle
   519  		}
   520  	}
   521  	// (optional) Metadata.
   522  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   523  	if err != nil {
   524  		return errors.WithStack(err)
   525  	}
   526  	term.Metadata = md
   527  	return nil
   528  }
   529  
   530  // --- [ resume ] --------------------------------------------------------------
   531  
   532  // irResumeTerm translates the AST resume terminator into an equivalent IR
   533  // terminator.
   534  func (fgen *funcGen) irResumeTerm(new ir.Terminator, old *ast.ResumeTerm) error {
   535  	term, ok := new.(*ir.TermResume)
   536  	if !ok {
   537  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermResume, got %T", new))
   538  	}
   539  	// Exception argument to propagate.
   540  	x, err := fgen.irTypeValue(old.X())
   541  	if err != nil {
   542  		return errors.WithStack(err)
   543  	}
   544  	term.X = x
   545  	// (optional) Metadata.
   546  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   547  	if err != nil {
   548  		return errors.WithStack(err)
   549  	}
   550  	term.Metadata = md
   551  	return nil
   552  }
   553  
   554  // --- [ catchswitch ] ---------------------------------------------------------
   555  
   556  // irCatchSwitchTerm translates the AST catchswitch terminator into an
   557  // equivalent IR terminator.
   558  func (fgen *funcGen) irCatchSwitchTerm(new ir.Terminator, old *ast.CatchSwitchTerm) error {
   559  	term, ok := new.(*ir.TermCatchSwitch)
   560  	if !ok {
   561  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermCatchSwitch, got %T", new))
   562  	}
   563  	// Parent exception pad.
   564  	parentPad, err := fgen.irExceptionPad(old.ParentPad())
   565  	if err != nil {
   566  		return errors.WithStack(err)
   567  	}
   568  	term.ParentPad = parentPad
   569  	// Exception handlers.
   570  	if oldHandlers := old.Handlers().Labels(); len(oldHandlers) > 0 {
   571  		term.Handlers = make([]value.Value, len(oldHandlers))
   572  		for i, oldHandler := range oldHandlers {
   573  			handler, err := fgen.irBlock(oldHandler)
   574  			if err != nil {
   575  				return errors.WithStack(err)
   576  			}
   577  			term.Handlers[i] = handler
   578  		}
   579  	}
   580  	// Optional default unwind target basic block; if nil unwind to caller.
   581  	defaultUnwindTarget, err := fgen.irUnwindTarget(old.DefaultUnwindTarget())
   582  	if err != nil {
   583  		return errors.WithStack(err)
   584  	}
   585  	if defaultUnwindTarget != nil {
   586  		// Note: since DefaultUnwindTarget is an interface we have to be careful
   587  		// with typed nil values (e.g. `(*ir.Block)(nil)`). This is to ensure that
   588  		// DefaultUnwindTarget is nil and not `{Type: ir.Block, Value: nil}`.
   589  		//
   590  		// ref: https://golang.org/doc/faq#nil_error
   591  		term.DefaultUnwindTarget = defaultUnwindTarget
   592  	}
   593  	// (optional) Metadata.
   594  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   595  	if err != nil {
   596  		return errors.WithStack(err)
   597  	}
   598  	term.Metadata = md
   599  	return nil
   600  }
   601  
   602  // --- [ catchret ] ------------------------------------------------------------
   603  
   604  // irCatchRetTerm translates the AST catchret terminator into an equivalent IR
   605  // terminator.
   606  func (fgen *funcGen) irCatchRetTerm(new ir.Terminator, old *ast.CatchRetTerm) error {
   607  	term, ok := new.(*ir.TermCatchRet)
   608  	if !ok {
   609  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermCatchRet, got %T", new))
   610  	}
   611  	// Exit catchpad.
   612  	v, err := fgen.irValue(types.Token, old.CatchPad())
   613  	if err != nil {
   614  		return errors.WithStack(err)
   615  	}
   616  	catchpad, ok := v.(*ir.InstCatchPad)
   617  	if !ok {
   618  		return errors.Errorf("invalid catchpad type; expected *ir.InstCatchPad, got %T", v)
   619  	}
   620  	term.CatchPad = catchpad
   621  	// Target basic block to transfer control flow to.
   622  	target, err := fgen.irBlock(old.Target())
   623  	if err != nil {
   624  		return errors.WithStack(err)
   625  	}
   626  	term.Target = target
   627  	// (optional) Metadata.
   628  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   629  	if err != nil {
   630  		return errors.WithStack(err)
   631  	}
   632  	term.Metadata = md
   633  	return nil
   634  }
   635  
   636  // --- [ cleanupret ] ----------------------------------------------------------
   637  
   638  // irCleanupRetTerm translates the AST cleanupret terminator into an equivalent
   639  // IR terminator.
   640  func (fgen *funcGen) irCleanupRetTerm(new ir.Terminator, old *ast.CleanupRetTerm) error {
   641  	term, ok := new.(*ir.TermCleanupRet)
   642  	if !ok {
   643  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermCleanupRet, got %T", new))
   644  	}
   645  	// Exit cleanuppad.
   646  	v, err := fgen.irValue(types.Token, old.CleanupPad())
   647  	if err != nil {
   648  		return errors.WithStack(err)
   649  	}
   650  	cleanuppad, ok := v.(*ir.InstCleanupPad)
   651  	if !ok {
   652  		return errors.Errorf("invalid cleanuppad type; expected *ir.InstCleanupPad, got %T", v)
   653  	}
   654  	term.CleanupPad = cleanuppad
   655  	// Optional unwind target basic block; if nil unwind to caller.
   656  	unwindTarget, err := fgen.irUnwindTarget(old.UnwindTarget())
   657  	if err != nil {
   658  		return errors.WithStack(err)
   659  	}
   660  	if unwindTarget != nil {
   661  		// Note: since UnwindTarget is an interface we have to be careful
   662  		// with typed nil values (e.g. `(*ir.Block)(nil)`). This is to ensure that
   663  		// UnwindTarget is nil and not `{Type: ir.Block, Value: nil}`.
   664  		//
   665  		// ref: https://golang.org/doc/faq#nil_error
   666  		term.UnwindTarget = unwindTarget
   667  	}
   668  	// (optional) Metadata.
   669  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   670  	if err != nil {
   671  		return errors.WithStack(err)
   672  	}
   673  	term.Metadata = md
   674  	return nil
   675  }
   676  
   677  // --- [ unreachable ] ---------------------------------------------------------
   678  
   679  // irUnreachableTerm translates the AST unreachable terminator into an
   680  // equivalent IR terminator.
   681  func (fgen *funcGen) irUnreachableTerm(new ir.Terminator, old *ast.UnreachableTerm) error {
   682  	term, ok := new.(*ir.TermUnreachable)
   683  	if !ok {
   684  		panic(fmt.Errorf("invalid IR terminator for AST terminator; expected *ir.TermUnreachable, got %T", new))
   685  	}
   686  	// (optional) Metadata.
   687  	md, err := fgen.gen.irMetadataAttachments(old.Metadata())
   688  	if err != nil {
   689  		return errors.WithStack(err)
   690  	}
   691  	term.Metadata = md
   692  	return nil
   693  }