github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/defer.go (about)

     1  package compiler
     2  
     3  // This file implements the 'defer' keyword in Go.
     4  // Defer statements are implemented by transforming the function in the
     5  // following way:
     6  //   * Creating an alloca in the entry block that contains a pointer (initially
     7  //     null) to the linked list of defer frames.
     8  //   * Every time a defer statement is executed, a new defer frame is created
     9  //     using alloca with a pointer to the previous defer frame, and the head
    10  //     pointer in the entry block is replaced with a pointer to this defer
    11  //     frame.
    12  //   * On return, runtime.rundefers is called which calls all deferred functions
    13  //     from the head of the linked list until it has gone through all defer
    14  //     frames.
    15  
    16  import (
    17  	"go/types"
    18  	"strconv"
    19  
    20  	"github.com/tinygo-org/tinygo/compiler/llvmutil"
    21  	"golang.org/x/tools/go/ssa"
    22  	"tinygo.org/x/go-llvm"
    23  )
    24  
    25  // supportsRecover returns whether the compiler supports the recover() builtin
    26  // for the current architecture.
    27  func (b *builder) supportsRecover() bool {
    28  	switch b.archFamily() {
    29  	case "wasm32":
    30  		// Probably needs to be implemented using the exception handling
    31  		// proposal of WebAssembly:
    32  		// https://github.com/WebAssembly/exception-handling
    33  		return false
    34  	case "riscv64", "xtensa":
    35  		// TODO: add support for these architectures
    36  		return false
    37  	default:
    38  		return true
    39  	}
    40  }
    41  
    42  // hasDeferFrame returns whether the current function needs to catch panics and
    43  // run defers.
    44  func (b *builder) hasDeferFrame() bool {
    45  	if b.fn.Recover == nil {
    46  		return false
    47  	}
    48  	return b.supportsRecover()
    49  }
    50  
    51  // deferInitFunc sets up this function for future deferred calls. It must be
    52  // called from within the entry block when this function contains deferred
    53  // calls.
    54  func (b *builder) deferInitFunc() {
    55  	// Some setup.
    56  	b.deferFuncs = make(map[*ssa.Function]int)
    57  	b.deferInvokeFuncs = make(map[string]int)
    58  	b.deferClosureFuncs = make(map[*ssa.Function]int)
    59  	b.deferExprFuncs = make(map[ssa.Value]int)
    60  	b.deferBuiltinFuncs = make(map[ssa.Value]deferBuiltin)
    61  
    62  	// Create defer list pointer.
    63  	b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr")
    64  	b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr)
    65  
    66  	if b.hasDeferFrame() {
    67  		// Set up the defer frame with the current stack pointer.
    68  		// This assumes that the stack pointer doesn't move outside of the
    69  		// function prologue/epilogue (an invariant maintained by TinyGo but
    70  		// possibly broken by the C alloca function).
    71  		// The frame pointer is _not_ saved, because it is marked as clobbered
    72  		// in the setjmp-like inline assembly.
    73  		deferFrameType := b.getLLVMRuntimeType("deferFrame")
    74  		b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf")
    75  		stackPointer := b.readStackPointer()
    76  		b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "")
    77  
    78  		// Create the landing pad block, which is where control transfers after
    79  		// a panic.
    80  		b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad")
    81  	}
    82  }
    83  
    84  // createLandingPad fills in the landing pad block. This block runs the deferred
    85  // functions and returns (by jumping to the recover block). If the function is
    86  // still panicking after the defers are run, the panic will be re-raised in
    87  // destroyDeferFrame.
    88  func (b *builder) createLandingPad() {
    89  	b.SetInsertPointAtEnd(b.landingpad)
    90  
    91  	// Add debug info, if needed.
    92  	// The location used is the closing bracket of the function.
    93  	if b.Debug {
    94  		pos := b.program.Fset.Position(b.fn.Syntax().End())
    95  		b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
    96  	}
    97  
    98  	b.createRunDefers()
    99  
   100  	// Continue at the 'recover' block, which returns to the parent in an
   101  	// appropriate way.
   102  	b.CreateBr(b.blockEntries[b.fn.Recover])
   103  }
   104  
   105  // createInvokeCheckpoint saves the function state at the given point, to
   106  // continue at the landing pad if a panic happened. This is implemented using a
   107  // setjmp-like construct.
   108  func (b *builder) createInvokeCheckpoint() {
   109  	// Construct inline assembly equivalents of setjmp.
   110  	// The assembly works as follows:
   111  	//   * All registers (both callee-saved and caller saved) are clobbered
   112  	//     after the inline assembly returns.
   113  	//   * The assembly stores the address just past the end of the assembly
   114  	//     into the jump buffer.
   115  	//   * The return value (eax, rax, r0, etc) is set to zero in the inline
   116  	//     assembly but set to an unspecified non-zero value when jumping using
   117  	//     a longjmp.
   118  	var asmString, constraints string
   119  	resultType := b.uintptrType
   120  	switch b.archFamily() {
   121  	case "i386":
   122  		asmString = `
   123  xorl %eax, %eax
   124  movl $$1f, 4(%ebx)
   125  1:`
   126  		constraints = "={eax},{ebx},~{ebx},~{ecx},~{edx},~{esi},~{edi},~{ebp},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
   127  		// This doesn't include the floating point stack because TinyGo uses
   128  		// newer floating point instructions.
   129  	case "x86_64":
   130  		asmString = `
   131  leaq 1f(%rip), %rax
   132  movq %rax, 8(%rbx)
   133  xorq %rax, %rax
   134  1:`
   135  		constraints = "={rax},{rbx},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{xmm0},~{xmm1},~{xmm2},~{xmm3},~{xmm4},~{xmm5},~{xmm6},~{xmm7},~{xmm8},~{xmm9},~{xmm10},~{xmm11},~{xmm12},~{xmm13},~{xmm14},~{xmm15},~{xmm16},~{xmm17},~{xmm18},~{xmm19},~{xmm20},~{xmm21},~{xmm22},~{xmm23},~{xmm24},~{xmm25},~{xmm26},~{xmm27},~{xmm28},~{xmm29},~{xmm30},~{xmm31},~{fpsr},~{fpcr},~{flags},~{dirflag},~{memory}"
   136  		// This list doesn't include AVX/AVX512 registers because TinyGo
   137  		// doesn't currently enable support for AVX instructions.
   138  	case "arm":
   139  		// Note: the following assembly takes into account that the PC is
   140  		// always 4 bytes ahead on ARM. The PC that is stored always points
   141  		// to the instruction just after the assembly fragment so that
   142  		// tinygo_longjmp lands at the correct instruction.
   143  		if b.isThumb() {
   144  			// Instructions are 2 bytes in size.
   145  			asmString = `
   146  movs r0, #0
   147  mov r2, pc
   148  str r2, [r1, #4]`
   149  		} else {
   150  			// Instructions are 4 bytes in size.
   151  			asmString = `
   152  str pc, [r1, #4]
   153  movs r0, #0`
   154  		}
   155  		constraints = "={r0},{r1},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{cpsr},~{memory}"
   156  	case "aarch64":
   157  		asmString = `
   158  adr x2, 1f
   159  str x2, [x1, #8]
   160  mov x0, #0
   161  1:
   162  `
   163  		constraints = "={x0},{x1},~{x1},~{x2},~{x3},~{x4},~{x5},~{x6},~{x7},~{x8},~{x9},~{x10},~{x11},~{x12},~{x13},~{x14},~{x15},~{x16},~{x17},~{x19},~{x20},~{x21},~{x22},~{x23},~{x24},~{x25},~{x26},~{x27},~{x28},~{lr},~{q0},~{q1},~{q2},~{q3},~{q4},~{q5},~{q6},~{q7},~{q8},~{q9},~{q10},~{q11},~{q12},~{q13},~{q14},~{q15},~{q16},~{q17},~{q18},~{q19},~{q20},~{q21},~{q22},~{q23},~{q24},~{q25},~{q26},~{q27},~{q28},~{q29},~{q30},~{nzcv},~{ffr},~{vg},~{memory}"
   164  		if b.GOOS != "darwin" && b.GOOS != "windows" {
   165  			// These registers cause the following warning when compiling for
   166  			// MacOS and Windows:
   167  			//     warning: inline asm clobber list contains reserved registers:
   168  			//     X18, FP
   169  			//     Reserved registers on the clobber list may not be preserved
   170  			//     across the asm statement, and clobbering them may lead to
   171  			//     undefined behaviour.
   172  			constraints += ",~{x18},~{fp}"
   173  		}
   174  		// TODO: SVE registers, which we don't use in TinyGo at the moment.
   175  	case "avr":
   176  		// Note: the Y register (R28:R29) is a fixed register and therefore
   177  		// needs to be saved manually. TODO: do this only once per function with
   178  		// a defer frame, not for every call.
   179  		resultType = b.ctx.Int8Type()
   180  		asmString = `
   181  ldi r24, pm_lo8(1f)
   182  ldi r25, pm_hi8(1f)
   183  std z+2, r24
   184  std z+3, r25
   185  std z+4, r28
   186  std z+5, r29
   187  ldi r24, 0
   188  1:`
   189  		constraints = "={r24},z,~{r0},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r25},~{r26},~{r27}"
   190  	case "riscv32":
   191  		asmString = `
   192  la a2, 1f
   193  sw a2, 4(a1)
   194  li a0, 0
   195  1:`
   196  		constraints = "={a0},{a1},~{a1},~{a2},~{a3},~{a4},~{a5},~{a6},~{a7},~{s0},~{s1},~{s2},~{s3},~{s4},~{s5},~{s6},~{s7},~{s8},~{s9},~{s10},~{s11},~{t0},~{t1},~{t2},~{t3},~{t4},~{t5},~{t6},~{ra},~{f0},~{f1},~{f2},~{f3},~{f4},~{f5},~{f6},~{f7},~{f8},~{f9},~{f10},~{f11},~{f12},~{f13},~{f14},~{f15},~{f16},~{f17},~{f18},~{f19},~{f20},~{f21},~{f22},~{f23},~{f24},~{f25},~{f26},~{f27},~{f28},~{f29},~{f30},~{f31},~{memory}"
   197  	default:
   198  		// This case should have been handled by b.supportsRecover().
   199  		b.addError(b.fn.Pos(), "unknown architecture for defer: "+b.archFamily())
   200  	}
   201  	asmType := llvm.FunctionType(resultType, []llvm.Type{b.deferFrame.Type()}, false)
   202  	asm := llvm.InlineAsm(asmType, asmString, constraints, false, false, 0, false)
   203  	result := b.CreateCall(asmType, asm, []llvm.Value{b.deferFrame}, "setjmp")
   204  	result.AddCallSiteAttribute(-1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("returns_twice"), 0))
   205  	isZero := b.CreateICmp(llvm.IntEQ, result, llvm.ConstInt(resultType, 0, false), "setjmp.result")
   206  	continueBB := b.insertBasicBlock("")
   207  	b.CreateCondBr(isZero, continueBB, b.landingpad)
   208  	b.SetInsertPointAtEnd(continueBB)
   209  	b.blockExits[b.currentBlock] = continueBB
   210  }
   211  
   212  // isInLoop checks if there is a path from a basic block to itself.
   213  func isInLoop(start *ssa.BasicBlock) bool {
   214  	// Use a breadth-first search to scan backwards through the block graph.
   215  	queue := []*ssa.BasicBlock{start}
   216  	checked := map[*ssa.BasicBlock]struct{}{}
   217  
   218  	for len(queue) > 0 {
   219  		// pop a block off of the queue
   220  		block := queue[len(queue)-1]
   221  		queue = queue[:len(queue)-1]
   222  
   223  		// Search through predecessors.
   224  		// Searching backwards means that this is pretty fast when the block is close to the start of the function.
   225  		// Defers are often placed near the start of the function.
   226  		for _, pred := range block.Preds {
   227  			if pred == start {
   228  				// cycle found
   229  				return true
   230  			}
   231  
   232  			if _, ok := checked[pred]; ok {
   233  				// block already checked
   234  				continue
   235  			}
   236  
   237  			// add to queue and checked map
   238  			queue = append(queue, pred)
   239  			checked[pred] = struct{}{}
   240  		}
   241  	}
   242  
   243  	return false
   244  }
   245  
   246  // createDefer emits a single defer instruction, to be run when this function
   247  // returns.
   248  func (b *builder) createDefer(instr *ssa.Defer) {
   249  	// The pointer to the previous defer struct, which we will replace to
   250  	// make a linked list.
   251  	next := b.CreateLoad(b.dataPtrType, b.deferPtr, "defer.next")
   252  
   253  	var values []llvm.Value
   254  	valueTypes := []llvm.Type{b.uintptrType, next.Type()}
   255  	if instr.Call.IsInvoke() {
   256  		// Method call on an interface.
   257  
   258  		// Get callback type number.
   259  		methodName := instr.Call.Method.FullName()
   260  		if _, ok := b.deferInvokeFuncs[methodName]; !ok {
   261  			b.deferInvokeFuncs[methodName] = len(b.allDeferFuncs)
   262  			b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
   263  		}
   264  		callback := llvm.ConstInt(b.uintptrType, uint64(b.deferInvokeFuncs[methodName]), false)
   265  
   266  		// Collect all values to be put in the struct (starting with
   267  		// runtime._defer fields, followed by the call parameters).
   268  		itf := b.getValue(instr.Call.Value, getPos(instr)) // interface
   269  		typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
   270  		receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
   271  		values = []llvm.Value{callback, next, typecode, receiverValue}
   272  		valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
   273  		for _, arg := range instr.Call.Args {
   274  			val := b.getValue(arg, getPos(instr))
   275  			values = append(values, val)
   276  			valueTypes = append(valueTypes, val.Type())
   277  		}
   278  
   279  	} else if callee, ok := instr.Call.Value.(*ssa.Function); ok {
   280  		// Regular function call.
   281  		if _, ok := b.deferFuncs[callee]; !ok {
   282  			b.deferFuncs[callee] = len(b.allDeferFuncs)
   283  			b.allDeferFuncs = append(b.allDeferFuncs, callee)
   284  		}
   285  		callback := llvm.ConstInt(b.uintptrType, uint64(b.deferFuncs[callee]), false)
   286  
   287  		// Collect all values to be put in the struct (starting with
   288  		// runtime._defer fields).
   289  		values = []llvm.Value{callback, next}
   290  		for _, param := range instr.Call.Args {
   291  			llvmParam := b.getValue(param, getPos(instr))
   292  			values = append(values, llvmParam)
   293  			valueTypes = append(valueTypes, llvmParam.Type())
   294  		}
   295  
   296  	} else if makeClosure, ok := instr.Call.Value.(*ssa.MakeClosure); ok {
   297  		// Immediately applied function literal with free variables.
   298  
   299  		// Extract the context from the closure. We won't need the function
   300  		// pointer.
   301  		// TODO: ignore this closure entirely and put pointers to the free
   302  		// variables directly in the defer struct, avoiding a memory allocation.
   303  		closure := b.getValue(instr.Call.Value, getPos(instr))
   304  		context := b.CreateExtractValue(closure, 0, "")
   305  
   306  		// Get the callback number.
   307  		fn := makeClosure.Fn.(*ssa.Function)
   308  		if _, ok := b.deferClosureFuncs[fn]; !ok {
   309  			b.deferClosureFuncs[fn] = len(b.allDeferFuncs)
   310  			b.allDeferFuncs = append(b.allDeferFuncs, makeClosure)
   311  		}
   312  		callback := llvm.ConstInt(b.uintptrType, uint64(b.deferClosureFuncs[fn]), false)
   313  
   314  		// Collect all values to be put in the struct (starting with
   315  		// runtime._defer fields, followed by all parameters including the
   316  		// context pointer).
   317  		values = []llvm.Value{callback, next}
   318  		for _, param := range instr.Call.Args {
   319  			llvmParam := b.getValue(param, getPos(instr))
   320  			values = append(values, llvmParam)
   321  			valueTypes = append(valueTypes, llvmParam.Type())
   322  		}
   323  		values = append(values, context)
   324  		valueTypes = append(valueTypes, context.Type())
   325  
   326  	} else if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok {
   327  		var argTypes []types.Type
   328  		var argValues []llvm.Value
   329  		for _, arg := range instr.Call.Args {
   330  			argTypes = append(argTypes, arg.Type())
   331  			argValues = append(argValues, b.getValue(arg, getPos(instr)))
   332  		}
   333  
   334  		if _, ok := b.deferBuiltinFuncs[instr.Call.Value]; !ok {
   335  			b.deferBuiltinFuncs[instr.Call.Value] = deferBuiltin{
   336  				callName: builtin.Name(),
   337  				pos:      builtin.Pos(),
   338  				argTypes: argTypes,
   339  				callback: len(b.allDeferFuncs),
   340  			}
   341  			b.allDeferFuncs = append(b.allDeferFuncs, instr.Call.Value)
   342  		}
   343  		callback := llvm.ConstInt(b.uintptrType, uint64(b.deferBuiltinFuncs[instr.Call.Value].callback), false)
   344  
   345  		// Collect all values to be put in the struct (starting with
   346  		// runtime._defer fields).
   347  		values = []llvm.Value{callback, next}
   348  		for _, param := range argValues {
   349  			values = append(values, param)
   350  			valueTypes = append(valueTypes, param.Type())
   351  		}
   352  
   353  	} else {
   354  		funcValue := b.getValue(instr.Call.Value, getPos(instr))
   355  
   356  		if _, ok := b.deferExprFuncs[instr.Call.Value]; !ok {
   357  			b.deferExprFuncs[instr.Call.Value] = len(b.allDeferFuncs)
   358  			b.allDeferFuncs = append(b.allDeferFuncs, &instr.Call)
   359  		}
   360  
   361  		callback := llvm.ConstInt(b.uintptrType, uint64(b.deferExprFuncs[instr.Call.Value]), false)
   362  
   363  		// Collect all values to be put in the struct (starting with
   364  		// runtime._defer fields, followed by all parameters including the
   365  		// context pointer).
   366  		values = []llvm.Value{callback, next, funcValue}
   367  		valueTypes = append(valueTypes, funcValue.Type())
   368  		for _, param := range instr.Call.Args {
   369  			llvmParam := b.getValue(param, getPos(instr))
   370  			values = append(values, llvmParam)
   371  			valueTypes = append(valueTypes, llvmParam.Type())
   372  		}
   373  	}
   374  
   375  	// Make a struct out of the collected values to put in the deferred call
   376  	// struct.
   377  	deferredCallType := b.ctx.StructType(valueTypes, false)
   378  	deferredCall := llvm.ConstNull(deferredCallType)
   379  	for i, value := range values {
   380  		deferredCall = b.CreateInsertValue(deferredCall, value, i, "")
   381  	}
   382  
   383  	// Put this struct in an allocation.
   384  	var alloca llvm.Value
   385  	if !isInLoop(instr.Block()) {
   386  		// This can safely use a stack allocation.
   387  		alloca = llvmutil.CreateEntryBlockAlloca(b.Builder, deferredCallType, "defer.alloca")
   388  	} else {
   389  		// This may be hit a variable number of times, so use a heap allocation.
   390  		size := b.targetData.TypeAllocSize(deferredCallType)
   391  		sizeValue := llvm.ConstInt(b.uintptrType, size, false)
   392  		nilPtr := llvm.ConstNull(b.dataPtrType)
   393  		alloca = b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, "defer.alloc.call")
   394  	}
   395  	if b.NeedsStackObjects {
   396  		b.trackPointer(alloca)
   397  	}
   398  	b.CreateStore(deferredCall, alloca)
   399  
   400  	// Push it on top of the linked list by replacing deferPtr.
   401  	b.CreateStore(alloca, b.deferPtr)
   402  }
   403  
   404  // createRunDefers emits code to run all deferred functions.
   405  func (b *builder) createRunDefers() {
   406  	deferType := b.getLLVMRuntimeType("_defer")
   407  
   408  	// Add a loop like the following:
   409  	//     for stack != nil {
   410  	//         _stack := stack
   411  	//         stack = stack.next
   412  	//         switch _stack.callback {
   413  	//         case 0:
   414  	//             // run first deferred call
   415  	//         case 1:
   416  	//             // run second deferred call
   417  	//             // etc.
   418  	//         default:
   419  	//             unreachable
   420  	//         }
   421  	//     }
   422  
   423  	// Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end.
   424  	end := b.insertBasicBlock("rundefers.end")
   425  	unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default")
   426  	loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop")
   427  	loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead")
   428  	b.CreateBr(loophead)
   429  
   430  	// Create loop head:
   431  	//     for stack != nil {
   432  	b.SetInsertPointAtEnd(loophead)
   433  	deferData := b.CreateLoad(b.dataPtrType, b.deferPtr, "")
   434  	stackIsNil := b.CreateICmp(llvm.IntEQ, deferData, llvm.ConstPointerNull(deferData.Type()), "stackIsNil")
   435  	b.CreateCondBr(stackIsNil, end, loop)
   436  
   437  	// Create loop body:
   438  	//     _stack := stack
   439  	//     stack = stack.next
   440  	//     switch stack.callback {
   441  	b.SetInsertPointAtEnd(loop)
   442  	nextStackGEP := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
   443  		llvm.ConstInt(b.ctx.Int32Type(), 0, false),
   444  		llvm.ConstInt(b.ctx.Int32Type(), 1, false), // .next field
   445  	}, "stack.next.gep")
   446  	nextStack := b.CreateLoad(b.dataPtrType, nextStackGEP, "stack.next")
   447  	b.CreateStore(nextStack, b.deferPtr)
   448  	gep := b.CreateInBoundsGEP(deferType, deferData, []llvm.Value{
   449  		llvm.ConstInt(b.ctx.Int32Type(), 0, false),
   450  		llvm.ConstInt(b.ctx.Int32Type(), 0, false), // .callback field
   451  	}, "callback.gep")
   452  	callback := b.CreateLoad(b.uintptrType, gep, "callback")
   453  	sw := b.CreateSwitch(callback, unreachable, len(b.allDeferFuncs))
   454  
   455  	for i, callback := range b.allDeferFuncs {
   456  		// Create switch case, for example:
   457  		//     case 0:
   458  		//         // run first deferred call
   459  		block := b.insertBasicBlock("rundefers.callback" + strconv.Itoa(i))
   460  		sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block)
   461  		b.SetInsertPointAtEnd(block)
   462  		switch callback := callback.(type) {
   463  		case *ssa.CallCommon:
   464  			// Call on an value or interface value.
   465  
   466  			// Get the real defer struct type and cast to it.
   467  			valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
   468  
   469  			if !callback.IsInvoke() {
   470  				//Expect funcValue to be passed through the deferred call.
   471  				valueTypes = append(valueTypes, b.getFuncType(callback.Signature()))
   472  			} else {
   473  				//Expect typecode
   474  				valueTypes = append(valueTypes, b.dataPtrType, b.dataPtrType)
   475  			}
   476  
   477  			for _, arg := range callback.Args {
   478  				valueTypes = append(valueTypes, b.getLLVMType(arg.Type()))
   479  			}
   480  
   481  			// Extract the params from the struct (including receiver).
   482  			forwardParams := []llvm.Value{}
   483  			zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
   484  			deferredCallType := b.ctx.StructType(valueTypes, false)
   485  			for i := 2; i < len(valueTypes); i++ {
   486  				gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "gep")
   487  				forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
   488  				forwardParams = append(forwardParams, forwardParam)
   489  			}
   490  
   491  			var fnPtr llvm.Value
   492  			var fnType llvm.Type
   493  
   494  			if !callback.IsInvoke() {
   495  				// Isolate the func value.
   496  				funcValue := forwardParams[0]
   497  				forwardParams = forwardParams[1:]
   498  
   499  				//Get function pointer and context
   500  				var context llvm.Value
   501  				fnPtr, context = b.decodeFuncValue(funcValue)
   502  				fnType = b.getLLVMFunctionType(callback.Signature())
   503  
   504  				//Pass context
   505  				forwardParams = append(forwardParams, context)
   506  			} else {
   507  				// Move typecode from the start to the end of the list of
   508  				// parameters.
   509  				forwardParams = append(forwardParams[1:], forwardParams[0])
   510  				fnPtr = b.getInvokeFunction(callback)
   511  				fnType = fnPtr.GlobalValueType()
   512  
   513  				// Add the context parameter. An interface call cannot also be a
   514  				// closure but we have to supply the parameter anyway for platforms
   515  				// with a strict calling convention.
   516  				forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
   517  			}
   518  
   519  			b.createCall(fnType, fnPtr, forwardParams, "")
   520  
   521  		case *ssa.Function:
   522  			// Direct call.
   523  
   524  			// Get the real defer struct type and cast to it.
   525  			valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
   526  			for _, param := range getParams(callback.Signature) {
   527  				valueTypes = append(valueTypes, b.getLLVMType(param.Type()))
   528  			}
   529  			deferredCallType := b.ctx.StructType(valueTypes, false)
   530  
   531  			// Extract the params from the struct.
   532  			forwardParams := []llvm.Value{}
   533  			zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
   534  			for i := range getParams(callback.Signature) {
   535  				gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
   536  				forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
   537  				forwardParams = append(forwardParams, forwardParam)
   538  			}
   539  
   540  			// Plain TinyGo functions add some extra parameters to implement async functionality and function receivers.
   541  			// These parameters should not be supplied when calling into an external C/ASM function.
   542  			if !b.getFunctionInfo(callback).exported {
   543  				// Add the context parameter. We know it is ignored by the receiving
   544  				// function, but we have to pass one anyway.
   545  				forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType))
   546  			}
   547  
   548  			// Call real function.
   549  			fnType, fn := b.getFunction(callback)
   550  			b.createInvoke(fnType, fn, forwardParams, "")
   551  
   552  		case *ssa.MakeClosure:
   553  			// Get the real defer struct type and cast to it.
   554  			fn := callback.Fn.(*ssa.Function)
   555  			valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
   556  			params := fn.Signature.Params()
   557  			for i := 0; i < params.Len(); i++ {
   558  				valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
   559  			}
   560  			valueTypes = append(valueTypes, b.dataPtrType) // closure
   561  			deferredCallType := b.ctx.StructType(valueTypes, false)
   562  
   563  			// Extract the params from the struct.
   564  			forwardParams := []llvm.Value{}
   565  			zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
   566  			for i := 2; i < len(valueTypes); i++ {
   567  				gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "")
   568  				forwardParam := b.CreateLoad(valueTypes[i], gep, "param")
   569  				forwardParams = append(forwardParams, forwardParam)
   570  			}
   571  
   572  			// Call deferred function.
   573  			fnType, llvmFn := b.getFunction(fn)
   574  			b.createCall(fnType, llvmFn, forwardParams, "")
   575  		case *ssa.Builtin:
   576  			db := b.deferBuiltinFuncs[callback]
   577  
   578  			//Get parameter types
   579  			valueTypes := []llvm.Type{b.uintptrType, b.dataPtrType}
   580  
   581  			//Get signature from call results
   582  			params := callback.Type().Underlying().(*types.Signature).Params()
   583  			for i := 0; i < params.Len(); i++ {
   584  				valueTypes = append(valueTypes, b.getLLVMType(params.At(i).Type()))
   585  			}
   586  
   587  			deferredCallType := b.ctx.StructType(valueTypes, false)
   588  
   589  			// Extract the params from the struct.
   590  			var argValues []llvm.Value
   591  			zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
   592  			for i := 0; i < params.Len(); i++ {
   593  				gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep")
   594  				forwardParam := b.CreateLoad(valueTypes[i+2], gep, "param")
   595  				argValues = append(argValues, forwardParam)
   596  			}
   597  
   598  			_, err := b.createBuiltin(db.argTypes, argValues, db.callName, db.pos)
   599  			if err != nil {
   600  				b.diagnostics = append(b.diagnostics, err)
   601  			}
   602  		default:
   603  			panic("unknown deferred function type")
   604  		}
   605  
   606  		// Branch back to the start of the loop.
   607  		b.CreateBr(loophead)
   608  	}
   609  
   610  	// Create default unreachable block:
   611  	//     default:
   612  	//         unreachable
   613  	//     }
   614  	b.SetInsertPointAtEnd(unreachable)
   615  	b.CreateUnreachable()
   616  
   617  	// End of loop.
   618  	b.SetInsertPointAtEnd(end)
   619  }