aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--builder/build.go1
-rw-r--r--compileopts/config.go22
-rw-r--r--compiler/compiler.go1
-rw-r--r--compiler/compiler_test.go3
-rw-r--r--compiler/func.go63
-rw-r--r--compiler/testdata/func-none.ll55
-rw-r--r--compiler/testdata/func.ll47
-rw-r--r--compiler/testdata/gc.go5
-rw-r--r--transform/func-lowering.go285
-rw-r--r--transform/func-lowering_test.go12
-rw-r--r--transform/optimizer.go7
-rw-r--r--transform/testdata/func-lowering.ll71
-rw-r--r--transform/testdata/func-lowering.out.ll101
-rw-r--r--transform/transform_test.go1
14 files changed, 57 insertions, 617 deletions
diff --git a/builder/build.go b/builder/build.go
index f3b006af8..20eb3f16c 100644
--- a/builder/build.go
+++ b/builder/build.go
@@ -153,7 +153,6 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
SizeLevel: sizeLevel,
Scheduler: config.Scheduler(),
- FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(),
diff --git a/compileopts/config.go b/compileopts/config.go
index 8537a0a6f..b4bb5f85e 100644
--- a/compileopts/config.go
+++ b/compileopts/config.go
@@ -159,28 +159,6 @@ func (c *Config) OptLevels() (optLevel, sizeLevel int, inlinerThreshold uint) {
}
}
-// FuncImplementation picks an appropriate func value implementation for the
-// target.
-func (c *Config) FuncImplementation() string {
- switch c.Scheduler() {
- case "tasks", "asyncify":
- // 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.
- return "doubleword"
- case "none":
- // As "doubleword", 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.
- return "switch"
- 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/compiler.go b/compiler/compiler.go
index 86b203061..21d8074d6 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -47,7 +47,6 @@ type Config struct {
// Various compiler options that determine how code is generated.
Scheduler string
- FuncImplementation string
AutomaticStackSize bool
DefaultStackSize uint64
NeedsStackObjects bool
diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go
index c929231cd..f940e15da 100644
--- a/compiler/compiler_test.go
+++ b/compiler/compiler_test.go
@@ -55,7 +55,7 @@ func TestCompiler(t *testing.T) {
{"string.go", "", ""},
{"float.go", "", ""},
{"interface.go", "", ""},
- {"func.go", "", "none"},
+ {"func.go", "", ""},
{"pragma.go", "", ""},
{"goroutine.go", "wasm", "asyncify"},
{"goroutine.go", "cortex-m-qemu", "tasks"},
@@ -105,7 +105,6 @@ func TestCompiler(t *testing.T) {
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(),
- FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
DefaultStackSize: config.Target.DefaultStackSize,
NeedsStackObjects: config.NeedsStackObjects(),
diff --git a/compiler/func.go b/compiler/func.go
index fa940570d..efb4ac4eb 100644
--- a/compiler/func.go
+++ b/compiler/func.go
@@ -19,29 +19,8 @@ func (b *builder) createFuncValue(funcPtr, context llvm.Value, sig *types.Signat
// createFuncValue creates a function value from a raw function pointer with no
// context.
func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context llvm.Value, sig *types.Signature) llvm.Value {
- var funcValueScalar llvm.Value
- switch c.FuncImplementation {
- case "doubleword":
- // Closure is: {context, function pointer}
- funcValueScalar = llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
- case "switch":
- funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature"
- funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName)
- if funcValueWithSignatureGlobal.IsNil() {
- funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature")
- funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{
- llvm.ConstPtrToInt(funcPtr, c.uintptrType),
- c.getFuncSignatureID(sig),
- })
- funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName)
- funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature)
- funcValueWithSignatureGlobal.SetGlobalConstant(true)
- funcValueWithSignatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage)
- }
- funcValueScalar = llvm.ConstPtrToInt(funcValueWithSignatureGlobal, c.uintptrType)
- default:
- panic("unimplemented func value variant")
- }
+ // Closure is: {context, function pointer}
+ funcValueScalar := llvm.ConstBitCast(funcPtr, c.rawVoidFuncType)
funcValueType := c.getFuncType(sig)
funcValue := llvm.Undef(funcValueType)
funcValue = builder.CreateInsertValue(funcValue, context, 0, "")
@@ -78,43 +57,19 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
// value. This may be an expensive operation.
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
context = b.CreateExtractValue(funcValue, 0, "")
- switch b.FuncImplementation {
- case "doubleword":
- bitcast := b.CreateExtractValue(funcValue, 1, "")
- if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
- funcPtr = bitcast.Operand(0)
- return
- }
- llvmSig := b.getRawFuncType(sig)
- funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
- case "switch":
- if !funcValue.IsAConstant().IsNil() {
- // If this is a constant func value, the underlying function is
- // known and can be returned directly.
- funcValueWithSignatureGlobal := llvm.ConstExtractValue(funcValue, []uint32{1}).Operand(0)
- funcPtr = llvm.ConstExtractValue(funcValueWithSignatureGlobal.Initializer(), []uint32{0}).Operand(0)
- return
- }
- llvmSig := b.getRawFuncType(sig)
- sigGlobal := b.getFuncSignatureID(sig)
- funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
- funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")
- default:
- panic("unimplemented func value variant")
+ bitcast := b.CreateExtractValue(funcValue, 1, "")
+ if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast {
+ funcPtr = bitcast.Operand(0)
+ return
}
+ llvmSig := b.getRawFuncType(sig)
+ funcPtr = b.CreateBitCast(bitcast, llvmSig, "")
return
}
// getFuncType returns the type of a func value given a signature.
func (c *compilerContext) getFuncType(typ *types.Signature) llvm.Type {
- switch c.FuncImplementation {
- case "doubleword":
- return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
- case "switch":
- return c.getLLVMRuntimeType("funcValue")
- default:
- panic("unimplemented func value variant")
- }
+ return c.ctx.StructType([]llvm.Type{c.i8ptrType, c.rawVoidFuncType}, false)
}
// getRawFuncType returns a LLVM function pointer type for a given signature.
diff --git a/compiler/testdata/func-none.ll b/compiler/testdata/func-none.ll
deleted file mode 100644
index d9c5c4d42..000000000
--- a/compiler/testdata/func-none.ll
+++ /dev/null
@@ -1,55 +0,0 @@
-; ModuleID = 'func.go'
-source_filename = "func.go"
-target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
-target triple = "wasm32-unknown-wasi"
-
-%runtime.funcValueWithSignature = type { i32, i8* }
-
-@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
-@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
-
-declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
-
-declare void @runtime.trackPointer(i8* nocapture readonly, i8*, i8*)
-
-; Function Attrs: nounwind
-define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
-entry:
- ret void
-}
-
-; Function Attrs: nounwind
-define hidden void @main.foo(i8* %callback.context, i32 %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
-entry:
- %0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null) #0
- %1 = icmp eq i32 %0, 0
- br i1 %1, label %fpcall.throw, label %fpcall.next
-
-fpcall.throw: ; preds = %entry
- call void @runtime.nilPanic(i8* undef, i8* null) #0
- unreachable
-
-fpcall.next: ; preds = %entry
- %2 = inttoptr i32 %0 to void (i32, i8*, i8*)*
- call void %2(i32 3, i8* %callback.context, i8* undef) #0
- ret void
-}
-
-declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*)
-
-declare void @runtime.nilPanic(i8*, i8*)
-
-; Function Attrs: nounwind
-define hidden void @main.bar(i8* %context, i8* %parentHandle) unnamed_addr #0 {
-entry:
- call void @main.foo(i8* undef, i32 ptrtoint (%runtime.funcValueWithSignature* @"main.someFunc$withSignature" to i32), i8* undef, i8* undef)
- ret void
-}
-
-; Function Attrs: nounwind
-define hidden void @main.someFunc(i32 %arg0, i8* %context, i8* %parentHandle) unnamed_addr #0 {
-entry:
- ret void
-}
-
-attributes #0 = { nounwind }
diff --git a/compiler/testdata/func.ll b/compiler/testdata/func.ll
new file mode 100644
index 000000000..f56779c59
--- /dev/null
+++ b/compiler/testdata/func.ll
@@ -0,0 +1,47 @@
+; ModuleID = 'func.go'
+source_filename = "func.go"
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20"
+target triple = "wasm32-unknown-wasi"
+
+declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*)
+
+declare void @runtime.trackPointer(i8* nocapture readonly, i8*, i8*)
+
+; Function Attrs: nounwind
+define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 {
+entry:
+ ret void
+}
+
+; Function Attrs: nounwind
+define hidden void @main.foo(i8* %callback.context, void ()* %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr #0 {
+entry:
+ %0 = icmp eq void ()* %callback.funcptr, null
+ br i1 %0, label %fpcall.throw, label %fpcall.next
+
+fpcall.throw: ; preds = %entry
+ call void @runtime.nilPanic(i8* undef, i8* null) #0
+ unreachable
+
+fpcall.next: ; preds = %entry
+ %1 = bitcast void ()* %callback.funcptr to void (i32, i8*, i8*)*
+ call void %1(i32 3, i8* %callback.context, i8* undef) #0
+ ret void
+}
+
+declare void @runtime.nilPanic(i8*, i8*)
+
+; Function Attrs: nounwind
+define hidden void @main.bar(i8* %context, i8* %parentHandle) unnamed_addr #0 {
+entry:
+ call void @main.foo(i8* undef, void ()* bitcast (void (i32, i8*, i8*)* @main.someFunc to void ()*), i8* undef, i8* undef)
+ ret void
+}
+
+; Function Attrs: nounwind
+define hidden void @main.someFunc(i32 %arg0, i8* %context, i8* %parentHandle) unnamed_addr #0 {
+entry:
+ ret void
+}
+
+attributes #0 = { nounwind }
diff --git a/compiler/testdata/gc.go b/compiler/testdata/gc.go
index ecff9f88b..20e596702 100644
--- a/compiler/testdata/gc.go
+++ b/compiler/testdata/gc.go
@@ -61,11 +61,6 @@ func newStruct() {
}
func newFuncValue() *func() {
- // On some platforms that use runtime.funcValue ("switch" style) function
- // values, a func value is allocated as having two pointer words while the
- // struct looks like {unsafe.Pointer; uintptr}. This is so that the interp
- // package won't get confused, see getPointerBitmap in compiler/llvm.go for
- // details.
return new(func())
}
diff --git a/transform/func-lowering.go b/transform/func-lowering.go
deleted file mode 100644
index 0e65bacb6..000000000
--- a/transform/func-lowering.go
+++ /dev/null
@@ -1,285 +0,0 @@
-package transform
-
-// This file lowers func values into their final form. This is necessary for
-// funcValueSwitch, which needs full program analysis.
-
-import (
- "sort"
- "strconv"
- "strings"
-
- "github.com/tinygo-org/tinygo/compiler/llvmutil"
- "tinygo.org/x/go-llvm"
-)
-
-// funcSignatureInfo keeps information about a single signature and its uses.
-type funcSignatureInfo struct {
- sig llvm.Value // *uint8 to identify the signature
- funcValueWithSignatures []llvm.Value // slice of runtime.funcValueWithSignature
-}
-
-// funcWithUses keeps information about a single function used as func value and
-// the assigned function ID. More commonly used functions are assigned a lower
-// ID.
-type funcWithUses struct {
- funcPtr llvm.Value
- useCount int // how often this function is used in a func value
- id int // assigned ID
-}
-
-// Slice to sort functions by their use counts, or else their name if they're
-// used equally often.
-type funcWithUsesList []*funcWithUses
-
-func (l funcWithUsesList) Len() int { return len(l) }
-func (l funcWithUsesList) Less(i, j int) bool {
- if l[i].useCount != l[j].useCount {
- // return the reverse: we want the highest use counts sorted first
- return l[i].useCount > l[j].useCount
- }
- iName := l[i].funcPtr.Name()
- jName := l[j].funcPtr.Name()
- return iName < jName
-}
-func (l funcWithUsesList) Swap(i, j int) {
- l[i], l[j] = l[j], l[i]
-}
-
-// LowerFuncValues lowers the runtime.funcValueWithSignature type and
-// runtime.getFuncPtr function to their final form.
-func LowerFuncValues(mod llvm.Module) {
- ctx := mod.Context()
- builder := ctx.NewBuilder()
- uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
-
- // Find all func values used in the program with their signatures.
- signatures := map[string]*funcSignatureInfo{}
- for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
- var sig, funcVal llvm.Value
- switch {
- case strings.HasSuffix(global.Name(), "$withSignature"):
- sig = llvm.ConstExtractValue(global.Initializer(), []uint32{1})
- funcVal = global
- case strings.HasPrefix(global.Name(), "reflect/types.funcid:func:{"):
- sig = global
- default:
- continue
- }
-
- name := sig.Name()
- var funcValueWithSignatures []llvm.Value
- if funcVal.IsNil() {
- funcValueWithSignatures = []llvm.Value{}
- } else {
- funcValueWithSignatures = []llvm.Value{funcVal}
- }
- if info, ok := signatures[name]; ok {
- info.funcValueWithSignatures = append(info.funcValueWithSignatures, funcValueWithSignatures...)
- } else {
- signatures[name] = &funcSignatureInfo{
- sig: sig,
- funcValueWithSignatures: funcValueWithSignatures,
- }
- }
- }
-
- // Sort the signatures, for deterministic execution.
- names := make([]string, 0, len(signatures))
- for name := range signatures {
- names = append(names, name)
- }
- sort.Strings(names)
-
- for _, name := range names {
- info := signatures[name]
- functions := make(funcWithUsesList, len(info.funcValueWithSignatures))
- for i, use := range info.funcValueWithSignatures {
- var useCount int
- for _, use2 := range getUses(use) {
- useCount += len(getUses(use2))
- }
- functions[i] = &funcWithUses{
- funcPtr: llvm.ConstExtractValue(use.Initializer(), []uint32{0}).Operand(0),
- useCount: useCount,
- }
- }
- sort.Sort(functions)
-
- for i, fn := range functions {
- fn.id = i + 1
- for _, ptrtoint := range getUses(fn.funcPtr) {
- if ptrtoint.IsAConstantExpr().IsNil() || ptrtoint.Opcode() != llvm.PtrToInt {
- continue
- }
- for _, funcValueWithSignatureConstant := range getUses(ptrtoint) {
- if !funcValueWithSignatureConstant.IsACallInst().IsNil() && funcValueWithSignatureConstant.CalledValue().Name() == "runtime.makeGoroutine" {
- // makeGoroutine calls are handled seperately
- continue
- }
- for _, funcValueWithSignatureGlobal := range getUses(funcValueWithSignatureConstant) {
- id := llvm.ConstInt(uintptrType, uint64(fn.id), false)
- for _, use := range getUses(funcValueWithSignatureGlobal) {
- // Try to replace uses directly: most will be
- // ptrtoint instructions.
- if !use.IsAConstantExpr().IsNil() && use.Opcode() == llvm.PtrToInt {
- use.ReplaceAllUsesWith(id)
- }
- }
- // Remaining uses can be replaced using a ptrtoint.
- // In my quick testing, this doesn't really happen in
- // practice.
- funcValueWithSignatureGlobal.ReplaceAllUsesWith(llvm.ConstIntToPtr(id, funcValueWithSignatureGlobal.Type()))
- }
- }
- }
- }
-
- for _, getFuncPtrCall := range getUses(info.sig) {
- if getFuncPtrCall.IsACallInst().IsNil() {
- continue
- }
- if getFuncPtrCall.CalledValue().Name() != "runtime.getFuncPtr" {
- panic("expected all call uses to be runtime.getFuncPtr")
- }
- funcID := getFuncPtrCall.Operand(1)
-
- // There are functions used in a func value that
- // implement this signature.
- // What we'll do is transform the following:
- // rawPtr := runtime.getFuncPtr(func.ptr)
- // if rawPtr == nil {
- // runtime.nilPanic()
- // }
- // result := rawPtr(...args, func.context)
- // into this:
- // if false {
- // runtime.nilPanic()
- // }
- // var result // Phi
- // switch fn.id {
- // case 0:
- // runtime.nilPanic()
- // case 1:
- // result = call first implementation...
- // case 2:
- // result = call second implementation...
- // default:
- // unreachable
- // }
-
- // Remove some casts, checks, and the old call which we're going
- // to replace.
- for _, callIntPtr := range getUses(getFuncPtrCall) {
- if !callIntPtr.IsACallInst().IsNil() && callIntPtr.CalledValue().Name() == "internal/task.start" {
- // Special case for goroutine starts.
- addFuncLoweringSwitch(mod, builder, funcID, callIntPtr, func(funcPtr llvm.Value, params []llvm.Value) llvm.Value {
- i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
- calleeValue := builder.CreatePtrToInt(funcPtr, uintptrType, "")
- start := mod.NamedFunction("internal/task.start")
- builder.CreateCall(start, []llvm.Value{calleeValue, callIntPtr.Operand(1), llvm.Undef(uintptrType), llvm.Undef(i8ptrType), llvm.ConstNull(i8ptrType)}, "")
- return llvm.Value{} // void so no return value
- }, functions)
- callIntPtr.EraseFromParentAsInstruction()
- continue
- }
- if callIntPtr.IsAIntToPtrInst().IsNil() {
- panic("expected inttoptr")
- }
- for _, ptrUse := range getUses(callIntPtr) {
- if !ptrUse.IsAICmpInst().IsNil() {
- ptrUse.ReplaceAllUsesWith(llvm.ConstInt(ctx.Int1Type(), 0, false))
- } else if !ptrUse.IsACallInst().IsNil() && ptrUse.CalledValue() == callIntPtr {
- addFuncLoweringSwitch(mod, builder, funcID, ptrUse, func(funcPtr llvm.Value, params []llvm.Value) llvm.Value {
- return builder.CreateCall(funcPtr, params, "")
- }, functions)
- } else {
- panic("unexpected getFuncPtrCall")
- }
- ptrUse.EraseFromParentAsInstruction()
- }
- callIntPtr.EraseFromParentAsInstruction()
- }
- getFuncPtrCall.EraseFromParentAsInstruction()
- }
-
- // Clean up all globals used before func lowering.
- for _, obj := range info.funcValueWithSignatures {
- obj.EraseFromParentAsGlobal()
- }
- info.sig.EraseFromParentAsGlobal()
- }
-}
-
-// addFuncLoweringSwitch creates a new switch on a function ID and inserts calls
-// to the newly created direct calls. The funcID is the number to switch on,
-// call is the call instruction to replace, and createCall is the callback that
-// actually creates the new call. By changing createCall to something other than
-// builder.CreateCall, instead of calling a function it can start a new
-// goroutine for example.
-func addFuncLoweringSwitch(mod llvm.Module, builder llvm.Builder, funcID, call llvm.Value, createCall func(funcPtr llvm.Value, params []llvm.Value) llvm.Value, functions funcWithUsesList) {
- ctx := mod.Context()
- uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
- i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
-
- // The block that cannot be reached with correct funcValues (to help the
- // optimizer).
- builder.SetInsertPointBefore(call)
- defaultBlock := ctx.AddBasicBlock(call.InstructionParent().Parent(), "func.default")
- builder.SetInsertPointAtEnd(defaultBlock)
- builder.CreateUnreachable()
-
- // Create the switch.
- builder.SetInsertPointBefore(call)
- sw := builder.CreateSwitch(funcID, defaultBlock, len(functions)+1)
-
- // Split right after the switch. We will need to insert a few basic blocks
- // in this gap.
- nextBlock := llvmutil.SplitBasicBlock(builder, sw, llvm.NextBasicBlock(sw.InstructionParent()), "func.next")
-
- // Temporarily set the insert point to set the correct debug insert location
- // for the builder. It got destroyed by the SplitBasicBlock call.
- builder.SetInsertPointBefore(call)
-
- // The 0 case, which is actually a nil check.
- nilBlock := ctx.InsertBasicBlock(nextBlock, "func.nil")
- builder.SetInsertPointAtEnd(nilBlock)
- nilPanic := mod.NamedFunction("runtime.nilPanic")
- builder.CreateCall(nilPanic, []llvm.Value{llvm.Undef(i8ptrType), llvm.ConstNull(i8ptrType)}, "")
- builder.CreateUnreachable()
- sw.AddCase(llvm.ConstInt(uintptrType, 0, false), nilBlock)
-
- // Gather the list of parameters for every call we're going to make.
- callParams := make([]llvm.Value, call.OperandsCount()-1)
- for i := range callParams {
- callParams[i] = call.Operand(i)
- }
-
- // If the call produces a value, we need to get it using a PHI
- // node.
- phiBlocks := make([]llvm.BasicBlock, len(functions))
- phiValues := make([]llvm.Value, len(functions))
- for i, fn := range functions {
- // Insert a switch case.
- bb := ctx.InsertBasicBlock(nextBlock, "func.call"+strconv.Itoa(fn.id))
- builder.SetInsertPointAtEnd(bb)
- result := createCall(fn.funcPtr, callParams)
- builder.CreateBr(nextBlock)
- sw.AddCase(llvm.ConstInt(uintptrType, uint64(fn.id), false), bb)
- phiBlocks[i] = bb
- phiValues[i] = result
- }
- if call.Type().TypeKind() != llvm.VoidTypeKind {
- if len(functions) > 0 {
- // Create the PHI node so that the call result flows into the
- // next block (after the split). This is only necessary when the
- // call produced a value.
- builder.SetInsertPointBefore(nextBlock.FirstInstruction())
- phi := builder.CreatePHI(call.Type(), "")
- phi.AddIncoming(phiValues, phiBlocks)
- call.ReplaceAllUsesWith(phi)
- } else {
- // This is always a nil panic, so replace the call result with undef.
- call.ReplaceAllUsesWith(llvm.Undef(call.Type()))
- }
- }
-}
diff --git a/transform/func-lowering_test.go b/transform/func-lowering_test.go
deleted file mode 100644
index e88212454..000000000
--- a/transform/func-lowering_test.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package transform_test
-
-import (
- "testing"
-
- "github.com/tinygo-org/tinygo/transform"
-)
-
-func TestFuncLowering(t *testing.T) {
- t.Parallel()
- testTransform(t, "testdata/func-lowering", transform.LowerFuncValues)
-}
diff --git a/transform/optimizer.go b/transform/optimizer.go
index ab90ca7cd..0c1d8a8f2 100644
--- a/transform/optimizer.go
+++ b/transform/optimizer.go
@@ -78,10 +78,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
return errs
}
- if config.FuncImplementation() == "switch" {
- LowerFuncValues(mod)
- }
-
// After interfaces are lowered, there are many more opportunities for
// interprocedural optimizations. To get them to work, function
// attributes have to be updated first.
@@ -102,9 +98,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
return []error{err}
}
LowerReflect(mod)
- if config.FuncImplementation() == "switch" {
- LowerFuncValues(mod)
- }
errs := LowerInterrupts(mod)
if len(errs) > 0 {
return errs
diff --git a/transform/testdata/func-lowering.ll b/transform/testdata/func-lowering.ll
deleted file mode 100644
index 872b3baee..000000000
--- a/transform/testdata/func-lowering.ll
+++ /dev/null
@@ -1,71 +0,0 @@
-target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
-target triple = "wasm32-unknown-unknown-wasm"
-
-%runtime.funcValueWithSignature = type { i32, i8* }
-
-@"reflect/types.funcid:func:{basic:uint8}{}" = external constant i8
-@"reflect/types.funcid:func:{basic:int}{}" = external constant i8
-@"reflect/types.funcid:func:{}{basic:uint32}" = external constant i8
-@"func1Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func1Uint8 to i32), i8* @"reflect/types.funcid:func:{basic:uint8}{}" }
-@"func2Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func2Uint8 to i32), i8* @"reflect/types.funcid:func:{basic:uint8}{}" }
-@"main$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$1" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
-@"main$2$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$2" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" }
-
-declare i32 @runtime.getFuncPtr(i8*, i32, i8*, i8*, i8*)
-
-declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
-
-declare void @runtime.nilPanic(i8*, i8*)
-
-declare void @"main$1"(i32, i8*, i8*)
-
-declare void @"main$2"(i32, i8*, i8*)
-
-declare void @func1Uint8(i8, i8*, i8*)
-
-declare void @func2Uint8(i8, i8*, i8*)
-
-; There are no functions with this signature used in a func value.
-; This means that this should unconditionally nil panic.
-define i32 @runFuncNone(i8*, i32, i8* %context, i8* %parentHandle) {
-entry:
- %2 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, i8* @"reflect/types.funcid:func:{}{basic:uint32}", i8* undef, i8* null)
- %3 = inttoptr i32 %2 to i32 (i8*, i8*)*
- %4 = icmp eq i32 (i8*, i8*)* %3, null
- br i1 %4, label %fpcall.nil, label %fpcall.next
-
-fpcall.nil:
- call void @runtime.nilPanic(i8* undef, i8* null)
- unreachable
-
-fpcall.next:
- %5 = call i32 %3(i8* %0, i8* undef)
- ret i32 %5
-}
-
-; There are two functions with this signature used in a func value. That means
-; that we'll have to check at runtime which of the two it is (or whether the
-; func value is nil). This call will thus be lowered to a switch statement.
-define void @runFunc2(i8*, i32, i8, i8* %context, i8* %parentHandle) {
-entry:
- %3 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, i8* @"reflect/types.funcid:func:{basic:uint8}{}", i8* undef, i8* null)
- %4 = inttoptr i32 %3 to void (i8, i8*, i8*)*
- %5 = icmp eq void (i8, i8*, i8*)* %4, null
- br i1 %5, label %fpcall.nil, label %fpcall.next
-
-fpcall.nil:
- call void @runtime.nilPanic(i8* undef, i8* null)
- unreachable
-
-fpcall.next:
- call void %4(i8 %2, i8* %0, i8* undef)
- ret void
-}
-
-; Special case for internal/task.start.
-define void @sleepFuncValue(i8*, i32, i8* nocapture readnone %context, i8* nocapture readnone %parentHandle) {
-entry:
- %2 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, i8* @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null)
- call void @"internal/task.start"(i32 %2, i8* null, i32 undef, i8* undef, i8* null)
- ret void
-}
diff --git a/transform/testdata/func-lowering.out.ll b/transform/testdata/func-lowering.out.ll
deleted file mode 100644
index 074fcb718..000000000
--- a/transform/testdata/func-lowering.out.ll
+++ /dev/null
@@ -1,101 +0,0 @@
-target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
-target triple = "wasm32-unknown-unknown-wasm"
-
-declare i32 @runtime.getFuncPtr(i8*, i32, i8*, i8*, i8*)
-
-declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
-
-declare void @runtime.nilPanic(i8*, i8*)
-
-declare void @"main$1"(i32, i8*, i8*)
-
-declare void @"main$2"(i32, i8*, i8*)
-
-declare void @func1Uint8(i8, i8*, i8*)
-
-declare void @func2Uint8(i8, i8*, i8*)
-
-define i32 @runFuncNone(i8* %0, i32 %1, i8* %context, i8* %parentHandle) {
-entry:
- br i1 false, label %fpcall.nil, label %fpcall.next
-
-fpcall.nil: ; preds = %entry
- call void @runtime.nilPanic(i8* undef, i8* null)
- unreachable
-
-fpcall.next: ; preds = %entry
- switch i32 %1, label %func.default [
- i32 0, label %func.nil
- ]
-
-func.nil: ; preds = %fpcall.next
- call void @runtime.nilPanic(i8* undef, i8* null)
- unreachable
-
-func.next: ; No predecessors!
- ret i32 undef
-
-func.default: ; preds = %fpcall.next
- unreachable
-}
-
-define void @runFunc2(i8* %0, i32 %1, i8 %2, i8* %context, i8* %parentHandle) {
-entry:
- br i1 false, label %fpcall.nil, label %fpcall.next
-
-fpcall.nil: ; preds = %entry
- call void @runtime.nilPanic(i8* undef, i8* null)
- unreachable
-
-fpcall.next: ; preds = %entry
- switch i32 %1, label %func.default [
- i32 0, label %func.nil
- i32 1, label %func.call1
- i32 2, label %func.call2
- ]
-
-func.nil: ; preds = %fpcall.next
- call void @runtime.nilPanic(i8* undef, i8* null)
- unreachable
-
-func.call1: ; preds = %fpcall.next
- call void @func1Uint8(i8 %2, i8* %0, i8* undef)
- br label %func.next
-
-func.call2: ; preds = %fpcall.next
- call void @func2Uint8(i8 %2, i8* %0, i8* undef)
- br label %func.next
-
-func.next: ; preds = %func.call2, %func.call1
- ret void
-
-func.default: ; preds = %fpcall.next
- unreachable
-}
-
-define void @sleepFuncValue(i8* %0, i32 %1, i8* nocapture readnone %context, i8* nocapture readnone %parentHandle) {
-entry:
- switch i32 %1, label %func.default [
- i32 0, label %func.nil
- i32 1, label %func.call1
- i32 2, label %func.call2
- ]
-
-func.nil: ; preds = %entry
- call void @runtime.nilPanic(i8* undef, i8* null)
- unreachable
-
-func.call1: ; preds = %entry
- call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main$1" to i32), i8* null, i32 undef, i8* undef, i8* null)
- br label %func.next
-
-func.call2: ; preds = %entry
- call void @"internal/task.start"(i32 ptrtoint (void (i32, i8*, i8*)* @"main$2" to i32), i8* null, i32 undef, i8* undef, i8* null)
- br label %func.next
-
-func.next: ; preds = %func.call2, %func.call1
- ret void
-
-func.default: ; preds = %entry
- unreachable
-}
diff --git a/transform/transform_test.go b/transform/transform_test.go
index 935d660b9..2296c97f5 100644
--- a/transform/transform_test.go
+++ b/transform/transform_test.go
@@ -146,7 +146,6 @@ func compileGoFileForTesting(t *testing.T, filename string) llvm.Module {
CodeModel: config.CodeModel(),
RelocationModel: config.RelocationModel(),
Scheduler: config.Scheduler(),
- FuncImplementation: config.FuncImplementation(),
AutomaticStackSize: config.AutomaticStackSize(),
Debug: true,
}