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
Этот коммит содержится в:
родитель
3fec22e819
коммит
564b1b3312
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
|
||||||
}
|
}
|
||||||
|
|
41
ir/ir.go
41
ir/ir.go
|
@ -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.
|
||||||
|
|
88
ir/passes.go
88
ir/passes.go
|
@ -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.
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче