diff --git a/compiler/calls.go b/compiler/calls.go index f5ded1f4..167e270a 100644 --- a/compiler/calls.go +++ b/compiler/calls.go @@ -21,6 +21,9 @@ func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name stri panic("trying to call runtime." + fnName) } fn := c.ir.GetFunction(member.(*ssa.Function)) + if !fn.IsExported() { + args = append(args, llvm.Undef(c.i8ptrType)) // unused context parameter + } return c.createCall(fn.LLVMFn, args, name) } diff --git a/compiler/compiler.go b/compiler/compiler.go index 818abe93..c3cf1ddf 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -218,12 +218,10 @@ func (c *Compiler) Compile(mainPath string) error { // Run some DCE and analysis passes. The results are later used by the // compiler. - c.ir.SimpleDCE() // remove most dead code - c.ir.AnalyseCallgraph() // set up callgraph - c.ir.AnalyseInterfaceConversions() // determine which types are converted to an interface - c.ir.AnalyseFunctionPointers() // determine which function pointer signatures need context - c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively) - c.ir.AnalyseGoCalls() // check whether we need a scheduler + c.ir.SimpleDCE() // remove most dead code + c.ir.AnalyseCallgraph() // set up callgraph + c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively) + c.ir.AnalyseGoCalls() // check whether we need a scheduler // Initialize debug information. c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{ @@ -368,7 +366,7 @@ func (c *Compiler) Compile(mainPath string) error { block := c.ctx.AddBasicBlock(initFn.LLVMFn, "entry") c.builder.SetInsertPointAtEnd(block) for _, fn := range c.initFuncs { - c.builder.CreateCall(fn, nil, "") + c.builder.CreateCall(fn, []llvm.Value{llvm.Undef(c.i8ptrType)}, "") } c.builder.CreateRetVoid() @@ -389,11 +387,10 @@ func (c *Compiler) Compile(mainPath string) error { c.builder.SetInsertPointAtEnd(block) realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main") if c.ir.NeedsScheduler() { - coroutine := c.builder.CreateCall(realMain, []llvm.Value{llvm.ConstPointerNull(c.i8ptrType)}, "") - scheduler := c.mod.NamedFunction("runtime.scheduler") - c.builder.CreateCall(scheduler, []llvm.Value{coroutine}, "") + coroutine := c.builder.CreateCall(realMain, []llvm.Value{llvm.ConstPointerNull(c.i8ptrType), llvm.Undef(c.i8ptrType)}, "") + c.createRuntimeCall("scheduler", []llvm.Value{coroutine}, "") } else { - c.builder.CreateCall(realMain, nil, "") + c.builder.CreateCall(realMain, []llvm.Value{llvm.Undef(c.i8ptrType)}, "") } c.builder.CreateRetVoid() @@ -515,17 +512,11 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) { } paramTypes = append(paramTypes, c.expandFormalParamType(subType)...) } - var ptr llvm.Type - if c.ir.SignatureNeedsContext(typ) { - // make a closure type (with a function pointer type inside): - // {context, funcptr} - paramTypes = append(paramTypes, c.i8ptrType) - ptr = llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0) - ptr = c.ctx.StructType([]llvm.Type{c.i8ptrType, ptr}, false) - } else { - // make a simple function pointer - ptr = llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0) - } + // make a closure type (with a function pointer type inside): + // {context, funcptr} + paramTypes = append(paramTypes, c.i8ptrType) + ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0) + ptr = c.ctx.StructType([]llvm.Type{c.i8ptrType, ptr}, false) return ptr, nil case *types.Slice: elemType, err := c.getLLVMType(typ.Elem()) @@ -706,9 +697,9 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) { paramTypes = append(paramTypes, paramTypeFragments...) } - if c.ir.FunctionNeedsContext(f) { - // This function gets an extra parameter: the context pointer (for - // closures and bound methods). Add it as an extra paramter here. + // Add an extra parameter as the function context. This context is used in + // closures and bound methods, but should be optimized away when not used. + if !f.IsExported() { paramTypes = append(paramTypes, c.i8ptrType) } @@ -840,10 +831,8 @@ func (c *Compiler) getInterpretedValue(prefix string, value ir.Value) (llvm.Valu } fn := c.ir.GetFunction(value.Elem) ptr := fn.LLVMFn - if c.ir.SignatureNeedsContext(fn.Signature) { - // Create closure value: {context, function pointer} - ptr = c.ctx.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false) - } + // Create closure value: {context, function pointer} + ptr = c.ctx.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false) return ptr, nil case *ir.GlobalValue: @@ -1128,9 +1117,6 @@ func (c *Compiler) parseFunc(frame *Frame) error { // Load free variables from the context. This is a closure (or bound // method). if len(frame.fn.FreeVars) != 0 { - if !c.ir.FunctionNeedsContext(frame.fn) { - panic("free variables on function without context") - } context := frame.fn.LLVMFn.LastParam() context.SetName("context") @@ -1799,24 +1785,20 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName()) } var context llvm.Value - if c.ir.FunctionNeedsContext(targetFunc) { - // This function call is to a (potential) closure, not a regular - // function. See whether it is a closure and if so, call it as such. - // Else, supply a dummy nil pointer as the last parameter. - var err error - if mkClosure, ok := instr.Value.(*ssa.MakeClosure); ok { - // closure is {context, function pointer} - closure, err := c.parseExpr(frame, mkClosure) - if err != nil { - return llvm.Value{}, err - } - context = c.builder.CreateExtractValue(closure, 0, "") - } else { - context, err = c.getZeroValue(c.i8ptrType) - if err != nil { - return llvm.Value{}, err - } + // This function call is to a (potential) closure, not a regular + // function. See whether it is a closure and if so, call it as such. + // Else, supply a dummy nil pointer as the last parameter. + if targetFunc.IsExported() { + // don't pass a context parameter + } else if mkClosure, ok := instr.Value.(*ssa.MakeClosure); ok { + // closure is {context, function pointer} + closure, err := c.parseExpr(frame, mkClosure) + if err != nil { + return llvm.Value{}, err } + context = c.builder.CreateExtractValue(closure, 0, "") + } else { + context = llvm.Undef(c.i8ptrType) } return c.parseFunctionCall(frame, instr.Args, targetFunc.LLVMFn, context, c.ir.IsBlocking(targetFunc), parentHandle) } @@ -1831,14 +1813,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l return llvm.Value{}, err } // TODO: blocking function pointers (needs analysis) - var context llvm.Value - if c.ir.SignatureNeedsContext(instr.Signature()) { - // 'value' is a closure, not a raw function pointer. - // Extract the function pointer and the context pointer. - // closure: {context, function pointer} - context = c.builder.CreateExtractValue(value, 0, "") - value = c.builder.CreateExtractValue(value, 1, "") - } + // 'value' is a closure, not a raw function pointer. + // Extract the function pointer and the context pointer. + // closure: {context, function pointer} + context := c.builder.CreateExtractValue(value, 0, "") + value = c.builder.CreateExtractValue(value, 1, "") return c.parseFunctionCall(frame, instr.Args, value, context, false, parentHandle) } } @@ -2016,16 +1995,12 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) { if fn.IsExported() { return llvm.Value{}, c.makeError(expr.Pos(), "cannot use an exported function as value") } - ptr := fn.LLVMFn - if c.ir.FunctionNeedsContext(fn) { - // Create closure for function pointer. - // Closure is: {context, function pointer} - ptr = c.ctx.ConstStruct([]llvm.Value{ - llvm.ConstPointerNull(c.i8ptrType), - ptr, - }, false) - } - return ptr, nil + // Create closure for function pointer. + // Closure is: {context, function pointer} + return c.ctx.ConstStruct([]llvm.Value{ + llvm.Undef(c.i8ptrType), + fn.LLVMFn, + }, false), nil case *ssa.Global: if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") { // Ignore CGo global variables which we don't use. @@ -2606,15 +2581,12 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p return llvm.Value{}, c.makeError(pos, "todo: unknown basic type in binop: "+typ.String()) } case *types.Signature: - if c.ir.SignatureNeedsContext(typ) { - // This is a closure, not a function pointer. Get the underlying - // function pointer. - // This is safe: function pointers are generally not comparable - // against each other, only against nil. So one or both has to be - // nil, so we can ignore the contents of the closure. - x = c.builder.CreateExtractValue(x, 1, "") - y = c.builder.CreateExtractValue(y, 1, "") - } + // Extract function pointers from the function values (closures). + // This is safe: function pointers are generally not comparable + // against each other, only against nil. So one or both has to be + // nil, so we can ignore the closure context. + x = c.builder.CreateExtractValue(x, 1, "") + y = c.builder.CreateExtractValue(y, 1, "") switch op { case token.EQL: // == return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil @@ -2790,7 +2762,7 @@ func (c *Compiler) parseConst(prefix string, expr *ssa.Const) (llvm.Value, error } // Create a generic nil interface with no dynamic type (typecode=0). fields := []llvm.Value{ - llvm.ConstInt(c.ctx.Int16Type(), 0, false), + llvm.ConstInt(c.uintptrType, 0, false), llvm.ConstPointerNull(c.i8ptrType), } itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields) @@ -2979,10 +2951,6 @@ func (c *Compiler) parseMakeClosure(frame *Frame, expr *ssa.MakeClosure) (llvm.V panic("unexpected: MakeClosure without bound variables") } f := c.ir.GetFunction(expr.Fn.(*ssa.Function)) - if !c.ir.FunctionNeedsContext(f) { - // Maybe AnalyseFunctionPointers didn't run? - panic("MakeClosure on function signature without context") - } // Collect all bound variables. boundVars := make([]llvm.Value, 0, len(expr.Bindings)) diff --git a/compiler/defer.go b/compiler/defer.go index db8f5769..02f545a1 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -14,8 +14,6 @@ package compiler // frames. import ( - "go/types" - "github.com/aykevl/go-llvm" "github.com/aykevl/tinygo/ir" "golang.org/x/tools/go/ssa" @@ -240,12 +238,10 @@ func (c *Compiler) emitRunDefers(frame *Frame) error { forwardParams = append(forwardParams, forwardParam) } - if c.ir.SignatureNeedsContext(callback.Method.Type().(*types.Signature)) { - // This function takes an extra context parameter. An interface call - // cannot also be a closure but we have to supply the parameter - // anyway for platforms with a strict calling convention. - forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) - } + // Add the context parameter. An interface call cannot also be a + // closure but we have to supply the parameter anyway for platforms + // with a strict calling convention. + forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) fnPtr, _, err := c.getInvokeCall(frame, callback) if err != nil { @@ -277,6 +273,10 @@ func (c *Compiler) emitRunDefers(frame *Frame) error { forwardParams = append(forwardParams, forwardParam) } + // Add the context parameter. We know it is ignored by the receiving + // function, but we have to pass one anyway. + forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType)) + // Call real function. c.createCall(callback.LLVMFn, forwardParams, "") diff --git a/compiler/interface.go b/compiler/interface.go index f6c0374d..a5e47a59 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -304,16 +304,10 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu if err != nil { return llvm.Value{}, nil, err } - if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { - // This is somewhat of a hack. - // getLLVMType() has created a closure type for us, but we don't - // actually want a closure type as an interface call can never be a - // closure call. So extract the function pointer type from the - // closure. - // This happens because somewhere the same function signature is - // used in a closure or bound method. - llvmFnType = llvmFnType.Subtypes()[1] - } + // getLLVMType() has created a closure type for us, but we don't actually + // want a closure type as an interface call can never be a closure call. So + // extract the function pointer type from the closure. + llvmFnType = llvmFnType.Subtypes()[1] typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode") values := []llvm.Value{ @@ -333,12 +327,9 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu } args = append(args, val) } - if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { - // This function takes an extra context parameter. An interface call - // cannot also be a closure but we have to supply the nil pointer - // anyway. - args = append(args, llvm.ConstPointerNull(c.i8ptrType)) - } + // Add the context parameter. An interface call never takes a context but we + // have to supply the parameter anyway. + args = append(args, llvm.Undef(c.i8ptrType)) return fnCast, args, nil } diff --git a/docs/internals.rst b/docs/internals.rst index a1b3016c..5cf61d61 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -77,14 +77,11 @@ interface interface.go for a detailed description of how typeasserts and interface calls are implemented. -function pointer - A function pointer has two representations: a literal function pointer and a - fat function pointer in the form of ``{context, function pointer}``. Which - representation is chosen depends on the AnalyseFunctionPointers pass in - `ir/passes.go `_: - it tries to use a raw function pointer but will use a fat function pointer - if there is a closure or bound method somewhere in the program with the - exact same signature. +function value + A function value is a fat function pointer in the form of ``{context, + function pointer}`` where context is a pointer which may have any value. + The function pointer is expected to be called with the context as the last + parameter in all cases. goroutine A goroutine is a linked list of `LLVM coroutines diff --git a/interp/interp.go b/interp/interp.go index 52b5c0e8..c0f018c5 100644 --- a/interp/interp.go +++ b/interp/interp.go @@ -56,13 +56,14 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error { } // Do this in a separate step to avoid corrupting the iterator above. + undefPtr := llvm.Undef(llvm.PointerType(mod.Context().Int8Type(), 0)) for _, call := range initCalls { initName := call.CalledValue().Name() if !strings.HasSuffix(initName, ".init") { return errors.New("expected all instructions in " + name + " to be *.init() calls") } pkgName := initName[:len(initName)-5] - _, err := e.Function(call.CalledValue(), nil, pkgName) + _, err := e.Function(call.CalledValue(), []Value{&LocalValue{e, undefPtr}}, pkgName) if err == ErrUnreachable { break } diff --git a/ir/ir.go b/ir/ir.go index 5da2e480..d0028610 100644 --- a/ir/ir.go +++ b/ir/ir.go @@ -19,33 +19,30 @@ import ( // View on all functions, types, and globals in a program, with analysis // results. type Program struct { - Program *ssa.Program - mainPkg *ssa.Package - Functions []*Function - functionMap map[*ssa.Function]*Function - Globals []*Global - globalMap map[*ssa.Global]*Global - comments map[string]*ast.CommentGroup - NamedTypes []*NamedType - needsScheduler bool - goCalls []*ssa.Go - typesInInterfaces map[string]struct{} // see AnalyseInterfaceConversions - fpWithContext map[string]struct{} // see AnalyseFunctionPointers + Program *ssa.Program + mainPkg *ssa.Package + Functions []*Function + functionMap map[*ssa.Function]*Function + Globals []*Global + globalMap map[*ssa.Global]*Global + comments map[string]*ast.CommentGroup + NamedTypes []*NamedType + needsScheduler bool + goCalls []*ssa.Go } // Function or method. type Function struct { *ssa.Function - LLVMFn llvm.Value - linkName string // go:linkname, go:export, go:interrupt - exported bool // go:export - nobounds bool // go:nobounds - blocking bool // calculated by AnalyseBlockingRecursive - flag bool // used by dead code elimination - interrupt bool // go:interrupt - addressTaken bool // used as function pointer, calculated by AnalyseFunctionPointers - parents []*Function // calculated by AnalyseCallgraph - children []*Function // calculated by AnalyseCallgraph + LLVMFn llvm.Value + linkName string // go:linkname, go:export, go:interrupt + exported bool // go:export + nobounds bool // go:nobounds + blocking bool // calculated by AnalyseBlockingRecursive + flag bool // used by dead code elimination + interrupt bool // go:interrupt + parents []*Function // calculated by AnalyseCallgraph + children []*Function // calculated by AnalyseCallgraph } // Global variable, possibly constant. diff --git a/ir/passes.go b/ir/passes.go index e2dbbb88..792700b7 100644 --- a/ir/passes.go +++ b/ir/passes.go @@ -17,16 +17,15 @@ import ( // String() string // Read([]byte) (int, error) func MethodSignature(method *types.Func) string { - return method.Name() + Signature(method.Type().(*types.Signature)) + return method.Name() + signature(method.Type().(*types.Signature)) } -// Make a readable version of a function (pointer) signature. This string is -// used internally to match signatures (like in AnalyseFunctionPointers). +// Make a readable version of a function (pointer) signature. // Examples: // // () string // (string, int) (int, error) -func Signature(sig *types.Signature) string { +func signature(sig *types.Signature) string { s := "" if sig.Params().Len() == 0 { s += "()" @@ -101,69 +100,6 @@ func (p *Program) AnalyseCallgraph() { } } -// Find all types that are put in an interface. -func (p *Program) AnalyseInterfaceConversions() { - // Clear, if AnalyseInterfaceConversions has been called before. - p.typesInInterfaces = map[string]struct{}{} - - for _, f := range p.Functions { - for _, block := range f.Blocks { - for _, instr := range block.Instrs { - switch instr := instr.(type) { - case *ssa.MakeInterface: - name := instr.X.Type().String() - if _, ok := p.typesInInterfaces[name]; !ok { - p.typesInInterfaces[name] = struct{}{} - } - } - } - } - } -} - -// Analyse which function pointer signatures need a context parameter. -// This makes calling function pointers more efficient. -func (p *Program) AnalyseFunctionPointers() { - // Clear, if AnalyseFunctionPointers has been called before. - p.fpWithContext = map[string]struct{}{} - - for _, f := range p.Functions { - for _, block := range f.Blocks { - for _, instr := range block.Instrs { - switch instr := instr.(type) { - case ssa.CallInstruction: - for _, arg := range instr.Common().Args { - switch arg := arg.(type) { - case *ssa.Function: - f := p.GetFunction(arg) - f.addressTaken = true - } - } - case *ssa.DebugRef: - default: - // For anything that isn't a call... - for _, operand := range instr.Operands(nil) { - if operand == nil || *operand == nil || isCGoInternal((*operand).Name()) { - continue - } - switch operand := (*operand).(type) { - case *ssa.Function: - f := p.GetFunction(operand) - f.addressTaken = true - } - } - } - switch instr := instr.(type) { - case *ssa.MakeClosure: - fn := instr.Fn.(*ssa.Function) - sig := Signature(fn.Signature) - p.fpWithContext[sig] = struct{}{} - } - } - } - } -} - // Analyse which functions are recursively blocking. // // Depends on AnalyseCallgraph. @@ -321,21 +257,3 @@ func (p *Program) IsBlocking(f *Function) bool { } return f.blocking } - -func (p *Program) FunctionNeedsContext(f *Function) bool { - if !f.addressTaken { - if f.Signature.Recv() != nil { - _, hasInterfaceConversion := p.typesInInterfaces[f.Signature.Recv().Type().String()] - if hasInterfaceConversion && p.SignatureNeedsContext(f.Signature) { - return true - } - } - return false - } - return p.SignatureNeedsContext(f.Signature) -} - -func (p *Program) SignatureNeedsContext(sig *types.Signature) bool { - _, needsContext := p.fpWithContext[Signature(sig)] - return needsContext -} diff --git a/src/runtime/panic.go b/src/runtime/panic.go index a71fe141..e00ec2fe 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -2,7 +2,7 @@ package runtime // trap is a compiler hint that this function cannot be executed. It is // translated into either a trap instruction or a call to abort(). -//go:linkname trap llvm.trap +//go:export llvm.trap func trap() // Builtin function panic(msg), used as a compiler intrinsic. diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index aa9c15ac..1d49b648 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -6,16 +6,25 @@ import ( "unsafe" ) -func _Cfunc_putchar(c int) int -func _Cfunc_usleep(usec uint) int -func _Cfunc_malloc(size uintptr) unsafe.Pointer -func _Cfunc_abort() -func _Cfunc_clock_gettime(clk_id uint, ts *timespec) +//go:export putchar +func _putchar(c int) int + +//go:export usleep +func usleep(usec uint) int + +//go:export malloc +func malloc(size uintptr) unsafe.Pointer + +//go:export abort +func abort() + +//go:export clock_gettime +func clock_gettime(clk_id uint, ts *timespec) const heapSize = 1 * 1024 * 1024 // 1MB to start var ( - heapStart = uintptr(_Cfunc_malloc(heapSize)) + heapStart = uintptr(malloc(heapSize)) heapEnd = heapStart + heapSize ) @@ -45,11 +54,11 @@ func main() int { } func putchar(c byte) { - _Cfunc_putchar(int(c)) + _putchar(int(c)) } func sleepTicks(d timeUnit) { - _Cfunc_usleep(uint(d) / 1000) + usleep(uint(d) / 1000) } // Return monotonic time in nanoseconds. @@ -57,15 +66,10 @@ func sleepTicks(d timeUnit) { // TODO: noescape func monotime() uint64 { ts := timespec{} - _Cfunc_clock_gettime(CLOCK_MONOTONIC_RAW, &ts) + clock_gettime(CLOCK_MONOTONIC_RAW, &ts) return uint64(ts.tv_sec)*1000*1000*1000 + uint64(ts.tv_nsec) } func ticks() timeUnit { return timeUnit(monotime()) } - -func abort() { - // panic() exits with exit code 2. - _Cfunc_abort() -} diff --git a/src/runtime/runtime_wasm.go b/src/runtime/runtime_wasm.go index ffc8c4cf..8212d859 100644 --- a/src/runtime/runtime_wasm.go +++ b/src/runtime/runtime_wasm.go @@ -8,16 +8,16 @@ const tickMicros = 1 var timestamp timeUnit -// CommonWA: io_get_stdout -func _Cfunc_io_get_stdout() int32 +//go:export io_get_stdout +func io_get_stdout() int32 -// CommonWA: resource_write -func _Cfunc_resource_write(id int32, ptr *uint8, len int32) int32 +//go:export resource_write +func resource_write(id int32, ptr *uint8, len int32) int32 var stdout int32 func init() { - stdout = _Cfunc_io_get_stdout() + stdout = io_get_stdout() } //go:export _start @@ -32,7 +32,7 @@ func cwa_main() { } func putchar(c byte) { - _Cfunc_resource_write(stdout, &c, 1) + resource_write(stdout, &c, 1) } func sleepTicks(d timeUnit) { diff --git a/src/runtime/scheduler.go b/src/runtime/scheduler.go index f31a601b..d2c8b606 100644 --- a/src/runtime/scheduler.go +++ b/src/runtime/scheduler.go @@ -31,16 +31,16 @@ const schedulerDebug = false // must not be used directly, it is meant to be used as an opaque *i8 in LLVM. type coroutine uint8 -//go:linkname resume llvm.coro.resume +//go:export llvm.coro.resume func (t *coroutine) resume() -//go:linkname destroy llvm.coro.destroy +//go:export llvm.coro.destroy func (t *coroutine) destroy() -//go:linkname done llvm.coro.done +//go:export llvm.coro.done func (t *coroutine) done() bool -//go:linkname _promise llvm.coro.promise +//go:export llvm.coro.promise func (t *coroutine) _promise(alignment int32, from bool) unsafe.Pointer // Get the promise belonging to a task.