From 78bd7e094f2883ec322d7ef2db326ec3327a338b Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 18 Mar 2020 18:47:09 +0100 Subject: [PATCH] compiler: move funcImplementation to compileopts This allows packages other than the compiler to know (from a single source of truth) which implemenation is used for Go func values. This refactor is necessary to be able to move the Optimize function to the transform package. --- compileopts/config.go | 38 +++++++++++++++++++++++++++++++ compiler/func.go | 53 ++++++++----------------------------------- compiler/optimizer.go | 5 ++-- 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/compileopts/config.go b/compileopts/config.go index cbd0bd1d..d3f4bd1d 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -21,6 +21,29 @@ type Config struct { TestConfig TestConfig } +// FuncValueImplementation is an enum for the particular implementations of Go +// func values. +type FuncValueImplementation int + +// These constants describe the various possible implementations of Go func +// values. +const ( + FuncValueNone FuncValueImplementation = iota + + // A func value is implemented as a pair of pointers: + // {context, function pointer} + // where the context may be a pointer to a heap-allocated struct containing + // the free variables, or it may be undef if the function being pointed to + // doesn't need a context. The function pointer is a regular function + // pointer. + FuncValueDoubleword + + // As funcValueDoubleword, but with the function pointer replaced by a + // unique ID per function signature. Function values are called by using a + // switch statement and choosing which function to call. + FuncValueSwitch +) + // Triple returns the LLVM target triple, like armv6m-none-eabi. func (c *Config) Triple() string { return c.Target.Triple @@ -111,6 +134,21 @@ func (c *Config) Scheduler() string { return "coroutines" } +// FuncImplementation picks an appropriate func value implementation for the +// target. +func (c *Config) FuncImplementation() FuncValueImplementation { + // Always pick the switch implementation, as it allows the use of blocking + // inside a function that is used as a func value. + switch c.Scheduler() { + case "none", "coroutines": + return FuncValueSwitch + case "tasks": + return FuncValueDoubleword + default: + panic("unknown scheduler type") + } +} + // PanicStrategy returns the panic strategy selected for this target. Valid // values are "print" (print the panic value, then exit) or "trap" (issue a trap // instruction). diff --git a/compiler/func.go b/compiler/func.go index c7c9f9ff..b79b17f8 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -6,53 +6,20 @@ package compiler import ( "go/types" + "github.com/tinygo-org/tinygo/compileopts" "golang.org/x/tools/go/ssa" "tinygo.org/x/go-llvm" ) -type funcValueImplementation int - -const ( - funcValueNone funcValueImplementation = iota - - // A func value is implemented as a pair of pointers: - // {context, function pointer} - // where the context may be a pointer to a heap-allocated struct containing - // the free variables, or it may be undef if the function being pointed to - // doesn't need a context. The function pointer is a regular function - // pointer. - funcValueDoubleword - - // As funcValueDoubleword, but with the function pointer replaced by a - // unique ID per function signature. Function values are called by using a - // switch statement and choosing which function to call. - funcValueSwitch -) - -// funcImplementation picks an appropriate func value implementation for the -// target. -func (c *Compiler) funcImplementation() funcValueImplementation { - // Always pick the switch implementation, as it allows the use of blocking - // inside a function that is used as a func value. - switch c.Scheduler() { - case "none", "coroutines": - return funcValueSwitch - case "tasks": - return funcValueDoubleword - default: - panic("unknown scheduler type") - } -} - // createFuncValue creates a function value from a raw function pointer with no // context. func (c *Compiler) createFuncValue(funcPtr, context llvm.Value, sig *types.Signature) llvm.Value { var funcValueScalar llvm.Value - switch c.funcImplementation() { - case funcValueDoubleword: + switch c.FuncImplementation() { + case compileopts.FuncValueDoubleword: // Closure is: {context, function pointer} funcValueScalar = funcPtr - case funcValueSwitch: + case compileopts.FuncValueSwitch: sigGlobal := c.getTypeCode(sig) funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature" funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName) @@ -94,10 +61,10 @@ func (c *Compiler) extractFuncContext(funcValue llvm.Value) llvm.Value { // value. This may be an expensive operation. func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { context = c.builder.CreateExtractValue(funcValue, 0, "") - switch c.funcImplementation() { - case funcValueDoubleword: + switch c.FuncImplementation() { + case compileopts.FuncValueDoubleword: funcPtr = c.builder.CreateExtractValue(funcValue, 1, "") - case funcValueSwitch: + case compileopts.FuncValueSwitch: llvmSig := c.getRawFuncType(sig) sigGlobal := c.getTypeCode(sig) funcPtr = c.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") @@ -110,11 +77,11 @@ func (c *Compiler) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) ( // getFuncType returns the type of a func value given a signature. func (c *Compiler) getFuncType(typ *types.Signature) llvm.Type { - switch c.funcImplementation() { - case funcValueDoubleword: + switch c.FuncImplementation() { + case compileopts.FuncValueDoubleword: rawPtr := c.getRawFuncType(typ) return c.ctx.StructType([]llvm.Type{c.i8ptrType, rawPtr}, false) - case funcValueSwitch: + case compileopts.FuncValueSwitch: return c.getLLVMRuntimeType("funcValue") default: panic("unimplemented func value variant") diff --git a/compiler/optimizer.go b/compiler/optimizer.go index 35438b4e..4b08eb44 100644 --- a/compiler/optimizer.go +++ b/compiler/optimizer.go @@ -3,6 +3,7 @@ package compiler import ( "errors" + "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/compiler/ircheck" "github.com/tinygo-org/tinygo/transform" "tinygo.org/x/go-llvm" @@ -67,7 +68,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er return errs } - if c.funcImplementation() == funcValueSwitch { + if c.FuncImplementation() == compileopts.FuncValueSwitch { transform.LowerFuncValues(c.mod) } @@ -99,7 +100,7 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) []er } else { // Must be run at any optimization level. transform.LowerInterfaces(c.mod) - if c.funcImplementation() == funcValueSwitch { + if c.FuncImplementation() == compileopts.FuncValueSwitch { transform.LowerFuncValues(c.mod) } errs := transform.LowerInterrupts(c.mod)