createGo emits code to start a new goroutine.
(instr *ssa.Go)
| 14 | |
| 15 | // createGo emits code to start a new goroutine. |
| 16 | func (b *builder) createGo(instr *ssa.Go) { |
| 17 | if builtin, ok := instr.Call.Value.(*ssa.Builtin); ok { |
| 18 | // We cheat. None of the builtins do any long or blocking operation, so |
| 19 | // we might as well run these builtins right away without the program |
| 20 | // noticing the difference. |
| 21 | // Possible exceptions: |
| 22 | // - copy: this is a possibly long operation, but not a blocking |
| 23 | // operation. Semantically it makes no difference to run it right |
| 24 | // away (not in a goroutine). However, in practice it makes no sense |
| 25 | // to run copy in a goroutine as there is no way to (safely) know |
| 26 | // when it is finished. |
| 27 | // - panic: the error message would appear in the parent goroutine. |
| 28 | // But because `go panic("err")` would halt the program anyway |
| 29 | // (there is no recover), panicking right away would give the same |
| 30 | // behavior as creating a goroutine, switching the scheduler to that |
| 31 | // goroutine, and panicking there. So this optimization seems |
| 32 | // correct. |
| 33 | // - recover: because it runs in a new goroutine, it is never a |
| 34 | // deferred function. Thus this is a no-op. |
| 35 | if builtin.Name() == "recover" { |
| 36 | // This is a no-op, even in a deferred function: |
| 37 | // go recover() |
| 38 | return |
| 39 | } |
| 40 | var argTypes []types.Type |
| 41 | var argValues []llvm.Value |
| 42 | for _, arg := range instr.Call.Args { |
| 43 | argTypes = append(argTypes, arg.Type()) |
| 44 | argValues = append(argValues, b.getValue(arg, getPos(instr))) |
| 45 | } |
| 46 | b.createBuiltin(argTypes, argValues, builtin.Name(), instr.Pos()) |
| 47 | return |
| 48 | } |
| 49 | |
| 50 | // Get all function parameters to pass to the goroutine. |
| 51 | var params []llvm.Value |
| 52 | for _, param := range instr.Call.Args { |
| 53 | params = append(params, b.expandFormalParam(b.getValue(param, getPos(instr)))...) |
| 54 | } |
| 55 | |
| 56 | var prefix string |
| 57 | var funcPtr llvm.Value |
| 58 | var funcType llvm.Type |
| 59 | hasContext := false |
| 60 | if callee := instr.Call.StaticCallee(); callee != nil { |
| 61 | // Static callee is known. This makes it easier to start a new |
| 62 | // goroutine. |
| 63 | var context llvm.Value |
| 64 | switch value := instr.Call.Value.(type) { |
| 65 | case *ssa.Function: |
| 66 | // Goroutine call is regular function call. No context is necessary. |
| 67 | case *ssa.MakeClosure: |
| 68 | // A goroutine call on a func value, but the callee is trivial to find. For |
| 69 | // example: immediately applied functions. |
| 70 | funcValue := b.getValue(value, getPos(instr)) |
| 71 | context = b.extractFuncContext(funcValue) |
| 72 | default: |
| 73 | panic("StaticCallee returned an unexpected value") |
no test coverage detected