github.com/aykevl/tinygo@v0.5.0/compiler/goroutine-lowering.go (about)

     1  package compiler
     2  
     3  // This file lowers goroutine pseudo-functions into coroutines scheduled by a
     4  // scheduler at runtime. It uses coroutine support in LLVM for this
     5  // transformation: https://llvm.org/docs/Coroutines.html
     6  //
     7  // For example, take the following code:
     8  //
     9  //     func main() {
    10  //         go foo()
    11  //         time.Sleep(2 * time.Second)
    12  //         println("some other operation")
    13  //         bar()
    14  //         println("done")
    15  //     }
    16  //
    17  //     func foo() {
    18  //         for {
    19  //             println("foo!")
    20  //             time.Sleep(time.Second)
    21  //         }
    22  //     }
    23  //
    24  //     func bar() {
    25  //         time.Sleep(time.Second)
    26  //         println("blocking operation completed)
    27  //     }
    28  //
    29  // It is transformed by the IR generator in compiler.go into the following
    30  // pseudo-Go code:
    31  //
    32  //     func main() {
    33  //         fn := runtime.makeGoroutine(foo)
    34  //         fn()
    35  //         time.Sleep(2 * time.Second)
    36  //         println("some other operation")
    37  //         bar() // imagine an 'await' keyword in front of this call
    38  //         println("done")
    39  //     }
    40  //
    41  //     func foo() {
    42  //         for {
    43  //             println("foo!")
    44  //             time.Sleep(time.Second)
    45  //         }
    46  //     }
    47  //
    48  //     func bar() {
    49  //         time.Sleep(time.Second)
    50  //         println("blocking operation completed)
    51  //     }
    52  //
    53  // The pass in this file transforms this code even further, to the following
    54  // async/await style pseudocode:
    55  //
    56  //     func main(parent) {
    57  //         hdl := llvm.makeCoroutine()
    58  //         foo(nil)                                // do not pass the parent coroutine: this is an independent goroutine
    59  //         runtime.sleepTask(hdl, 2 * time.Second) // ask the scheduler to re-activate this coroutine at the right time
    60  //         llvm.suspend(hdl)                       // suspend point
    61  //         println("some other operation")
    62  //         bar(hdl)                                // await, pass a continuation (hdl) to bar
    63  //         llvm.suspend(hdl)                       // suspend point, wait for the callee to re-activate
    64  //         println("done")
    65  //         runtime.activateTask(parent)            // re-activate the parent (nop, there is no parent)
    66  //     }
    67  //
    68  //     func foo(parent) {
    69  //         hdl := llvm.makeCoroutine()
    70  //         for {
    71  //             println("foo!")
    72  //             runtime.sleepTask(hdl, time.Second) // ask the scheduler to re-activate this coroutine at the right time
    73  //             llvm.suspend(hdl)                   // suspend point
    74  //         }
    75  //     }
    76  //
    77  //     func bar(parent) {
    78  //         hdl := llvm.makeCoroutine()
    79  //         runtime.sleepTask(hdl, time.Second) // ask the scheduler to re-activate this coroutine at the right time
    80  //         llvm.suspend(hdl)                   // suspend point
    81  //         println("blocking operation completed)
    82  //         runtime.activateTask(parent)        // re-activate the parent coroutine before returning
    83  //     }
    84  //
    85  // The real LLVM code is more complicated, but this is the general idea.
    86  //
    87  // The LLVM coroutine passes will then process this file further transforming
    88  // these three functions into coroutines. Most of the actual work is done by the
    89  // scheduler, which runs in the background scheduling all coroutines.
    90  
    91  import (
    92  	"errors"
    93  	"strings"
    94  
    95  	"tinygo.org/x/go-llvm"
    96  )
    97  
    98  type asyncFunc struct {
    99  	taskHandle       llvm.Value
   100  	cleanupBlock     llvm.BasicBlock
   101  	suspendBlock     llvm.BasicBlock
   102  	unreachableBlock llvm.BasicBlock
   103  }
   104  
   105  // LowerGoroutines is a pass called during optimization that transforms the IR
   106  // into one where all blocking functions are turned into goroutines and blocking
   107  // calls into await calls.
   108  func (c *Compiler) LowerGoroutines() error {
   109  	needsScheduler, err := c.markAsyncFunctions()
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	uses := getUses(c.mod.NamedFunction("runtime.callMain"))
   115  	if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
   116  		panic("expected exactly 1 call of runtime.callMain, check the entry point")
   117  	}
   118  	mainCall := uses[0]
   119  
   120  	// Replace call of runtime.callMain() with a real call to main.main(),
   121  	// optionally followed by a call to runtime.scheduler().
   122  	c.builder.SetInsertPointBefore(mainCall)
   123  	realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
   124  	c.builder.CreateCall(realMain, []llvm.Value{llvm.Undef(c.i8ptrType), llvm.ConstPointerNull(c.i8ptrType)}, "")
   125  	if needsScheduler {
   126  		c.createRuntimeCall("scheduler", nil, "")
   127  	}
   128  	mainCall.EraseFromParentAsInstruction()
   129  
   130  	if !needsScheduler {
   131  		go_scheduler := c.mod.NamedFunction("go_scheduler")
   132  		if !go_scheduler.IsNil() {
   133  			// This is the WebAssembly backend.
   134  			// There is no need to export the go_scheduler function, but it is
   135  			// still exported. Make sure it is optimized away.
   136  			go_scheduler.SetLinkage(llvm.InternalLinkage)
   137  		}
   138  	}
   139  
   140  	// main.main was set to external linkage during IR construction. Set it to
   141  	// internal linkage to enable interprocedural optimizations.
   142  	realMain.SetLinkage(llvm.InternalLinkage)
   143  	c.mod.NamedFunction("runtime.alloc").SetLinkage(llvm.InternalLinkage)
   144  	c.mod.NamedFunction("runtime.free").SetLinkage(llvm.InternalLinkage)
   145  	c.mod.NamedFunction("runtime.chanSend").SetLinkage(llvm.InternalLinkage)
   146  	c.mod.NamedFunction("runtime.chanRecv").SetLinkage(llvm.InternalLinkage)
   147  	c.mod.NamedFunction("runtime.sleepTask").SetLinkage(llvm.InternalLinkage)
   148  	c.mod.NamedFunction("runtime.activateTask").SetLinkage(llvm.InternalLinkage)
   149  	c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.InternalLinkage)
   150  
   151  	return nil
   152  }
   153  
   154  // markAsyncFunctions does the bulk of the work of lowering goroutines. It
   155  // determines whether a scheduler is needed, and if it is, it transforms
   156  // blocking operations into goroutines and blocking calls into await calls.
   157  //
   158  // It does the following operations:
   159  //    * Find all blocking functions.
   160  //    * Determine whether a scheduler is necessary. If not, it skips the
   161  //      following operations.
   162  //    * Transform call instructions into await calls.
   163  //    * Transform return instructions into final suspends.
   164  //    * Set up the coroutine frames for async functions.
   165  //    * Transform blocking calls into their async equivalents.
   166  func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
   167  	var worklist []llvm.Value
   168  
   169  	sleep := c.mod.NamedFunction("time.Sleep")
   170  	if !sleep.IsNil() {
   171  		worklist = append(worklist, sleep)
   172  	}
   173  	deadlockStub := c.mod.NamedFunction("runtime.deadlockStub")
   174  	if !deadlockStub.IsNil() {
   175  		worklist = append(worklist, deadlockStub)
   176  	}
   177  	chanSendStub := c.mod.NamedFunction("runtime.chanSendStub")
   178  	if !chanSendStub.IsNil() {
   179  		worklist = append(worklist, chanSendStub)
   180  	}
   181  	chanRecvStub := c.mod.NamedFunction("runtime.chanRecvStub")
   182  	if !chanRecvStub.IsNil() {
   183  		worklist = append(worklist, chanRecvStub)
   184  	}
   185  
   186  	if len(worklist) == 0 {
   187  		// There are no blocking operations, so no need to transform anything.
   188  		return false, c.lowerMakeGoroutineCalls()
   189  	}
   190  
   191  	// Find all async functions.
   192  	// Keep reducing this worklist by marking a function as recursively async
   193  	// from the worklist and pushing all its parents that are non-async.
   194  	// This is somewhat similar to a worklist in a mark-sweep garbage collector:
   195  	// the work items are then grey objects.
   196  	asyncFuncs := make(map[llvm.Value]*asyncFunc)
   197  	asyncList := make([]llvm.Value, 0, 4)
   198  	for len(worklist) != 0 {
   199  		// Pick the topmost.
   200  		f := worklist[len(worklist)-1]
   201  		worklist = worklist[:len(worklist)-1]
   202  		if _, ok := asyncFuncs[f]; ok {
   203  			continue // already processed
   204  		}
   205  		// Add to set of async functions.
   206  		asyncFuncs[f] = &asyncFunc{}
   207  		asyncList = append(asyncList, f)
   208  
   209  		// Add all callees to the worklist.
   210  		for _, use := range getUses(f) {
   211  			if use.IsConstant() && use.Opcode() == llvm.BitCast {
   212  				bitcastUses := getUses(use)
   213  				for _, call := range bitcastUses {
   214  					if call.IsACallInst().IsNil() || call.CalledValue().Name() != "runtime.makeGoroutine" {
   215  						return false, errors.New("async function " + f.Name() + " incorrectly used in bitcast, expected runtime.makeGoroutine")
   216  					}
   217  				}
   218  				// This is a go statement. Do not mark the parent as async, as
   219  				// starting a goroutine is not a blocking operation.
   220  				continue
   221  			}
   222  			if use.IsACallInst().IsNil() {
   223  				// Not a call instruction. Maybe a store to a global? In any
   224  				// case, this requires support for async calls across function
   225  				// pointers which is not yet supported.
   226  				return false, errors.New("async function " + f.Name() + " used as function pointer")
   227  			}
   228  			parent := use.InstructionParent().Parent()
   229  			for i := 0; i < use.OperandsCount()-1; i++ {
   230  				if use.Operand(i) == f {
   231  					return false, errors.New("async function " + f.Name() + " used as function pointer in " + parent.Name())
   232  				}
   233  			}
   234  			worklist = append(worklist, parent)
   235  		}
   236  	}
   237  
   238  	// Check whether a scheduler is needed.
   239  	makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
   240  	if c.GOOS == "js" && strings.HasPrefix(c.Triple, "wasm") {
   241  		// JavaScript always needs a scheduler, as in general no blocking
   242  		// operations are possible. Blocking operations block the browser UI,
   243  		// which is very bad.
   244  		needsScheduler = true
   245  	} else {
   246  		// Only use a scheduler when an async goroutine is started. When the
   247  		// goroutine is not async (does not do any blocking operation), no
   248  		// scheduler is necessary as it can be called directly.
   249  		for _, use := range getUses(makeGoroutine) {
   250  			// Input param must be const bitcast of function.
   251  			bitcast := use.Operand(0)
   252  			if !bitcast.IsConstant() || bitcast.Opcode() != llvm.BitCast {
   253  				panic("expected const bitcast operand of runtime.makeGoroutine")
   254  			}
   255  			goroutine := bitcast.Operand(0)
   256  			if _, ok := asyncFuncs[goroutine]; ok {
   257  				needsScheduler = true
   258  				break
   259  			}
   260  		}
   261  	}
   262  
   263  	if !needsScheduler {
   264  		// No scheduler is needed. Do not transform all functions here.
   265  		// However, make sure that all go calls (which are all non-async) are
   266  		// transformed into regular calls.
   267  		return false, c.lowerMakeGoroutineCalls()
   268  	}
   269  
   270  	// Create a few LLVM intrinsics for coroutine support.
   271  
   272  	coroIdType := llvm.FunctionType(c.ctx.TokenType(), []llvm.Type{c.ctx.Int32Type(), c.i8ptrType, c.i8ptrType, c.i8ptrType}, false)
   273  	coroIdFunc := llvm.AddFunction(c.mod, "llvm.coro.id", coroIdType)
   274  
   275  	coroSizeType := llvm.FunctionType(c.ctx.Int32Type(), nil, false)
   276  	coroSizeFunc := llvm.AddFunction(c.mod, "llvm.coro.size.i32", coroSizeType)
   277  
   278  	coroBeginType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.ctx.TokenType(), c.i8ptrType}, false)
   279  	coroBeginFunc := llvm.AddFunction(c.mod, "llvm.coro.begin", coroBeginType)
   280  
   281  	coroPromiseType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.i8ptrType, c.ctx.Int32Type(), c.ctx.Int1Type()}, false)
   282  	coroPromiseFunc := llvm.AddFunction(c.mod, "llvm.coro.promise", coroPromiseType)
   283  
   284  	coroSuspendType := llvm.FunctionType(c.ctx.Int8Type(), []llvm.Type{c.ctx.TokenType(), c.ctx.Int1Type()}, false)
   285  	coroSuspendFunc := llvm.AddFunction(c.mod, "llvm.coro.suspend", coroSuspendType)
   286  
   287  	coroEndType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.i8ptrType, c.ctx.Int1Type()}, false)
   288  	coroEndFunc := llvm.AddFunction(c.mod, "llvm.coro.end", coroEndType)
   289  
   290  	coroFreeType := llvm.FunctionType(c.i8ptrType, []llvm.Type{c.ctx.TokenType(), c.i8ptrType}, false)
   291  	coroFreeFunc := llvm.AddFunction(c.mod, "llvm.coro.free", coroFreeType)
   292  
   293  	// Transform all async functions into coroutines.
   294  	for _, f := range asyncList {
   295  		if f == sleep || f == deadlockStub || f == chanSendStub || f == chanRecvStub {
   296  			continue
   297  		}
   298  
   299  		frame := asyncFuncs[f]
   300  		frame.cleanupBlock = c.ctx.AddBasicBlock(f, "task.cleanup")
   301  		frame.suspendBlock = c.ctx.AddBasicBlock(f, "task.suspend")
   302  		frame.unreachableBlock = c.ctx.AddBasicBlock(f, "task.unreachable")
   303  
   304  		// Scan for async calls and return instructions that need to have
   305  		// suspend points inserted.
   306  		var asyncCalls []llvm.Value
   307  		var returns []llvm.Value
   308  		for bb := f.EntryBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
   309  			for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
   310  				if !inst.IsACallInst().IsNil() {
   311  					callee := inst.CalledValue()
   312  					if _, ok := asyncFuncs[callee]; !ok || callee == sleep || callee == deadlockStub || callee == chanSendStub || callee == chanRecvStub {
   313  						continue
   314  					}
   315  					asyncCalls = append(asyncCalls, inst)
   316  				} else if !inst.IsAReturnInst().IsNil() {
   317  					returns = append(returns, inst)
   318  				}
   319  			}
   320  		}
   321  
   322  		// Coroutine setup.
   323  		c.builder.SetInsertPointBefore(f.EntryBasicBlock().FirstInstruction())
   324  		taskState := c.builder.CreateAlloca(c.mod.GetTypeByName("runtime.taskState"), "task.state")
   325  		stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8")
   326  		id := c.builder.CreateCall(coroIdFunc, []llvm.Value{
   327  			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   328  			stateI8,
   329  			llvm.ConstNull(c.i8ptrType),
   330  			llvm.ConstNull(c.i8ptrType),
   331  		}, "task.token")
   332  		size := c.builder.CreateCall(coroSizeFunc, nil, "task.size")
   333  		if c.targetData.TypeAllocSize(size.Type()) > c.targetData.TypeAllocSize(c.uintptrType) {
   334  			size = c.builder.CreateTrunc(size, c.uintptrType, "task.size.uintptr")
   335  		} else if c.targetData.TypeAllocSize(size.Type()) < c.targetData.TypeAllocSize(c.uintptrType) {
   336  			size = c.builder.CreateZExt(size, c.uintptrType, "task.size.uintptr")
   337  		}
   338  		data := c.createRuntimeCall("alloc", []llvm.Value{size}, "task.data")
   339  		frame.taskHandle = c.builder.CreateCall(coroBeginFunc, []llvm.Value{id, data}, "task.handle")
   340  
   341  		// Modify async calls so this function suspends right after the child
   342  		// returns, because the child is probably not finished yet. Wait until
   343  		// the child reactivates the parent.
   344  		for _, inst := range asyncCalls {
   345  			inst.SetOperand(inst.OperandsCount()-2, frame.taskHandle)
   346  
   347  			// Split this basic block.
   348  			await := c.splitBasicBlock(inst, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.await")
   349  
   350  			// Set task state to TASK_STATE_CALL.
   351  			c.builder.SetInsertPointAtEnd(inst.InstructionParent())
   352  
   353  			// Suspend.
   354  			continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
   355  				llvm.ConstNull(c.ctx.TokenType()),
   356  				llvm.ConstInt(c.ctx.Int1Type(), 0, false),
   357  			}, "")
   358  			sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
   359  			sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), await)
   360  			sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
   361  		}
   362  
   363  		// Replace return instructions with suspend points that should
   364  		// reactivate the parent coroutine.
   365  		for _, inst := range returns {
   366  			if inst.OperandsCount() == 0 {
   367  				// These properties were added by the functionattrs pass.
   368  				// Remove them, because now we start using the parameter.
   369  				// https://llvm.org/docs/Passes.html#functionattrs-deduce-function-attributes
   370  				for _, kind := range []string{"nocapture", "readnone"} {
   371  					kindID := llvm.AttributeKindID(kind)
   372  					f.RemoveEnumAttributeAtIndex(f.ParamsCount(), kindID)
   373  				}
   374  
   375  				// Reactivate the parent coroutine. This adds it back to
   376  				// the run queue, so it is started again by the
   377  				// scheduler when possible (possibly right after the
   378  				// following suspend).
   379  				c.builder.SetInsertPointBefore(inst)
   380  
   381  				parentHandle := f.LastParam()
   382  				c.createRuntimeCall("activateTask", []llvm.Value{parentHandle}, "")
   383  
   384  				// Suspend this coroutine.
   385  				// It would look like this is unnecessary, but if this
   386  				// suspend point is left out, it leads to undefined
   387  				// behavior somehow (with the unreachable instruction).
   388  				continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
   389  					llvm.ConstNull(c.ctx.TokenType()),
   390  					llvm.ConstInt(c.ctx.Int1Type(), 1, false),
   391  				}, "ret")
   392  				sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
   393  				sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
   394  				sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
   395  				inst.EraseFromParentAsInstruction()
   396  			} else {
   397  				panic("todo: return value from coroutine")
   398  			}
   399  		}
   400  
   401  		// Coroutine cleanup. Free resources associated with this coroutine.
   402  		c.builder.SetInsertPointAtEnd(frame.cleanupBlock)
   403  		mem := c.builder.CreateCall(coroFreeFunc, []llvm.Value{id, frame.taskHandle}, "task.data.free")
   404  		c.createRuntimeCall("free", []llvm.Value{mem}, "")
   405  		c.builder.CreateBr(frame.suspendBlock)
   406  
   407  		// Coroutine suspend. A call to llvm.coro.suspend() will branch here.
   408  		c.builder.SetInsertPointAtEnd(frame.suspendBlock)
   409  		c.builder.CreateCall(coroEndFunc, []llvm.Value{frame.taskHandle, llvm.ConstInt(c.ctx.Int1Type(), 0, false)}, "unused")
   410  		returnType := f.Type().ElementType().ReturnType()
   411  		if returnType.TypeKind() == llvm.VoidTypeKind {
   412  			c.builder.CreateRetVoid()
   413  		} else {
   414  			c.builder.CreateRet(llvm.Undef(returnType))
   415  		}
   416  
   417  		// Coroutine exit. All final suspends (return instructions) will branch
   418  		// here.
   419  		c.builder.SetInsertPointAtEnd(frame.unreachableBlock)
   420  		c.builder.CreateUnreachable()
   421  	}
   422  
   423  	// Transform calls to time.Sleep() into coroutine suspend points.
   424  	for _, sleepCall := range getUses(sleep) {
   425  		// sleepCall must be a call instruction.
   426  		frame := asyncFuncs[sleepCall.InstructionParent().Parent()]
   427  		duration := sleepCall.Operand(0)
   428  
   429  		// Set task state to TASK_STATE_SLEEP and set the duration.
   430  		c.builder.SetInsertPointBefore(sleepCall)
   431  		c.createRuntimeCall("sleepTask", []llvm.Value{frame.taskHandle, duration}, "")
   432  
   433  		// Yield to scheduler.
   434  		continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
   435  			llvm.ConstNull(c.ctx.TokenType()),
   436  			llvm.ConstInt(c.ctx.Int1Type(), 0, false),
   437  		}, "")
   438  		wakeup := c.splitBasicBlock(sleepCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup")
   439  		c.builder.SetInsertPointBefore(sleepCall)
   440  		sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
   441  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
   442  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
   443  		sleepCall.EraseFromParentAsInstruction()
   444  	}
   445  
   446  	// Transform calls to runtime.deadlockStub into coroutine suspends (without
   447  	// resume).
   448  	for _, deadlockCall := range getUses(deadlockStub) {
   449  		// deadlockCall must be a call instruction.
   450  		frame := asyncFuncs[deadlockCall.InstructionParent().Parent()]
   451  
   452  		// Exit coroutine.
   453  		c.builder.SetInsertPointBefore(deadlockCall)
   454  		continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
   455  			llvm.ConstNull(c.ctx.TokenType()),
   456  			llvm.ConstInt(c.ctx.Int1Type(), 1, false), // final suspend
   457  		}, "")
   458  		c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
   459  		c.builder.SetInsertPointBefore(deadlockCall)
   460  		sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
   461  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
   462  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
   463  		deadlockCall.EraseFromParentAsInstruction()
   464  	}
   465  
   466  	// Transform calls to runtime.chanSendStub into channel send operations.
   467  	for _, sendOp := range getUses(chanSendStub) {
   468  		// sendOp must be a call instruction.
   469  		frame := asyncFuncs[sendOp.InstructionParent().Parent()]
   470  
   471  		// Send the value over the channel, or block.
   472  		sendOp.SetOperand(0, frame.taskHandle)
   473  		sendOp.SetOperand(sendOp.OperandsCount()-1, c.mod.NamedFunction("runtime.chanSend"))
   474  
   475  		// Use taskState.data to store the value to send:
   476  		//     *(*valueType)(&coroutine.promise().data) = valueToSend
   477  		//     runtime.chanSend(coroutine, ch)
   478  		bitcast := sendOp.Operand(2)
   479  		valueAlloca := bitcast.Operand(0)
   480  		c.builder.SetInsertPointBefore(valueAlloca)
   481  		promiseType := c.mod.GetTypeByName("runtime.taskState")
   482  		promiseRaw := c.builder.CreateCall(coroPromiseFunc, []llvm.Value{
   483  			frame.taskHandle,
   484  			llvm.ConstInt(c.ctx.Int32Type(), uint64(c.targetData.PrefTypeAlignment(promiseType)), false),
   485  			llvm.ConstInt(c.ctx.Int1Type(), 0, false),
   486  		}, "task.promise.raw")
   487  		promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise")
   488  		dataPtr := c.builder.CreateGEP(promise, []llvm.Value{
   489  			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   490  			llvm.ConstInt(c.ctx.Int32Type(), 2, false),
   491  		}, "task.promise.data")
   492  		sendOp.SetOperand(2, llvm.Undef(c.i8ptrType))
   493  		valueAlloca.ReplaceAllUsesWith(c.builder.CreateBitCast(dataPtr, valueAlloca.Type(), ""))
   494  		bitcast.EraseFromParentAsInstruction()
   495  		valueAlloca.EraseFromParentAsInstruction()
   496  
   497  		// Yield to scheduler.
   498  		c.builder.SetInsertPointBefore(llvm.NextInstruction(sendOp))
   499  		continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
   500  			llvm.ConstNull(c.ctx.TokenType()),
   501  			llvm.ConstInt(c.ctx.Int1Type(), 0, false),
   502  		}, "")
   503  		sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
   504  		wakeup := c.splitBasicBlock(sw, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.sent")
   505  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
   506  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
   507  	}
   508  
   509  	// Transform calls to runtime.chanRecvStub into channel receive operations.
   510  	for _, recvOp := range getUses(chanRecvStub) {
   511  		// recvOp must be a call instruction.
   512  		frame := asyncFuncs[recvOp.InstructionParent().Parent()]
   513  
   514  		bitcast := recvOp.Operand(2)
   515  		commaOk := recvOp.Operand(3)
   516  		valueAlloca := bitcast.Operand(0)
   517  
   518  		// Receive the value over the channel, or block.
   519  		recvOp.SetOperand(0, frame.taskHandle)
   520  		recvOp.SetOperand(recvOp.OperandsCount()-1, c.mod.NamedFunction("runtime.chanRecv"))
   521  		recvOp.SetOperand(2, llvm.Undef(c.i8ptrType))
   522  		bitcast.EraseFromParentAsInstruction()
   523  
   524  		// Yield to scheduler.
   525  		c.builder.SetInsertPointBefore(llvm.NextInstruction(recvOp))
   526  		continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
   527  			llvm.ConstNull(c.ctx.TokenType()),
   528  			llvm.ConstInt(c.ctx.Int1Type(), 0, false),
   529  		}, "")
   530  		sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
   531  		wakeup := c.splitBasicBlock(sw, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.received")
   532  		c.builder.SetInsertPointAtEnd(recvOp.InstructionParent())
   533  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), wakeup)
   534  		sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 1, false), frame.cleanupBlock)
   535  
   536  		// The value to receive is stored in taskState.data:
   537  		//     runtime.chanRecv(coroutine, ch)
   538  		//     promise := coroutine.promise()
   539  		//     valueReceived := *(*valueType)(&promise.data)
   540  		//     ok := promise.commaOk
   541  		c.builder.SetInsertPointBefore(wakeup.FirstInstruction())
   542  		promiseType := c.mod.GetTypeByName("runtime.taskState")
   543  		promiseRaw := c.builder.CreateCall(coroPromiseFunc, []llvm.Value{
   544  			frame.taskHandle,
   545  			llvm.ConstInt(c.ctx.Int32Type(), uint64(c.targetData.PrefTypeAlignment(promiseType)), false),
   546  			llvm.ConstInt(c.ctx.Int1Type(), 0, false),
   547  		}, "task.promise.raw")
   548  		promise := c.builder.CreateBitCast(promiseRaw, llvm.PointerType(promiseType, 0), "task.promise")
   549  		dataPtr := c.builder.CreateGEP(promise, []llvm.Value{
   550  			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   551  			llvm.ConstInt(c.ctx.Int32Type(), 2, false),
   552  		}, "task.promise.data")
   553  		valueAlloca.ReplaceAllUsesWith(c.builder.CreateBitCast(dataPtr, valueAlloca.Type(), ""))
   554  		valueAlloca.EraseFromParentAsInstruction()
   555  		commaOkPtr := c.builder.CreateGEP(promise, []llvm.Value{
   556  			llvm.ConstInt(c.ctx.Int32Type(), 0, false),
   557  			llvm.ConstInt(c.ctx.Int32Type(), 1, false),
   558  		}, "task.promise.comma-ok")
   559  		commaOk.ReplaceAllUsesWith(commaOkPtr)
   560  		recvOp.SetOperand(3, llvm.Undef(commaOk.Type()))
   561  	}
   562  
   563  	return true, c.lowerMakeGoroutineCalls()
   564  }
   565  
   566  // Lower runtime.makeGoroutine calls to regular call instructions. This is done
   567  // after the regular goroutine transformations. The started goroutines are
   568  // either non-blocking (in which case they can be called directly) or blocking,
   569  // in which case they will ask the scheduler themselves to be rescheduled.
   570  func (c *Compiler) lowerMakeGoroutineCalls() error {
   571  	// The following Go code:
   572  	//   go startedGoroutine()
   573  	//
   574  	// Is translated to the following during IR construction, to preserve the
   575  	// fact that this function should be called as a new goroutine.
   576  	//   %0 = call i8* @runtime.makeGoroutine(i8* bitcast (void (i8*, i8*)* @main.startedGoroutine to i8*), i8* undef, i8* null)
   577  	//   %1 = bitcast i8* %0 to void (i8*, i8*)*
   578  	//   call void %1(i8* undef, i8* undef)
   579  	//
   580  	// This function rewrites it to a direct call:
   581  	//   call void @main.startedGoroutine(i8* undef, i8* null)
   582  
   583  	makeGoroutine := c.mod.NamedFunction("runtime.makeGoroutine")
   584  	for _, goroutine := range getUses(makeGoroutine) {
   585  		bitcastIn := goroutine.Operand(0)
   586  		origFunc := bitcastIn.Operand(0)
   587  		uses := getUses(goroutine)
   588  		if len(uses) != 1 || uses[0].IsABitCastInst().IsNil() {
   589  			return errors.New("expected exactly 1 bitcast use of runtime.makeGoroutine")
   590  		}
   591  		bitcastOut := uses[0]
   592  		uses = getUses(bitcastOut)
   593  		if len(uses) != 1 || uses[0].IsACallInst().IsNil() {
   594  			return errors.New("expected exactly 1 call use of runtime.makeGoroutine bitcast")
   595  		}
   596  		realCall := uses[0]
   597  
   598  		// Create call instruction.
   599  		var params []llvm.Value
   600  		for i := 0; i < realCall.OperandsCount()-1; i++ {
   601  			params = append(params, realCall.Operand(i))
   602  		}
   603  		params[len(params)-1] = llvm.ConstPointerNull(c.i8ptrType) // parent coroutine handle (must be nil)
   604  		c.builder.SetInsertPointBefore(realCall)
   605  		c.builder.CreateCall(origFunc, params, "")
   606  		realCall.EraseFromParentAsInstruction()
   607  		bitcastOut.EraseFromParentAsInstruction()
   608  		goroutine.EraseFromParentAsInstruction()
   609  	}
   610  
   611  	return nil
   612  }