diff options
-rw-r--r-- | builder/build.go | 1 | ||||
-rw-r--r-- | compileopts/config.go | 22 | ||||
-rw-r--r-- | compiler/compiler.go | 1 | ||||
-rw-r--r-- | compiler/compiler_test.go | 3 | ||||
-rw-r--r-- | compiler/func.go | 63 | ||||
-rw-r--r-- | compiler/testdata/func-none.ll | 55 | ||||
-rw-r--r-- | compiler/testdata/func.ll | 47 | ||||
-rw-r--r-- | compiler/testdata/gc.go | 5 | ||||
-rw-r--r-- | transform/func-lowering.go | 285 | ||||
-rw-r--r-- | transform/func-lowering_test.go | 12 | ||||
-rw-r--r-- | transform/optimizer.go | 7 | ||||
-rw-r--r-- | transform/testdata/func-lowering.ll | 71 | ||||
-rw-r--r-- | transform/testdata/func-lowering.out.ll | 101 | ||||
-rw-r--r-- | transform/transform_test.go | 1 |
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, } |