compiler: always use fat function pointers with context

This reduces complexity in the compiler without affecting binary sizes
too much.

Cortex-M0:   no changes
Linux x64:   no changes
WebAssembly: some testcases (calls, coroutines, map) are slightly bigger
Этот коммит содержится в:
Ayke van Laethem 2018-12-09 18:10:04 +01:00
родитель 3fec22e819
коммит 564b1b3312
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
12 изменённых файлов: 125 добавлений и 246 удалений

Просмотреть файл

@ -21,6 +21,9 @@ func (c *Compiler) createRuntimeCall(fnName string, args []llvm.Value, name stri
panic("trying to call runtime." + fnName) panic("trying to call runtime." + fnName)
} }
fn := c.ir.GetFunction(member.(*ssa.Function)) 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) return c.createCall(fn.LLVMFn, args, name)
} }

Просмотреть файл

@ -218,12 +218,10 @@ func (c *Compiler) Compile(mainPath string) error {
// Run some DCE and analysis passes. The results are later used by the // Run some DCE and analysis passes. The results are later used by the
// compiler. // compiler.
c.ir.SimpleDCE() // remove most dead code c.ir.SimpleDCE() // remove most dead code
c.ir.AnalyseCallgraph() // set up callgraph c.ir.AnalyseCallgraph() // set up callgraph
c.ir.AnalyseInterfaceConversions() // determine which types are converted to an interface c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)
c.ir.AnalyseFunctionPointers() // determine which function pointer signatures need context c.ir.AnalyseGoCalls() // check whether we need a scheduler
c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)
c.ir.AnalyseGoCalls() // check whether we need a scheduler
// Initialize debug information. // Initialize debug information.
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{ 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") block := c.ctx.AddBasicBlock(initFn.LLVMFn, "entry")
c.builder.SetInsertPointAtEnd(block) c.builder.SetInsertPointAtEnd(block)
for _, fn := range c.initFuncs { for _, fn := range c.initFuncs {
c.builder.CreateCall(fn, nil, "") c.builder.CreateCall(fn, []llvm.Value{llvm.Undef(c.i8ptrType)}, "")
} }
c.builder.CreateRetVoid() c.builder.CreateRetVoid()
@ -389,11 +387,10 @@ func (c *Compiler) Compile(mainPath string) error {
c.builder.SetInsertPointAtEnd(block) c.builder.SetInsertPointAtEnd(block)
realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main") realMain := c.mod.NamedFunction(c.ir.MainPkg().Pkg.Path() + ".main")
if c.ir.NeedsScheduler() { if c.ir.NeedsScheduler() {
coroutine := c.builder.CreateCall(realMain, []llvm.Value{llvm.ConstPointerNull(c.i8ptrType)}, "") coroutine := c.builder.CreateCall(realMain, []llvm.Value{llvm.ConstPointerNull(c.i8ptrType), llvm.Undef(c.i8ptrType)}, "")
scheduler := c.mod.NamedFunction("runtime.scheduler") c.createRuntimeCall("scheduler", []llvm.Value{coroutine}, "")
c.builder.CreateCall(scheduler, []llvm.Value{coroutine}, "")
} else { } else {
c.builder.CreateCall(realMain, nil, "") c.builder.CreateCall(realMain, []llvm.Value{llvm.Undef(c.i8ptrType)}, "")
} }
c.builder.CreateRetVoid() c.builder.CreateRetVoid()
@ -515,17 +512,11 @@ func (c *Compiler) getLLVMType(goType types.Type) (llvm.Type, error) {
} }
paramTypes = append(paramTypes, c.expandFormalParamType(subType)...) paramTypes = append(paramTypes, c.expandFormalParamType(subType)...)
} }
var ptr llvm.Type // make a closure type (with a function pointer type inside):
if c.ir.SignatureNeedsContext(typ) { // {context, funcptr}
// make a closure type (with a function pointer type inside): paramTypes = append(paramTypes, c.i8ptrType)
// {context, funcptr} ptr := llvm.PointerType(llvm.FunctionType(returnType, paramTypes, false), 0)
paramTypes = append(paramTypes, c.i8ptrType) ptr = c.ctx.StructType([]llvm.Type{c.i8ptrType, ptr}, false)
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)
}
return ptr, nil return ptr, nil
case *types.Slice: case *types.Slice:
elemType, err := c.getLLVMType(typ.Elem()) elemType, err := c.getLLVMType(typ.Elem())
@ -706,9 +697,9 @@ func (c *Compiler) parseFuncDecl(f *ir.Function) (*Frame, error) {
paramTypes = append(paramTypes, paramTypeFragments...) paramTypes = append(paramTypes, paramTypeFragments...)
} }
if c.ir.FunctionNeedsContext(f) { // Add an extra parameter as the function context. This context is used in
// This function gets an extra parameter: the context pointer (for // closures and bound methods, but should be optimized away when not used.
// closures and bound methods). Add it as an extra paramter here. if !f.IsExported() {
paramTypes = append(paramTypes, c.i8ptrType) 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) fn := c.ir.GetFunction(value.Elem)
ptr := fn.LLVMFn ptr := fn.LLVMFn
if c.ir.SignatureNeedsContext(fn.Signature) { // Create closure value: {context, function pointer}
// Create closure value: {context, function pointer} ptr = c.ctx.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false)
ptr = c.ctx.ConstStruct([]llvm.Value{llvm.ConstPointerNull(c.i8ptrType), ptr}, false)
}
return ptr, nil return ptr, nil
case *ir.GlobalValue: 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 // Load free variables from the context. This is a closure (or bound
// method). // method).
if len(frame.fn.FreeVars) != 0 { 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 := frame.fn.LLVMFn.LastParam()
context.SetName("context") 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()) return llvm.Value{}, c.makeError(instr.Pos(), "undefined function: "+targetFunc.LinkName())
} }
var context llvm.Value var context llvm.Value
if c.ir.FunctionNeedsContext(targetFunc) { // This function call is to a (potential) closure, not a regular
// 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.
// function. See whether it is a closure and if so, call it as such. // Else, supply a dummy nil pointer as the last parameter.
// Else, supply a dummy nil pointer as the last parameter. if targetFunc.IsExported() {
var err error // don't pass a context parameter
if mkClosure, ok := instr.Value.(*ssa.MakeClosure); ok { } else if mkClosure, ok := instr.Value.(*ssa.MakeClosure); ok {
// closure is {context, function pointer} // closure is {context, function pointer}
closure, err := c.parseExpr(frame, mkClosure) closure, err := c.parseExpr(frame, mkClosure)
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
}
context = c.builder.CreateExtractValue(closure, 0, "")
} else {
context, err = c.getZeroValue(c.i8ptrType)
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) 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 return llvm.Value{}, err
} }
// TODO: blocking function pointers (needs analysis) // TODO: blocking function pointers (needs analysis)
var context llvm.Value // 'value' is a closure, not a raw function pointer.
if c.ir.SignatureNeedsContext(instr.Signature()) { // Extract the function pointer and the context pointer.
// 'value' is a closure, not a raw function pointer. // closure: {context, function pointer}
// Extract the function pointer and the context pointer. context := c.builder.CreateExtractValue(value, 0, "")
// closure: {context, function pointer} value = c.builder.CreateExtractValue(value, 1, "")
context = c.builder.CreateExtractValue(value, 0, "")
value = c.builder.CreateExtractValue(value, 1, "")
}
return c.parseFunctionCall(frame, instr.Args, value, context, false, parentHandle) 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() { if fn.IsExported() {
return llvm.Value{}, c.makeError(expr.Pos(), "cannot use an exported function as value") return llvm.Value{}, c.makeError(expr.Pos(), "cannot use an exported function as value")
} }
ptr := fn.LLVMFn // Create closure for function pointer.
if c.ir.FunctionNeedsContext(fn) { // Closure is: {context, function pointer}
// Create closure for function pointer. return c.ctx.ConstStruct([]llvm.Value{
// Closure is: {context, function pointer} llvm.Undef(c.i8ptrType),
ptr = c.ctx.ConstStruct([]llvm.Value{ fn.LLVMFn,
llvm.ConstPointerNull(c.i8ptrType), }, false), nil
ptr,
}, false)
}
return ptr, nil
case *ssa.Global: case *ssa.Global:
if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") { if strings.HasPrefix(expr.Name(), "__cgofn__cgo_") || strings.HasPrefix(expr.Name(), "_cgo_") {
// Ignore CGo global variables which we don't use. // 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()) return llvm.Value{}, c.makeError(pos, "todo: unknown basic type in binop: "+typ.String())
} }
case *types.Signature: case *types.Signature:
if c.ir.SignatureNeedsContext(typ) { // Extract function pointers from the function values (closures).
// This is a closure, not a function pointer. Get the underlying // This is safe: function pointers are generally not comparable
// function pointer. // against each other, only against nil. So one or both has to be
// This is safe: function pointers are generally not comparable // nil, so we can ignore the closure context.
// against each other, only against nil. So one or both has to be x = c.builder.CreateExtractValue(x, 1, "")
// nil, so we can ignore the contents of the closure. y = c.builder.CreateExtractValue(y, 1, "")
x = c.builder.CreateExtractValue(x, 1, "")
y = c.builder.CreateExtractValue(y, 1, "")
}
switch op { switch op {
case token.EQL: // == case token.EQL: // ==
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil 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). // Create a generic nil interface with no dynamic type (typecode=0).
fields := []llvm.Value{ fields := []llvm.Value{
llvm.ConstInt(c.ctx.Int16Type(), 0, false), llvm.ConstInt(c.uintptrType, 0, false),
llvm.ConstPointerNull(c.i8ptrType), llvm.ConstPointerNull(c.i8ptrType),
} }
itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime._interface"), fields) 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") panic("unexpected: MakeClosure without bound variables")
} }
f := c.ir.GetFunction(expr.Fn.(*ssa.Function)) 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. // Collect all bound variables.
boundVars := make([]llvm.Value, 0, len(expr.Bindings)) boundVars := make([]llvm.Value, 0, len(expr.Bindings))

Просмотреть файл

@ -14,8 +14,6 @@ package compiler
// frames. // frames.
import ( import (
"go/types"
"github.com/aykevl/go-llvm" "github.com/aykevl/go-llvm"
"github.com/aykevl/tinygo/ir" "github.com/aykevl/tinygo/ir"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
@ -240,12 +238,10 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
forwardParams = append(forwardParams, forwardParam) forwardParams = append(forwardParams, forwardParam)
} }
if c.ir.SignatureNeedsContext(callback.Method.Type().(*types.Signature)) { // Add the context parameter. An interface call cannot also be a
// This function takes an extra context parameter. An interface call // closure but we have to supply the parameter anyway for platforms
// cannot also be a closure but we have to supply the parameter // with a strict calling convention.
// anyway for platforms with a strict calling convention. forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType))
forwardParams = append(forwardParams, llvm.Undef(c.i8ptrType))
}
fnPtr, _, err := c.getInvokeCall(frame, callback) fnPtr, _, err := c.getInvokeCall(frame, callback)
if err != nil { if err != nil {
@ -277,6 +273,10 @@ func (c *Compiler) emitRunDefers(frame *Frame) error {
forwardParams = append(forwardParams, forwardParam) 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. // Call real function.
c.createCall(callback.LLVMFn, forwardParams, "") c.createCall(callback.LLVMFn, forwardParams, "")

Просмотреть файл

@ -304,16 +304,10 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
if err != nil { if err != nil {
return llvm.Value{}, nil, err return llvm.Value{}, nil, err
} }
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { // getLLVMType() has created a closure type for us, but we don't actually
// This is somewhat of a hack. // want a closure type as an interface call can never be a closure call. So
// getLLVMType() has created a closure type for us, but we don't // extract the function pointer type from the closure.
// actually want a closure type as an interface call can never be a llvmFnType = llvmFnType.Subtypes()[1]
// 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]
}
typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode") typecode := c.builder.CreateExtractValue(itf, 0, "invoke.typecode")
values := []llvm.Value{ values := []llvm.Value{
@ -333,12 +327,9 @@ func (c *Compiler) getInvokeCall(frame *Frame, instr *ssa.CallCommon) (llvm.Valu
} }
args = append(args, val) args = append(args, val)
} }
if c.ir.SignatureNeedsContext(instr.Method.Type().(*types.Signature)) { // Add the context parameter. An interface call never takes a context but we
// This function takes an extra context parameter. An interface call // have to supply the parameter anyway.
// cannot also be a closure but we have to supply the nil pointer args = append(args, llvm.Undef(c.i8ptrType))
// anyway.
args = append(args, llvm.ConstPointerNull(c.i8ptrType))
}
return fnCast, args, nil return fnCast, args, nil
} }

Просмотреть файл

@ -77,14 +77,11 @@ interface
interface.go for a detailed description of how typeasserts and interface interface.go for a detailed description of how typeasserts and interface
calls are implemented. calls are implemented.
function pointer function value
A function pointer has two representations: a literal function pointer and a A function value is a fat function pointer in the form of ``{context,
fat function pointer in the form of ``{context, function pointer}``. Which function pointer}`` where context is a pointer which may have any value.
representation is chosen depends on the AnalyseFunctionPointers pass in The function pointer is expected to be called with the context as the last
`ir/passes.go <https://github.com/aykevl/tinygo/blob/master/ir/passes.go>`_: parameter in all cases.
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.
goroutine goroutine
A goroutine is a linked list of `LLVM coroutines A goroutine is a linked list of `LLVM coroutines

Просмотреть файл

@ -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. // 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 { for _, call := range initCalls {
initName := call.CalledValue().Name() initName := call.CalledValue().Name()
if !strings.HasSuffix(initName, ".init") { if !strings.HasSuffix(initName, ".init") {
return errors.New("expected all instructions in " + name + " to be *.init() calls") return errors.New("expected all instructions in " + name + " to be *.init() calls")
} }
pkgName := initName[:len(initName)-5] 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 { if err == ErrUnreachable {
break break
} }

Просмотреть файл

@ -19,33 +19,30 @@ import (
// View on all functions, types, and globals in a program, with analysis // View on all functions, types, and globals in a program, with analysis
// results. // results.
type Program struct { type Program struct {
Program *ssa.Program Program *ssa.Program
mainPkg *ssa.Package mainPkg *ssa.Package
Functions []*Function Functions []*Function
functionMap map[*ssa.Function]*Function functionMap map[*ssa.Function]*Function
Globals []*Global Globals []*Global
globalMap map[*ssa.Global]*Global globalMap map[*ssa.Global]*Global
comments map[string]*ast.CommentGroup comments map[string]*ast.CommentGroup
NamedTypes []*NamedType NamedTypes []*NamedType
needsScheduler bool needsScheduler bool
goCalls []*ssa.Go goCalls []*ssa.Go
typesInInterfaces map[string]struct{} // see AnalyseInterfaceConversions
fpWithContext map[string]struct{} // see AnalyseFunctionPointers
} }
// Function or method. // Function or method.
type Function struct { type Function struct {
*ssa.Function *ssa.Function
LLVMFn llvm.Value LLVMFn llvm.Value
linkName string // go:linkname, go:export, go:interrupt linkName string // go:linkname, go:export, go:interrupt
exported bool // go:export exported bool // go:export
nobounds bool // go:nobounds nobounds bool // go:nobounds
blocking bool // calculated by AnalyseBlockingRecursive blocking bool // calculated by AnalyseBlockingRecursive
flag bool // used by dead code elimination flag bool // used by dead code elimination
interrupt bool // go:interrupt interrupt bool // go:interrupt
addressTaken bool // used as function pointer, calculated by AnalyseFunctionPointers parents []*Function // calculated by AnalyseCallgraph
parents []*Function // calculated by AnalyseCallgraph children []*Function // calculated by AnalyseCallgraph
children []*Function // calculated by AnalyseCallgraph
} }
// Global variable, possibly constant. // Global variable, possibly constant.

Просмотреть файл

@ -17,16 +17,15 @@ import (
// String() string // String() string
// Read([]byte) (int, error) // Read([]byte) (int, error)
func MethodSignature(method *types.Func) string { 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 // Make a readable version of a function (pointer) signature.
// used internally to match signatures (like in AnalyseFunctionPointers).
// Examples: // Examples:
// //
// () string // () string
// (string, int) (int, error) // (string, int) (int, error)
func Signature(sig *types.Signature) string { func signature(sig *types.Signature) string {
s := "" s := ""
if sig.Params().Len() == 0 { if sig.Params().Len() == 0 {
s += "()" 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. // Analyse which functions are recursively blocking.
// //
// Depends on AnalyseCallgraph. // Depends on AnalyseCallgraph.
@ -321,21 +257,3 @@ func (p *Program) IsBlocking(f *Function) bool {
} }
return f.blocking 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
}

Просмотреть файл

@ -2,7 +2,7 @@ package runtime
// trap is a compiler hint that this function cannot be executed. It is // trap is a compiler hint that this function cannot be executed. It is
// translated into either a trap instruction or a call to abort(). // translated into either a trap instruction or a call to abort().
//go:linkname trap llvm.trap //go:export llvm.trap
func trap() func trap()
// Builtin function panic(msg), used as a compiler intrinsic. // Builtin function panic(msg), used as a compiler intrinsic.

Просмотреть файл

@ -6,16 +6,25 @@ import (
"unsafe" "unsafe"
) )
func _Cfunc_putchar(c int) int //go:export putchar
func _Cfunc_usleep(usec uint) int func _putchar(c int) int
func _Cfunc_malloc(size uintptr) unsafe.Pointer
func _Cfunc_abort() //go:export usleep
func _Cfunc_clock_gettime(clk_id uint, ts *timespec) 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 const heapSize = 1 * 1024 * 1024 // 1MB to start
var ( var (
heapStart = uintptr(_Cfunc_malloc(heapSize)) heapStart = uintptr(malloc(heapSize))
heapEnd = heapStart + heapSize heapEnd = heapStart + heapSize
) )
@ -45,11 +54,11 @@ func main() int {
} }
func putchar(c byte) { func putchar(c byte) {
_Cfunc_putchar(int(c)) _putchar(int(c))
} }
func sleepTicks(d timeUnit) { func sleepTicks(d timeUnit) {
_Cfunc_usleep(uint(d) / 1000) usleep(uint(d) / 1000)
} }
// Return monotonic time in nanoseconds. // Return monotonic time in nanoseconds.
@ -57,15 +66,10 @@ func sleepTicks(d timeUnit) {
// TODO: noescape // TODO: noescape
func monotime() uint64 { func monotime() uint64 {
ts := timespec{} 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) return uint64(ts.tv_sec)*1000*1000*1000 + uint64(ts.tv_nsec)
} }
func ticks() timeUnit { func ticks() timeUnit {
return timeUnit(monotime()) return timeUnit(monotime())
} }
func abort() {
// panic() exits with exit code 2.
_Cfunc_abort()
}

Просмотреть файл

@ -8,16 +8,16 @@ const tickMicros = 1
var timestamp timeUnit var timestamp timeUnit
// CommonWA: io_get_stdout //go:export io_get_stdout
func _Cfunc_io_get_stdout() int32 func io_get_stdout() int32
// CommonWA: resource_write //go:export resource_write
func _Cfunc_resource_write(id int32, ptr *uint8, len int32) int32 func resource_write(id int32, ptr *uint8, len int32) int32
var stdout int32 var stdout int32
func init() { func init() {
stdout = _Cfunc_io_get_stdout() stdout = io_get_stdout()
} }
//go:export _start //go:export _start
@ -32,7 +32,7 @@ func cwa_main() {
} }
func putchar(c byte) { func putchar(c byte) {
_Cfunc_resource_write(stdout, &c, 1) resource_write(stdout, &c, 1)
} }
func sleepTicks(d timeUnit) { func sleepTicks(d timeUnit) {

Просмотреть файл

@ -31,16 +31,16 @@ const schedulerDebug = false
// must not be used directly, it is meant to be used as an opaque *i8 in LLVM. // must not be used directly, it is meant to be used as an opaque *i8 in LLVM.
type coroutine uint8 type coroutine uint8
//go:linkname resume llvm.coro.resume //go:export llvm.coro.resume
func (t *coroutine) resume() func (t *coroutine) resume()
//go:linkname destroy llvm.coro.destroy //go:export llvm.coro.destroy
func (t *coroutine) destroy() func (t *coroutine) destroy()
//go:linkname done llvm.coro.done //go:export llvm.coro.done
func (t *coroutine) done() bool 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 func (t *coroutine) _promise(alignment int32, from bool) unsafe.Pointer
// Get the promise belonging to a task. // Get the promise belonging to a task.