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 }