From 2f7952ff2985bd1061203f2d4a5c421c2e93ccaf Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 4 Aug 2024 15:37:50 +0200 Subject: [PATCH 1/2] compiler: support panics inside indirect deferred functions Found this bug while working on WebAssembly recover support. --- compiler/defer.go | 4 ++-- testdata/recover.go | 27 +++++++++++++++++++++++++++ testdata/recover.txt | 6 ++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/compiler/defer.go b/compiler/defer.go index df8686957a..1abc34a953 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -528,7 +528,7 @@ func (b *builder) createRunDefers() { forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType)) } - b.createCall(fnType, fnPtr, forwardParams, "") + b.createInvoke(fnType, fnPtr, forwardParams, "") case *ssa.Function: // Direct call. @@ -583,7 +583,7 @@ func (b *builder) createRunDefers() { // Call deferred function. fnType, llvmFn := b.getFunction(fn) - b.createCall(fnType, llvmFn, forwardParams, "") + b.createInvoke(fnType, llvmFn, forwardParams, "") case *ssa.Builtin: db := b.deferBuiltinFuncs[callback] diff --git a/testdata/recover.go b/testdata/recover.go index ced90cfaee..725821bfa9 100644 --- a/testdata/recover.go +++ b/testdata/recover.go @@ -17,6 +17,12 @@ func main() { println("\n# panic inside defer") panicInsideDefer() + println("\n# panic inside indirect defer") + panicInsideIndirectDefer(callPanic) + + println("\n# panic inside closure") + panicInsideClosure() + println("\n# panic replace") panicReplace() } @@ -77,6 +83,27 @@ func panicInsideDefer() { }() } +func panicInsideIndirectDefer(callback func()) { + defer func() { + printitf("recovered:", recover()) + }() + defer callback() +} + +func callPanic() { + panic("panic") +} + +func panicInsideClosure() { + msg := "panic" + defer func() { + printitf("recovered:", recover()) + }() + defer func() { + panic(msg) + }() +} + func panicReplace() { defer func() { printitf("recovered:", recover()) diff --git a/testdata/recover.txt b/testdata/recover.txt index d276498550..9faf8619e6 100644 --- a/testdata/recover.txt +++ b/testdata/recover.txt @@ -19,6 +19,12 @@ recovered 2: foo # panic inside defer recovered: panic +# panic inside indirect defer +recovered: panic + +# panic inside closure +recovered: panic + # panic replace panic 1 panic 2 From 89ffb47a0c8eebcae4f015df722563bb72ef5e33 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sat, 3 Aug 2024 09:56:19 +0200 Subject: [PATCH 2/2] wasm: add recover support Add unwinding and recover support for wasm using WebAssembly exception handling. This still has a few gotchas: * Many WASI systems don't support exception handling yet. For example, see: https://github.com/bytecodealliance/wasmtime/issues/2049 * Asyncify doesn't support wasm exception handling: https://github.com/WebAssembly/binaryen/issues/4470 This means it's not possible to use goroutines together with panic/recover. * The current way that exceptions are implemented pretend to be C++ exceptions, but work slightly differently. If C++ code is called (for example through CGo) that raises an exception, that exception will be eaten by TinyGo and not be propagated. This is fixable, it just hasn't been implemented (because we don't actually support C++ right now). I hope that these issues will be resolved over time. At least for now, people who need `recover()` have a way to use it. --- builder/build.go | 1 + compiler/calls.go | 31 ++- compiler/compiler.go | 23 +- compiler/compiler_test.go | 1 + compiler/defer.go | 171 +++++++++--- compiler/symbol.go | 5 +- compiler/testdata/basic.ll | 49 ++-- compiler/testdata/channel.ll | 31 ++- compiler/testdata/defer-cortex-m-qemu.ll | 22 +- compiler/testdata/defer-wasm-none.ll | 273 +++++++++++++++++++ compiler/testdata/float.ll | 24 +- compiler/testdata/func.ll | 17 +- compiler/testdata/gc.ll | 90 +++--- compiler/testdata/go1.20.ll | 23 +- compiler/testdata/go1.21.ll | 49 ++-- compiler/testdata/goroutine-wasm-asyncify.ll | 65 ++--- compiler/testdata/interface.ll | 55 ++-- compiler/testdata/pointer.ll | 23 +- compiler/testdata/pragma.ll | 33 +-- compiler/testdata/slice.ll | 85 +++--- compiler/testdata/string.ll | 31 ++- compiler/testdata/zeromap.ll | 33 +-- go.mod | 2 +- go.sum | 4 +- main_test.go | 12 +- src/runtime/panic.go | 126 +++++++-- targets/wasm.json | 3 +- transform/gc.go | 2 +- 28 files changed, 886 insertions(+), 398 deletions(-) create mode 100644 compiler/testdata/defer-wasm-none.ll diff --git a/builder/build.go b/builder/build.go index f728cde79d..5f11803122 100644 --- a/builder/build.go +++ b/builder/build.go @@ -747,6 +747,7 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe } ldflags = append(ldflags, "-mllvm", "-mcpu="+config.CPU()) ldflags = append(ldflags, "-mllvm", "-mattr="+config.Features()) // needed for MIPS softfloat + ldflags = append(ldflags, "-mllvm", "-wasm-enable-eh") if config.GOOS() == "windows" { // Options for the MinGW wrapper for the lld COFF linker. ldflags = append(ldflags, diff --git a/compiler/calls.go b/compiler/calls.go index f4b76a5135..3b03f39755 100644 --- a/compiler/calls.go +++ b/compiler/calls.go @@ -70,21 +70,25 @@ func (b *builder) createRuntimeInvoke(fnName string, args []llvm.Value, name str // createCall creates a call to the given function with the arguments possibly // expanded. func (b *builder) createCall(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value { - expanded := make([]llvm.Value, 0, len(args)) - for _, arg := range args { - fragments := b.expandFormalParam(arg) - expanded = append(expanded, fragments...) - } - return b.CreateCall(fnType, fn, expanded, name) + return b.CreateCall(fnType, fn, b.expandFormalParams(args), name) } // createInvoke is like createCall but continues execution at the landing pad if // the call resulted in a panic. func (b *builder) createInvoke(fnType llvm.Type, fn llvm.Value, args []llvm.Value, name string) llvm.Value { - if b.hasDeferFrame() { + switch b.deferFrameType() { + case recoverInlineAsm: b.createInvokeCheckpoint() + return b.createCall(fnType, fn, args, name) + case recoverWasmEH: + continueBB := b.insertBasicBlock("invoke.cont") + call := b.CreateInvoke(fnType, fn, b.expandFormalParams(args), continueBB, b.landingpad, name) + b.SetInsertPointAtEnd(continueBB) + b.blockExits[b.currentBlock] = continueBB + return call + default: + return b.createCall(fnType, fn, args, name) } - return b.createCall(fnType, fn, args, name) } // Expand an argument type to a list that can be used in a function call @@ -123,6 +127,17 @@ func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 { } } +// expandFormalParams expands every param in the params slice like +// expandFormalParam. +func (b *builder) expandFormalParams(params []llvm.Value) []llvm.Value { + expanded := make([]llvm.Value, 0, len(params)) + for _, arg := range params { + fragments := b.expandFormalParam(arg) + expanded = append(expanded, fragments...) + } + return expanded +} + // expandFormalParam splits a formal param value into pieces, so it can be // passed directly as part of a function call. For example, it splits up small // structs into individual fields. It is the equivalent of expandFormalParamType diff --git a/compiler/compiler.go b/compiler/compiler.go index bc17250d99..dcf4c1a832 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -355,7 +355,10 @@ func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, } func (c *compilerContext) getRuntimeType(name string) types.Type { - return c.runtimePkg.Scope().Lookup(name).(*types.TypeName).Type() + if typ, ok := c.runtimePkg.Scope().Lookup(name).(*types.TypeName); ok { + return typ.Type() + } + panic("runtime type not found: " + name) } // getLLVMRuntimeType obtains a named type from the runtime package and returns @@ -1350,7 +1353,7 @@ func (b *builder) createFunction() { b.CreateBr(b.afterDefersBlock[i]) } - if b.hasDeferFrame() { + if b.deferFrameType() != recoverNone { // Create the landing pad block, where execution continues after a // panic. b.createLandingPad() @@ -1487,8 +1490,11 @@ func (b *builder) createInstruction(instr ssa.Instruction) { b.createRuntimeInvoke("_panic", []llvm.Value{value}, "") b.CreateUnreachable() case *ssa.Return: - if b.hasDeferFrame() { - b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "") + switch b.deferFrameType() { + case recoverInlineAsm: + b.createRuntimeCall("destroyDeferFrameInlineAsm", []llvm.Value{b.deferFrame}, "") + case recoverWasmEH: + b.createRuntimeCall("destroyDeferFrameWasmEH", []llvm.Value{b.deferFrame}, "") } if len(instr.Results) == 0 { b.CreateRetVoid() @@ -1741,7 +1747,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c return b.CreateExtractValue(cplx, 0, "real"), nil case "recover": useParentFrame := uint64(0) - if b.hasDeferFrame() { + if b.fn.Recover != nil { // recover() should return the panic value of the parent function, // not of the current function. useParentFrame = 1 @@ -1851,11 +1857,8 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"): return b.createRawSyscallNoError(instr) case name == "runtime.supportsRecover": - supportsRecover := uint64(0) - if b.supportsRecover() { - supportsRecover = 1 - } - return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil + supportsRecover := uint64(b.supportsRecover()) + return llvm.ConstInt(b.ctx.Int8Type(), supportsRecover, false), nil case name == "runtime.panicStrategy": // These constants are defined in src/runtime/panic.go. panicStrategy := map[string]uint64{ diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index 2e5094875c..c27cd9db09 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -44,6 +44,7 @@ func TestCompiler(t *testing.T) { {"interface.go", "", ""}, {"func.go", "", ""}, {"defer.go", "cortex-m-qemu", ""}, + {"defer.go", "wasm", "none"}, {"pragma.go", "", ""}, {"goroutine.go", "wasm", "asyncify"}, {"goroutine.go", "cortex-m-qemu", "tasks"}, diff --git a/compiler/defer.go b/compiler/defer.go index 1abc34a953..f71a92644c 100644 --- a/compiler/defer.go +++ b/compiler/defer.go @@ -16,34 +16,54 @@ package compiler import ( "go/types" "strconv" + "strings" "github.com/tinygo-org/tinygo/compiler/llvmutil" "golang.org/x/tools/go/ssa" "tinygo.org/x/go-llvm" ) +type recoverType uint8 + +const ( + // Note: this matches recoverSupport in src/runtime/panic.go + recoverNone recoverType = iota + recoverInlineAsm + recoverWasmEH // WebAssembly exception handling +) + // supportsRecover returns whether the compiler supports the recover() builtin // for the current architecture. -func (b *builder) supportsRecover() bool { - switch b.archFamily() { +func (c *compilerContext) supportsRecover() recoverType { + switch c.archFamily() { case "wasm32": - // Probably needs to be implemented using the exception handling - // proposal of WebAssembly: - // https://github.com/WebAssembly/exception-handling - return false + if !strings.Contains(c.Features, "+exception-handling") { + // No support for the exception handling proposal. + return recoverNone + } + if c.Scheduler == "asyncify" { + // Asyncify does not support WebAssembly exception handling. + // It exits with the following message: + // unexpected expression type + // UNREACHABLE executed at $TINYGOROOT/lib/binaryen/src/passes/Asyncify.cpp:1142! + // See: https://github.com/WebAssembly/binaryen/issues/4470 + return recoverNone + } + // Use WebAssembly exception handling. + return recoverWasmEH case "riscv64", "xtensa": // TODO: add support for these architectures - return false + return recoverNone default: - return true + return recoverInlineAsm } } -// hasDeferFrame returns whether the current function needs to catch panics and -// run defers. -func (b *builder) hasDeferFrame() bool { +// deferFrameType returns the defer type for this function. It is always +// recoverNone for functions without defer. +func (b *builder) deferFrameType() recoverType { if b.fn.Recover == nil { - return false + return recoverNone } return b.supportsRecover() } @@ -63,21 +83,40 @@ func (b *builder) deferInitFunc() { b.deferPtr = b.CreateAlloca(b.dataPtrType, "deferPtr") b.CreateStore(llvm.ConstPointerNull(b.dataPtrType), b.deferPtr) - if b.hasDeferFrame() { + switch b.deferFrameType() { + case recoverInlineAsm: // Set up the defer frame with the current stack pointer. // This assumes that the stack pointer doesn't move outside of the // function prologue/epilogue (an invariant maintained by TinyGo but // possibly broken by the C alloca function). // The frame pointer is _not_ saved, because it is marked as clobbered // in the setjmp-like inline assembly. - deferFrameType := b.getLLVMRuntimeType("deferFrame") + deferFrameType := b.getLLVMRuntimeType("deferFrameInlineAsm") b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf") stackPointer := b.readStackPointer() - b.createRuntimeCall("setupDeferFrame", []llvm.Value{b.deferFrame, stackPointer}, "") + b.createRuntimeCall("setupDeferFrameInlineAsm", []llvm.Value{b.deferFrame, stackPointer}, "") // Create the landing pad block, which is where control transfers after // a panic. b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "lpad") + case recoverWasmEH: + // Set a personality function, which is required by LLVM but not + // actually called. __gxx_wasm_personality_v0 is the only one that is + // acceptable to the WebAssembly backend it seems. + personality := b.mod.NamedFunction("__gxx_wasm_personality_v0") + if personality.IsNil() { + fnType := llvm.FunctionType(b.ctx.Int32Type(), nil, true) + personality = llvm.AddFunction(b.mod, "__gxx_wasm_personality_v0", fnType) + } + b.llvmFn.SetPersonality(personality) + + // Set up the defer frame. + deferFrameType := b.getLLVMRuntimeType("deferFrameWasmEH") + b.deferFrame = b.CreateAlloca(deferFrameType, "deferframe.buf") + b.createRuntimeCall("setupDeferFrameWasmEH", []llvm.Value{b.deferFrame}, "") + + // Create the landing pad where the 'throw' instruction will jump to. + b.landingpad = b.ctx.AddBasicBlock(b.llvmFn, "catch.dispatch") } } @@ -95,6 +134,43 @@ func (b *builder) createLandingPad() { b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{}) } + if b.supportsRecover() == recoverWasmEH { + // Insert exception handling instructions. + // This emulates C++ exceptions, as if every function is wrapped like + // this: + // + // try { + // function body + // } catch (...) { + // run all defers + // } + // if deferFrame.Panicking { + // // re-raise the same panic message + // panic(previousPanic) + // } + // + // This means that if there is a C++ exception in the program, it will + // get eaten by the Go code. This is fixable if needed (by checking + // whether it's actually a TinyGo exception and re-raising the same + // exception if it isn't after running deferred functions). + // For more information about C++ exception support in WebAssembly: + // https://github.com/WebAssembly/tool-conventions/blob/main/EHScheme.md + + // basic block: catch.dispatch + catchStartBB := b.insertBasicBlock("catch.start") + catchswitch := b.CreateCatchSwitch(llvm.Value{}, llvm.BasicBlock{}, 1, "") + catchswitch.AddHandler(catchStartBB) + + // basic block: catch.start + b.SetInsertPointAtEnd(catchStartBB) + catchpad := b.CreateCatchPad(catchswitch, []llvm.Value{llvm.ConstNull(b.dataPtrType)}, "") + rundefersBB := b.insertBasicBlock("rundefers") + b.CreateCatchRet(catchpad, rundefersBB) + + // basic block: rundefers + b.SetInsertPointAtEnd(rundefersBB) + } + b.createRunDefers() // Continue at the 'recover' block, which returns to the parent in an @@ -432,11 +508,25 @@ func (b *builder) createRunDefers() { // } // } - // Create loop, in the order: loophead, loop, callback0, callback1, ..., unreachable, end. + // Create loop, in the order: + // - loophead + // - loop + // - callback0 + // - callback1 + // - callback* + // - unreachable + // - catch.dispatch (if wasm EH) + // - catch.start (if wasm EH) + // - end + var catchDispatch, catchStart llvm.BasicBlock end := b.insertBasicBlock("rundefers.end") unreachable := b.ctx.InsertBasicBlock(end, "rundefers.default") loop := b.ctx.InsertBasicBlock(unreachable, "rundefers.loop") loophead := b.ctx.InsertBasicBlock(loop, "rundefers.loophead") + if b.supportsRecover() == recoverWasmEH { + catchDispatch = b.ctx.InsertBasicBlock(end, "rundefers.catch.dispatch") + catchStart = b.ctx.InsertBasicBlock(end, "rundefers.catch.start") + } b.CreateBr(loophead) // Create loop head: @@ -471,6 +561,9 @@ func (b *builder) createRunDefers() { block := b.insertBasicBlock("rundefers.callback" + strconv.Itoa(i)) sw.AddCase(llvm.ConstInt(b.uintptrType, uint64(i), false), block) b.SetInsertPointAtEnd(block) + var fnType llvm.Type + var llvmFn llvm.Value + var forwardParams []llvm.Value switch callback := callback.(type) { case *ssa.CallCommon: // Call on an value or interface value. @@ -491,7 +584,6 @@ func (b *builder) createRunDefers() { } // Extract the params from the struct (including receiver). - forwardParams := []llvm.Value{} zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) deferredCallType := b.ctx.StructType(valueTypes, false) for i := 2; i < len(valueTypes); i++ { @@ -500,9 +592,6 @@ func (b *builder) createRunDefers() { forwardParams = append(forwardParams, forwardParam) } - var fnPtr llvm.Value - var fnType llvm.Type - if !callback.IsInvoke() { // Isolate the func value. funcValue := forwardParams[0] @@ -510,7 +599,7 @@ func (b *builder) createRunDefers() { //Get function pointer and context var context llvm.Value - fnPtr, context = b.decodeFuncValue(funcValue) + llvmFn, context = b.decodeFuncValue(funcValue) fnType = b.getLLVMFunctionType(callback.Signature()) //Pass context @@ -519,8 +608,8 @@ func (b *builder) createRunDefers() { // Move typecode from the start to the end of the list of // parameters. forwardParams = append(forwardParams[1:], forwardParams[0]) - fnPtr = b.getInvokeFunction(callback) - fnType = fnPtr.GlobalValueType() + llvmFn = b.getInvokeFunction(callback) + fnType = llvmFn.GlobalValueType() // Add the context parameter. An interface call cannot also be a // closure but we have to supply the parameter anyway for platforms @@ -528,8 +617,6 @@ func (b *builder) createRunDefers() { forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType)) } - b.createInvoke(fnType, fnPtr, forwardParams, "") - case *ssa.Function: // Direct call. @@ -541,7 +628,6 @@ func (b *builder) createRunDefers() { deferredCallType := b.ctx.StructType(valueTypes, false) // Extract the params from the struct. - forwardParams := []llvm.Value{} zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := range getParams(callback.Signature) { gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i+2), false)}, "gep") @@ -557,9 +643,7 @@ func (b *builder) createRunDefers() { forwardParams = append(forwardParams, llvm.Undef(b.dataPtrType)) } - // Call real function. - fnType, fn := b.getFunction(callback) - b.createInvoke(fnType, fn, forwardParams, "") + fnType, llvmFn = b.getFunction(callback) case *ssa.MakeClosure: // Get the real defer struct type and cast to it. @@ -573,7 +657,6 @@ func (b *builder) createRunDefers() { deferredCallType := b.ctx.StructType(valueTypes, false) // Extract the params from the struct. - forwardParams := []llvm.Value{} zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false) for i := 2; i < len(valueTypes); i++ { gep := b.CreateInBoundsGEP(deferredCallType, deferData, []llvm.Value{zero, llvm.ConstInt(b.ctx.Int32Type(), uint64(i), false)}, "") @@ -582,8 +665,7 @@ func (b *builder) createRunDefers() { } // Call deferred function. - fnType, llvmFn := b.getFunction(fn) - b.createInvoke(fnType, llvmFn, forwardParams, "") + fnType, llvmFn = b.getFunction(fn) case *ssa.Builtin: db := b.deferBuiltinFuncs[callback] @@ -611,12 +693,20 @@ func (b *builder) createRunDefers() { if err != nil { b.diagnostics = append(b.diagnostics, err) } + b.CreateBr(loophead) + continue default: panic("unknown deferred function type") } - // Branch back to the start of the loop. - b.CreateBr(loophead) + // Call the deferred function. + if b.supportsRecover() == recoverWasmEH { + b.CreateInvoke(fnType, llvmFn, b.expandFormalParams(forwardParams), loophead, catchDispatch, "") + } else { + b.createInvoke(fnType, llvmFn, forwardParams, "") + // Branch back to the start of the loop. + b.CreateBr(loophead) + } } // Create default unreachable block: @@ -626,6 +716,19 @@ func (b *builder) createRunDefers() { b.SetInsertPointAtEnd(unreachable) b.CreateUnreachable() + // Ignore any exception that might have occured while running the deferred + // functions. The compiler will insert a check whether the function is still + // panicking before return, so no panic gets lost. + if b.supportsRecover() == recoverWasmEH { + b.SetInsertPointAtEnd(catchDispatch) + catchswitch := b.CreateCatchSwitch(llvm.Value{}, llvm.BasicBlock{}, 1, "") + catchswitch.AddHandler(catchStart) + + b.SetInsertPointAtEnd(catchStart) + catchpad := b.CreateCatchPad(catchswitch, []llvm.Value{llvm.ConstNull(b.dataPtrType)}, "") + b.CreateCatchRet(catchpad, loophead) + } + // End of loop. b.SetInsertPointAtEnd(end) } diff --git a/compiler/symbol.go b/compiler/symbol.go index 5ebdee1471..d2a722d23b 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -174,6 +174,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value) // that the only thing we'll do is read the pointer. llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nocapture"), 0)) llvmFn.AddAttributeAtIndex(1, c.ctx.CreateEnumAttribute(llvm.AttributeKindID("readonly"), 0)) + llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0)) case "__mulsi3", "__divmodsi4", "__udivmodsi4": if strings.Split(c.Triple, "-")[0] == "avr" { // These functions are compiler-rt/libgcc functions that are @@ -470,7 +471,9 @@ func (c *compilerContext) addStandardDefinedAttributes(llvmFn llvm.Value) { // This behavior matches Clang when compiling C source files. // It reduces binary size on Linux a little bit on non-x86_64 targets by // eliminating exception tables for these functions. - llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0)) + if c.supportsRecover() != recoverWasmEH { + llvmFn.AddFunctionAttr(c.ctx.CreateEnumAttribute(llvm.AttributeKindID("nounwind"), 0)) + } if strings.Split(c.Triple, "-")[0] == "x86_64" { // Required by the ABI. // The uwtable has two possible values: sync (1) or async (2). We use diff --git a/compiler/testdata/basic.ll b/compiler/testdata/basic.ll index e81d5f2a6a..7d30554074 100644 --- a/compiler/testdata/basic.ll +++ b/compiler/testdata/basic.ll @@ -13,30 +13,31 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 { +define hidden i32 @main.addInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { entry: %0 = add i32 %x, %y ret i32 %0 } ; Function Attrs: nounwind -define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 { +define hidden i1 @main.equalInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { entry: %0 = icmp eq i32 %x, %y ret i1 %0 } ; Function Attrs: nounwind -define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 { +define hidden i32 @main.divInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { entry: %0 = icmp eq i32 %y, 0 br i1 %0, label %divbyzero.throw, label %divbyzero.next @@ -54,10 +55,10 @@ divbyzero.throw: ; preds = %entry unreachable } -declare void @runtime.divideByZeroPanic(ptr) #1 +declare void @runtime.divideByZeroPanic(ptr) #2 ; Function Attrs: nounwind -define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 { +define hidden i32 @main.divUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { entry: %0 = icmp eq i32 %y, 0 br i1 %0, label %divbyzero.throw, label %divbyzero.next @@ -72,7 +73,7 @@ divbyzero.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #2 { +define hidden i32 @main.remInt(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { entry: %0 = icmp eq i32 %y, 0 br i1 %0, label %divbyzero.throw, label %divbyzero.next @@ -91,7 +92,7 @@ divbyzero.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #2 { +define hidden i32 @main.remUint(i32 %x, i32 %y, ptr %context) unnamed_addr #1 { entry: %0 = icmp eq i32 %y, 0 br i1 %0, label %divbyzero.throw, label %divbyzero.next @@ -106,61 +107,61 @@ divbyzero.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #2 { +define hidden i1 @main.floatEQ(float %x, float %y, ptr %context) unnamed_addr #1 { entry: %0 = fcmp oeq float %x, %y ret i1 %0 } ; Function Attrs: nounwind -define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #2 { +define hidden i1 @main.floatNE(float %x, float %y, ptr %context) unnamed_addr #1 { entry: %0 = fcmp une float %x, %y ret i1 %0 } ; Function Attrs: nounwind -define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #2 { +define hidden i1 @main.floatLower(float %x, float %y, ptr %context) unnamed_addr #1 { entry: %0 = fcmp olt float %x, %y ret i1 %0 } ; Function Attrs: nounwind -define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #2 { +define hidden i1 @main.floatLowerEqual(float %x, float %y, ptr %context) unnamed_addr #1 { entry: %0 = fcmp ole float %x, %y ret i1 %0 } ; Function Attrs: nounwind -define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #2 { +define hidden i1 @main.floatGreater(float %x, float %y, ptr %context) unnamed_addr #1 { entry: %0 = fcmp ogt float %x, %y ret i1 %0 } ; Function Attrs: nounwind -define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #2 { +define hidden i1 @main.floatGreaterEqual(float %x, float %y, ptr %context) unnamed_addr #1 { entry: %0 = fcmp oge float %x, %y ret i1 %0 } ; Function Attrs: nounwind -define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #2 { +define hidden float @main.complexReal(float %x.r, float %x.i, ptr %context) unnamed_addr #1 { entry: ret float %x.r } ; Function Attrs: nounwind -define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #2 { +define hidden float @main.complexImag(float %x.r, float %x.i, ptr %context) unnamed_addr #1 { entry: ret float %x.i } ; Function Attrs: nounwind -define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 { +define hidden { float, float } @main.complexAdd(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 { entry: %0 = fadd float %x.r, %y.r %1 = fadd float %x.i, %y.i @@ -170,7 +171,7 @@ entry: } ; Function Attrs: nounwind -define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 { +define hidden { float, float } @main.complexSub(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 { entry: %0 = fsub float %x.r, %y.r %1 = fsub float %x.i, %y.i @@ -180,7 +181,7 @@ entry: } ; Function Attrs: nounwind -define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #2 { +define hidden { float, float } @main.complexMul(float %x.r, float %x.i, float %y.r, float %y.i, ptr %context) unnamed_addr #1 { entry: %0 = fmul float %x.r, %y.r %1 = fmul float %x.i, %y.i @@ -194,19 +195,19 @@ entry: } ; Function Attrs: nounwind -define hidden void @main.foo(ptr %context) unnamed_addr #2 { +define hidden void @main.foo(ptr %context) unnamed_addr #1 { entry: call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef) ret void } ; Function Attrs: nounwind -define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #2 { +define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 { entry: ret void } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #3 = { nounwind } diff --git a/compiler/testdata/channel.ll b/compiler/testdata/channel.ll index 65e18dea85..3333ef6239 100644 --- a/compiler/testdata/channel.ll +++ b/compiler/testdata/channel.ll @@ -9,16 +9,17 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden void @main.chanIntSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 { +define hidden void @main.chanIntSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { entry: %chan.blockedList = alloca %runtime.channelBlockedList, align 8 %chan.value = alloca i32, align 4 @@ -32,15 +33,15 @@ entry: } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) -declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3 +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #2 -declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1 +declare void @runtime.chanSend(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #3 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) -declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3 +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #2 ; Function Attrs: nounwind -define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 { +define hidden void @main.chanIntRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { entry: %chan.blockedList = alloca %runtime.channelBlockedList, align 8 %chan.value = alloca i32, align 4 @@ -52,10 +53,10 @@ entry: ret void } -declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #1 +declare i1 @runtime.chanRecv(ptr dereferenceable_or_null(32), ptr, ptr dereferenceable_or_null(24), ptr) #3 ; Function Attrs: nounwind -define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 { +define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { entry: %chan.blockedList = alloca %runtime.channelBlockedList, align 8 call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) @@ -65,7 +66,7 @@ entry: } ; Function Attrs: nounwind -define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 { +define hidden void @main.chanZeroRecv(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { entry: %chan.blockedList = alloca %runtime.channelBlockedList, align 8 call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) @@ -75,7 +76,7 @@ entry: } ; Function Attrs: nounwind -define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(32) %ch1, ptr dereferenceable_or_null(32) %ch2, ptr %context) unnamed_addr #2 { +define hidden void @main.selectZeroRecv(ptr dereferenceable_or_null(32) %ch1, ptr dereferenceable_or_null(32) %ch2, ptr %context) unnamed_addr #1 { entry: %select.states.alloca = alloca [2 x %runtime.chanSelectState], align 8 %select.send.value = alloca i32, align 4 @@ -105,10 +106,10 @@ select.body: ; preds = %select.next br label %select.done } -declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #1 +declare { i32, i1 } @runtime.tryChanSelect(ptr, ptr, i32, i32, ptr) #3 -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #4 = { nounwind } diff --git a/compiler/testdata/defer-cortex-m-qemu.ll b/compiler/testdata/defer-cortex-m-qemu.ll index 52a3bfbabf..076edd6431 100644 --- a/compiler/testdata/defer-cortex-m-qemu.ll +++ b/compiler/testdata/defer-cortex-m-qemu.ll @@ -3,7 +3,7 @@ source_filename = "defer.go" target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" target triple = "thumbv7m-unknown-unknown-eabi" -%runtime.deferFrame = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface } +%runtime.deferFrameInlineAsm = type { ptr, ptr, [0 x ptr], ptr, i1, %runtime._interface } %runtime._interface = type { ptr, ptr } %runtime._defer = type { i32, ptr } @@ -24,9 +24,9 @@ entry: %defer.alloca = alloca { i32, ptr }, align 4 %deferPtr = alloca ptr, align 4 store ptr null, ptr %deferPtr, align 4 - %deferframe.buf = alloca %runtime.deferFrame, align 4 + %deferframe.buf = alloca %runtime.deferFrameInlineAsm, align 4 %0 = call ptr @llvm.stacksave.p0() - call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4 + call void @runtime.setupDeferFrameInlineAsm(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4 store i32 0, ptr %defer.alloca, align 4 %defer.alloca.repack15 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1 store ptr null, ptr %defer.alloca.repack15, align 4 @@ -40,7 +40,7 @@ entry: br label %rundefers.block rundefers.after: ; preds = %rundefers.end - call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4 + call void @runtime.destroyDeferFrameInlineAsm(ptr nonnull %deferframe.buf, ptr undef) #4 ret void rundefers.block: ; preds = %1 @@ -76,7 +76,7 @@ rundefers.end: ; preds = %rundefers.loophead br label %rundefers.after recover: ; preds = %rundefers.end3 - call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4 + call void @runtime.destroyDeferFrameInlineAsm(ptr nonnull %deferframe.buf, ptr undef) #4 ret void lpad: ; preds = %rundefers.callback012, %rundefers.callback0, %entry @@ -115,9 +115,9 @@ rundefers.end3: ; preds = %rundefers.loophead6 ; Function Attrs: nocallback nofree nosync nounwind willreturn declare ptr @llvm.stacksave.p0() #3 -declare void @runtime.setupDeferFrame(ptr dereferenceable_or_null(24), ptr, ptr) #2 +declare void @runtime.setupDeferFrameInlineAsm(ptr dereferenceable_or_null(24), ptr, ptr) #2 -declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2 +declare void @runtime.destroyDeferFrameInlineAsm(ptr dereferenceable_or_null(24), ptr) #2 ; Function Attrs: nounwind define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 { @@ -135,9 +135,9 @@ entry: %defer.alloca = alloca { i32, ptr }, align 4 %deferPtr = alloca ptr, align 4 store ptr null, ptr %deferPtr, align 4 - %deferframe.buf = alloca %runtime.deferFrame, align 4 + %deferframe.buf = alloca %runtime.deferFrameInlineAsm, align 4 %0 = call ptr @llvm.stacksave.p0() - call void @runtime.setupDeferFrame(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4 + call void @runtime.setupDeferFrameInlineAsm(ptr nonnull %deferframe.buf, ptr %0, ptr undef) #4 store i32 0, ptr %defer.alloca, align 4 %defer.alloca.repack22 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1 store ptr null, ptr %defer.alloca.repack22, align 4 @@ -155,7 +155,7 @@ entry: br label %rundefers.block rundefers.after: ; preds = %rundefers.end - call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4 + call void @runtime.destroyDeferFrameInlineAsm(ptr nonnull %deferframe.buf, ptr undef) #4 ret void rundefers.block: ; preds = %1 @@ -201,7 +201,7 @@ rundefers.end: ; preds = %rundefers.loophead br label %rundefers.after recover: ; preds = %rundefers.end7 - call void @runtime.destroyDeferFrame(ptr nonnull %deferframe.buf, ptr undef) #4 + call void @runtime.destroyDeferFrameInlineAsm(ptr nonnull %deferframe.buf, ptr undef) #4 ret void lpad: ; preds = %rundefers.callback119, %rundefers.callback016, %rundefers.callback1, %rundefers.callback0, %entry diff --git a/compiler/testdata/defer-wasm-none.ll b/compiler/testdata/defer-wasm-none.ll new file mode 100644 index 0000000000..e5987d3675 --- /dev/null +++ b/compiler/testdata/defer-wasm-none.ll @@ -0,0 +1,273 @@ +; ModuleID = 'defer.go' +source_filename = "defer.go" +target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20" +target triple = "wasm32-unknown-wasi" + +%runtime.deferFrameWasmEH = type { ptr, i1, %runtime._interface } +%runtime._interface = type { ptr, ptr } +%runtime._defer = type { i32, ptr } + +; Function Attrs: allockind("alloc,zeroed") allocsize(0) +declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 + +; Function Attrs: nounwind +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 + +define hidden void @main.init(ptr %context) unnamed_addr #2 { +entry: + ret void +} + +declare void @main.external(ptr) #2 + +define hidden void @main.deferSimple(ptr %context) unnamed_addr #2 personality ptr @__gxx_wasm_personality_v0 { +entry: + %defer.alloca = alloca { i32, ptr }, align 8 + %deferPtr = alloca ptr, align 4 + store ptr null, ptr %deferPtr, align 4 + %deferframe.buf = alloca %runtime.deferFrameWasmEH, align 8 + call void @runtime.setupDeferFrameWasmEH(ptr nonnull %deferframe.buf, ptr undef) + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr nonnull %defer.alloca, ptr nonnull %stackalloc, ptr undef) + store i32 0, ptr %defer.alloca, align 4 + %defer.alloca.repack13 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1 + store ptr null, ptr %defer.alloca.repack13, align 4 + store ptr %defer.alloca, ptr %deferPtr, align 4 + invoke void @main.external(ptr undef) + to label %invoke.cont unwind label %catch.dispatch + +invoke.cont: ; preds = %entry + br label %rundefers.block + +rundefers.after: ; preds = %rundefers.end + call void @runtime.destroyDeferFrameWasmEH(ptr nonnull %deferframe.buf, ptr undef) + ret void + +rundefers.block: ; preds = %invoke.cont + br label %rundefers.loophead + +rundefers.loophead: ; preds = %rundefers.catch.start, %rundefers.callback0, %rundefers.block + %0 = load ptr, ptr %deferPtr, align 4 + %stackIsNil = icmp eq ptr %0, null + br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop + +rundefers.loop: ; preds = %rundefers.loophead + %stack.next.gep = getelementptr inbounds %runtime._defer, ptr %0, i32 0, i32 1 + %stack.next = load ptr, ptr %stack.next.gep, align 4 + store ptr %stack.next, ptr %deferPtr, align 4 + %callback = load i32, ptr %0, align 4 + switch i32 %callback, label %rundefers.default [ + i32 0, label %rundefers.callback0 + ] + +rundefers.callback0: ; preds = %rundefers.loop + invoke void @"main.deferSimple$1"(ptr undef) + to label %rundefers.loophead unwind label %rundefers.catch.dispatch + +rundefers.default: ; preds = %rundefers.loop + unreachable + +rundefers.catch.dispatch: ; preds = %rundefers.callback0 + %1 = catchswitch within none [label %rundefers.catch.start] unwind to caller + +rundefers.catch.start: ; preds = %rundefers.catch.dispatch + %2 = catchpad within %1 [ptr null] + catchret from %2 to label %rundefers.loophead + +rundefers.end: ; preds = %rundefers.loophead + br label %rundefers.after + +recover: ; preds = %rundefers.end1 + call void @runtime.destroyDeferFrameWasmEH(ptr nonnull %deferframe.buf, ptr undef) + ret void + +catch.dispatch: ; preds = %entry + %3 = catchswitch within none [label %catch.start] unwind to caller + +catch.start: ; preds = %catch.dispatch + %4 = catchpad within %3 [ptr null] + catchret from %4 to label %rundefers + +rundefers: ; preds = %catch.start + br label %rundefers.loophead4 + +rundefers.loophead4: ; preds = %rundefers.catch.start6, %rundefers.callback012, %rundefers + %5 = load ptr, ptr %deferPtr, align 4 + %stackIsNil7 = icmp eq ptr %5, null + br i1 %stackIsNil7, label %rundefers.end1, label %rundefers.loop3 + +rundefers.loop3: ; preds = %rundefers.loophead4 + %stack.next.gep8 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1 + %stack.next9 = load ptr, ptr %stack.next.gep8, align 4 + store ptr %stack.next9, ptr %deferPtr, align 4 + %callback11 = load i32, ptr %5, align 4 + switch i32 %callback11, label %rundefers.default2 [ + i32 0, label %rundefers.callback012 + ] + +rundefers.callback012: ; preds = %rundefers.loop3 + invoke void @"main.deferSimple$1"(ptr undef) + to label %rundefers.loophead4 unwind label %rundefers.catch.dispatch5 + +rundefers.default2: ; preds = %rundefers.loop3 + unreachable + +rundefers.catch.dispatch5: ; preds = %rundefers.callback012 + %6 = catchswitch within none [label %rundefers.catch.start6] unwind to caller + +rundefers.catch.start6: ; preds = %rundefers.catch.dispatch5 + %7 = catchpad within %6 [ptr null] + catchret from %7 to label %rundefers.loophead4 + +rundefers.end1: ; preds = %rundefers.loophead4 + br label %recover +} + +declare i32 @__gxx_wasm_personality_v0(...) + +declare void @runtime.setupDeferFrameWasmEH(ptr dereferenceable_or_null(16), ptr) #2 + +declare void @runtime.destroyDeferFrameWasmEH(ptr dereferenceable_or_null(16), ptr) #2 + +define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #2 { +entry: + call void @runtime.printint32(i32 3, ptr undef) + ret void +} + +declare void @runtime.printint32(i32, ptr) #2 + +define hidden void @main.deferMultiple(ptr %context) unnamed_addr #2 personality ptr @__gxx_wasm_personality_v0 { +entry: + %defer.alloca2 = alloca { i32, ptr }, align 8 + %defer.alloca = alloca { i32, ptr }, align 8 + %deferPtr = alloca ptr, align 4 + store ptr null, ptr %deferPtr, align 4 + %deferframe.buf = alloca %runtime.deferFrameWasmEH, align 8 + call void @runtime.setupDeferFrameWasmEH(ptr nonnull %deferframe.buf, ptr undef) + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr nonnull %defer.alloca, ptr nonnull %stackalloc, ptr undef) + store i32 0, ptr %defer.alloca, align 4 + %defer.alloca.repack16 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca, i32 0, i32 1 + store ptr null, ptr %defer.alloca.repack16, align 4 + store ptr %defer.alloca, ptr %deferPtr, align 4 + call void @runtime.trackPointer(ptr nonnull %defer.alloca2, ptr nonnull %stackalloc, ptr undef) + store i32 1, ptr %defer.alloca2, align 4 + %defer.alloca2.repack17 = getelementptr inbounds { i32, ptr }, ptr %defer.alloca2, i32 0, i32 1 + store ptr %defer.alloca, ptr %defer.alloca2.repack17, align 4 + store ptr %defer.alloca2, ptr %deferPtr, align 4 + invoke void @main.external(ptr undef) + to label %invoke.cont unwind label %catch.dispatch + +invoke.cont: ; preds = %entry + br label %rundefers.block + +rundefers.after: ; preds = %rundefers.end + call void @runtime.destroyDeferFrameWasmEH(ptr nonnull %deferframe.buf, ptr undef) + ret void + +rundefers.block: ; preds = %invoke.cont + br label %rundefers.loophead + +rundefers.loophead: ; preds = %rundefers.catch.start, %rundefers.callback1, %rundefers.callback0, %rundefers.block + %0 = load ptr, ptr %deferPtr, align 4 + %stackIsNil = icmp eq ptr %0, null + br i1 %stackIsNil, label %rundefers.end, label %rundefers.loop + +rundefers.loop: ; preds = %rundefers.loophead + %stack.next.gep = getelementptr inbounds %runtime._defer, ptr %0, i32 0, i32 1 + %stack.next = load ptr, ptr %stack.next.gep, align 4 + store ptr %stack.next, ptr %deferPtr, align 4 + %callback = load i32, ptr %0, align 4 + switch i32 %callback, label %rundefers.default [ + i32 0, label %rundefers.callback0 + i32 1, label %rundefers.callback1 + ] + +rundefers.callback0: ; preds = %rundefers.loop + invoke void @"main.deferMultiple$1"(ptr undef) + to label %rundefers.loophead unwind label %rundefers.catch.dispatch + +rundefers.callback1: ; preds = %rundefers.loop + invoke void @"main.deferMultiple$2"(ptr undef) + to label %rundefers.loophead unwind label %rundefers.catch.dispatch + +rundefers.default: ; preds = %rundefers.loop + unreachable + +rundefers.catch.dispatch: ; preds = %rundefers.callback1, %rundefers.callback0 + %1 = catchswitch within none [label %rundefers.catch.start] unwind to caller + +rundefers.catch.start: ; preds = %rundefers.catch.dispatch + %2 = catchpad within %1 [ptr null] + catchret from %2 to label %rundefers.loophead + +rundefers.end: ; preds = %rundefers.loophead + br label %rundefers.after + +recover: ; preds = %rundefers.end3 + call void @runtime.destroyDeferFrameWasmEH(ptr nonnull %deferframe.buf, ptr undef) + ret void + +catch.dispatch: ; preds = %entry + %3 = catchswitch within none [label %catch.start] unwind to caller + +catch.start: ; preds = %catch.dispatch + %4 = catchpad within %3 [ptr null] + catchret from %4 to label %rundefers + +rundefers: ; preds = %catch.start + br label %rundefers.loophead6 + +rundefers.loophead6: ; preds = %rundefers.catch.start8, %rundefers.callback115, %rundefers.callback014, %rundefers + %5 = load ptr, ptr %deferPtr, align 4 + %stackIsNil9 = icmp eq ptr %5, null + br i1 %stackIsNil9, label %rundefers.end3, label %rundefers.loop5 + +rundefers.loop5: ; preds = %rundefers.loophead6 + %stack.next.gep10 = getelementptr inbounds %runtime._defer, ptr %5, i32 0, i32 1 + %stack.next11 = load ptr, ptr %stack.next.gep10, align 4 + store ptr %stack.next11, ptr %deferPtr, align 4 + %callback13 = load i32, ptr %5, align 4 + switch i32 %callback13, label %rundefers.default4 [ + i32 0, label %rundefers.callback014 + i32 1, label %rundefers.callback115 + ] + +rundefers.callback014: ; preds = %rundefers.loop5 + invoke void @"main.deferMultiple$1"(ptr undef) + to label %rundefers.loophead6 unwind label %rundefers.catch.dispatch7 + +rundefers.callback115: ; preds = %rundefers.loop5 + invoke void @"main.deferMultiple$2"(ptr undef) + to label %rundefers.loophead6 unwind label %rundefers.catch.dispatch7 + +rundefers.default4: ; preds = %rundefers.loop5 + unreachable + +rundefers.catch.dispatch7: ; preds = %rundefers.callback115, %rundefers.callback014 + %6 = catchswitch within none [label %rundefers.catch.start8] unwind to caller + +rundefers.catch.start8: ; preds = %rundefers.catch.dispatch7 + %7 = catchpad within %6 [ptr null] + catchret from %7 to label %rundefers.loophead6 + +rundefers.end3: ; preds = %rundefers.loophead6 + br label %recover +} + +define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #2 { +entry: + call void @runtime.printint32(i32 3, ptr undef) + ret void +} + +define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #2 { +entry: + call void @runtime.printint32(i32 5, ptr undef) + ret void +} + +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } diff --git a/compiler/testdata/float.ll b/compiler/testdata/float.ll index 735ab19768..c778e70751 100644 --- a/compiler/testdata/float.ll +++ b/compiler/testdata/float.ll @@ -6,16 +6,17 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #2 { +define hidden i32 @main.f32tou32(float %v, ptr %context) unnamed_addr #1 { entry: %positive = fcmp oge float %v, 0.000000e+00 %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 @@ -27,25 +28,25 @@ entry: } ; Function Attrs: nounwind -define hidden float @main.maxu32f(ptr %context) unnamed_addr #2 { +define hidden float @main.maxu32f(ptr %context) unnamed_addr #1 { entry: ret float 0x41F0000000000000 } ; Function Attrs: nounwind -define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #2 { +define hidden i32 @main.maxu32tof32(ptr %context) unnamed_addr #1 { entry: ret i32 -1 } ; Function Attrs: nounwind -define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #2 { +define hidden { i32, i32, i32, i32 } @main.inftoi32(ptr %context) unnamed_addr #1 { entry: ret { i32, i32, i32, i32 } { i32 -1, i32 0, i32 2147483647, i32 -2147483648 } } ; Function Attrs: nounwind -define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #2 { +define hidden i32 @main.u32tof32tou32(i32 %v, ptr %context) unnamed_addr #1 { entry: %0 = uitofp i32 %v to float %withinmax = fcmp ole float %0, 0x41EFFFFFC0000000 @@ -55,7 +56,7 @@ entry: } ; Function Attrs: nounwind -define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #2 { +define hidden float @main.f32tou32tof32(float %v, ptr %context) unnamed_addr #1 { entry: %positive = fcmp oge float %v, 0.000000e+00 %withinmax = fcmp ole float %v, 0x41EFFFFFC0000000 @@ -68,7 +69,7 @@ entry: } ; Function Attrs: nounwind -define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #2 { +define hidden i8 @main.f32tou8(float %v, ptr %context) unnamed_addr #1 { entry: %positive = fcmp oge float %v, 0.000000e+00 %withinmax = fcmp ole float %v, 2.550000e+02 @@ -80,7 +81,7 @@ entry: } ; Function Attrs: nounwind -define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #2 { +define hidden i8 @main.f32toi8(float %v, ptr %context) unnamed_addr #1 { entry: %abovemin = fcmp oge float %v, -1.280000e+02 %belowmax = fcmp ole float %v, 1.270000e+02 @@ -93,6 +94,5 @@ entry: ret i8 %0 } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } diff --git a/compiler/testdata/func.ll b/compiler/testdata/func.ll index bec79bffc5..7591546440 100644 --- a/compiler/testdata/func.ll +++ b/compiler/testdata/func.ll @@ -6,16 +6,17 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #2 { +define hidden void @main.foo(ptr %callback.context, ptr %callback.funcptr, ptr %context) unnamed_addr #1 { entry: %0 = icmp eq ptr %callback.funcptr, null br i1 %0, label %fpcall.throw, label %fpcall.next @@ -29,22 +30,22 @@ fpcall.throw: ; preds = %entry unreachable } -declare void @runtime.nilPanic(ptr) #1 +declare void @runtime.nilPanic(ptr) #2 ; Function Attrs: nounwind -define hidden void @main.bar(ptr %context) unnamed_addr #2 { +define hidden void @main.bar(ptr %context) unnamed_addr #1 { entry: call void @main.foo(ptr undef, ptr nonnull @main.someFunc, ptr undef) ret void } ; Function Attrs: nounwind -define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #2 { +define hidden void @main.someFunc(i32 %arg0, ptr %context) unnamed_addr #1 { entry: ret void } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #3 = { nounwind } diff --git a/compiler/testdata/gc.ll b/compiler/testdata/gc.ll index 82260fbf45..988fc3d6f5 100644 --- a/compiler/testdata/gc.ll +++ b/compiler/testdata/gc.ll @@ -27,93 +27,94 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden void @main.newScalar(ptr %context) unnamed_addr #2 { +define hidden void @main.newScalar(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + %new = call align 1 dereferenceable(1) ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) store ptr %new, ptr @main.scalar1, align 4 - %new1 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 + %new1 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) store ptr %new1, ptr @main.scalar2, align 4 - %new2 = call align 8 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 + %new2 = call align 8 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) store ptr %new2, ptr @main.scalar3, align 4 - %new3 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3 + %new3 = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) store ptr %new3, ptr @main.scalar4, align 4 ret void } ; Function Attrs: nounwind -define hidden void @main.newArray(ptr %context) unnamed_addr #2 { +define hidden void @main.newArray(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + %new = call align 1 dereferenceable(3) ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) store ptr %new, ptr @main.array1, align 4 - %new1 = call align 1 dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 + %new1 = call align 1 dereferenceable(71) ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) store ptr %new1, ptr @main.array2, align 4 - %new2 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 + %new2 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) store ptr %new2, ptr @main.array3, align 4 ret void } ; Function Attrs: nounwind -define hidden void @main.newStruct(ptr %context) unnamed_addr #2 { +define hidden void @main.newStruct(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + %new = call align 1 ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) store ptr %new, ptr @main.struct1, align 4 - %new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3 + %new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) store ptr %new1, ptr @main.struct2, align 4 - %new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3 + %new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) store ptr %new2, ptr @main.struct3, align 4 - %new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3 + %new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) store ptr %new3, ptr @main.struct4, align 4 ret void } ; Function Attrs: nounwind -define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #2 { +define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #3 + %new = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) ret ptr %new } ; Function Attrs: nounwind -define hidden void @main.makeSlice(ptr %context) unnamed_addr #2 { +define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3 + %makeslice = call align 1 dereferenceable(5) ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) store ptr %makeslice, ptr @main.slice1, align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 1), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice1, i32 0, i32 2), align 4 - %makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #3 + %makeslice1 = call align 4 dereferenceable(20) ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) store ptr %makeslice1, ptr @main.slice2, align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 1), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice2, i32 0, i32 2), align 4 - %makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #3 + %makeslice3 = call align 4 dereferenceable(60) ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) store ptr %makeslice3, ptr @main.slice3, align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 1), align 4 store i32 5, ptr getelementptr inbounds ({ ptr, i32, i32 }, ptr @main.slice3, i32 0, i32 2), align 4 @@ -121,21 +122,20 @@ entry: } ; Function Attrs: nounwind -define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #2 { +define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - %0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3 + %0 = call align 8 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) store double %v.r, ptr %0, align 8 %.repack1 = getelementptr inbounds { double, double }, ptr %0, i32 0, i32 1 store double %v.i, ptr %.repack1, align 8 %1 = insertvalue %runtime._interface { ptr @"reflect/types.type:basic:complex128", ptr undef }, ptr %0, 1 - call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:complex128", ptr nonnull %stackalloc, ptr undef) + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) ret %runtime._interface %1 } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #3 = { nounwind } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { nounwind } diff --git a/compiler/testdata/go1.20.ll b/compiler/testdata/go1.20.ll index 6ef13fb4a8..583e87f018 100644 --- a/compiler/testdata/go1.20.ll +++ b/compiler/testdata/go1.20.ll @@ -8,24 +8,25 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 { +define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) ret ptr %s.data } ; Function Attrs: nounwind -define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 { +define hidden %runtime._string @main.unsafeString(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = icmp slt i16 %len, 0 @@ -39,7 +40,7 @@ unsafe.String.next: ; preds = %entry %5 = zext i16 %len to i32 %6 = insertvalue %runtime._string undef, ptr %ptr, 0 %7 = insertvalue %runtime._string %6, i32 %5, 1 - call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) ret %runtime._string %7 unsafe.String.throw: ; preds = %entry @@ -47,17 +48,17 @@ unsafe.String.throw: ; preds = %entry unreachable } -declare void @runtime.unsafeSlicePanic(ptr) #1 +declare void @runtime.unsafeSlicePanic(ptr) #2 ; Function Attrs: nounwind -define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 { +define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) ret ptr %s.data } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #3 = { nounwind } diff --git a/compiler/testdata/go1.21.ll b/compiler/testdata/go1.21.ll index d76ec6212c..89a5a4d170 100644 --- a/compiler/testdata/go1.21.ll +++ b/compiler/testdata/go1.21.ll @@ -8,29 +8,30 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #2 { +define hidden i32 @main.min1(i32 %a, ptr %context) unnamed_addr #1 { entry: ret i32 %a } ; Function Attrs: nounwind -define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { +define hidden i32 @main.min2(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { entry: %0 = call i32 @llvm.smin.i32(i32 %a, i32 %b) ret i32 %0 } ; Function Attrs: nounwind -define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #2 { +define hidden i32 @main.min3(i32 %a, i32 %b, i32 %c, ptr %context) unnamed_addr #1 { entry: %0 = call i32 @llvm.smin.i32(i32 %a, i32 %b) %1 = call i32 @llvm.smin.i32(i32 %0, i32 %c) @@ -38,7 +39,7 @@ entry: } ; Function Attrs: nounwind -define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #2 { +define hidden i32 @main.min4(i32 %a, i32 %b, i32 %c, i32 %d, ptr %context) unnamed_addr #1 { entry: %0 = call i32 @llvm.smin.i32(i32 %a, i32 %b) %1 = call i32 @llvm.smin.i32(i32 %0, i32 %c) @@ -47,21 +48,21 @@ entry: } ; Function Attrs: nounwind -define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #2 { +define hidden i8 @main.minUint8(i8 %a, i8 %b, ptr %context) unnamed_addr #1 { entry: %0 = call i8 @llvm.umin.i8(i8 %a, i8 %b) ret i8 %0 } ; Function Attrs: nounwind -define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { +define hidden i32 @main.minUnsigned(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { entry: %0 = call i32 @llvm.umin.i32(i32 %a, i32 %b) ret i32 %0 } ; Function Attrs: nounwind -define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #2 { +define hidden float @main.minFloat32(float %a, float %b, ptr %context) unnamed_addr #1 { entry: %0 = fcmp olt float %a, %b %1 = select i1 %0, float %a, float %b @@ -69,7 +70,7 @@ entry: } ; Function Attrs: nounwind -define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #2 { +define hidden double @main.minFloat64(double %a, double %b, ptr %context) unnamed_addr #1 { entry: %0 = fcmp olt double %a, %b %1 = select i1 %0, double %a, double %b @@ -77,7 +78,7 @@ entry: } ; Function Attrs: nounwind -define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 { +define hidden %runtime._string @main.minString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #1 { entry: %0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0 %1 = insertvalue %runtime._string %0, i32 %a.len, 1 @@ -87,28 +88,28 @@ entry: %4 = call i1 @runtime.stringLess(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr undef) #5 %5 = select i1 %4, %runtime._string %1, %runtime._string %3 %6 = extractvalue %runtime._string %5, 0 - call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5 + call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) ret %runtime._string %5 } -declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1 +declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #2 ; Function Attrs: nounwind -define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { +define hidden i32 @main.maxInt(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { entry: %0 = call i32 @llvm.smax.i32(i32 %a, i32 %b) ret i32 %0 } ; Function Attrs: nounwind -define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #2 { +define hidden i32 @main.maxUint(i32 %a, i32 %b, ptr %context) unnamed_addr #1 { entry: %0 = call i32 @llvm.umax.i32(i32 %a, i32 %b) ret i32 %0 } ; Function Attrs: nounwind -define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #2 { +define hidden float @main.maxFloat32(float %a, float %b, ptr %context) unnamed_addr #1 { entry: %0 = fcmp ogt float %a, %b %1 = select i1 %0, float %a, float %b @@ -116,7 +117,7 @@ entry: } ; Function Attrs: nounwind -define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #2 { +define hidden %runtime._string @main.maxString(ptr %a.data, i32 %a.len, ptr %b.data, i32 %b.len, ptr %context) unnamed_addr #1 { entry: %0 = insertvalue %runtime._string zeroinitializer, ptr %a.data, 0 %1 = insertvalue %runtime._string %0, i32 %a.len, 1 @@ -126,12 +127,12 @@ entry: %4 = call i1 @runtime.stringLess(ptr %b.data, i32 %b.len, ptr %a.data, i32 %a.len, ptr undef) #5 %5 = select i1 %4, %runtime._string %1, %runtime._string %3 %6 = extractvalue %runtime._string %5, 0 - call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) #5 + call void @runtime.trackPointer(ptr %6, ptr nonnull %stackalloc, ptr undef) ret %runtime._string %5 } ; Function Attrs: nounwind -define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 { +define hidden void @main.clearSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 { entry: %0 = shl i32 %s.len, 2 call void @llvm.memset.p0.i32(ptr align 4 %s.data, i8 0, i32 %0, i1 false) @@ -142,19 +143,19 @@ entry: declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #3 ; Function Attrs: nounwind -define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 { +define hidden void @main.clearZeroSizedSlice(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 { +define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #1 { entry: call void @runtime.hashmapClear(ptr %m, ptr undef) #5 ret void } -declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1 +declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #2 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare i32 @llvm.smin.i32(i32, i32) #4 @@ -171,9 +172,9 @@ declare i32 @llvm.smax.i32(i32, i32) #4 ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) declare i32 @llvm.umax.i32(i32, i32) #4 -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #3 = { nocallback nofree nounwind willreturn memory(argmem: write) } attributes #4 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } attributes #5 = { nounwind } diff --git a/compiler/testdata/goroutine-wasm-asyncify.ll b/compiler/testdata/goroutine-wasm-asyncify.ll index 1281857d31..ae8e54f939 100644 --- a/compiler/testdata/goroutine-wasm-asyncify.ll +++ b/compiler/testdata/goroutine-wasm-asyncify.ll @@ -10,24 +10,25 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #2 { +define hidden void @main.regularFunctionGoroutine(ptr %context) unnamed_addr #1 { entry: call void @"internal/task.start"(i32 ptrtoint (ptr @"main.regularFunction$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9 ret void } -declare void @main.regularFunction(i32, ptr) #1 +declare void @main.regularFunction(i32, ptr) #2 -declare void @runtime.deadlock(ptr) #1 +declare void @runtime.deadlock(ptr) #2 ; Function Attrs: nounwind define linkonce_odr void @"main.regularFunction$gowrapper"(ptr %0) unnamed_addr #3 { @@ -38,17 +39,17 @@ entry: unreachable } -declare void @"internal/task.start"(i32, ptr, i32, ptr) #1 +declare void @"internal/task.start"(i32, ptr, i32, ptr) #2 ; Function Attrs: nounwind -define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #2 { +define hidden void @main.inlineFunctionGoroutine(ptr %context) unnamed_addr #1 { entry: call void @"internal/task.start"(i32 ptrtoint (ptr @"main.inlineFunctionGoroutine$1$gowrapper" to i32), ptr nonnull inttoptr (i32 5 to ptr), i32 65536, ptr undef) #9 ret void } ; Function Attrs: nounwind -define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 { +define internal void @"main.inlineFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 { entry: ret void } @@ -63,16 +64,16 @@ entry: } ; Function Attrs: nounwind -define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #2 { +define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %n = call align 4 dereferenceable(4) ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9 - call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9 + call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) store i32 3, ptr %n, align 4 - call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #9 - call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #9 + call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) + call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) %0 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr null, ptr undef) #9 - call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) store i32 5, ptr %0, align 4 %1 = getelementptr inbounds { i32, ptr }, ptr %0, i32 0, i32 1 store ptr %n, ptr %1, align 4 @@ -83,7 +84,7 @@ entry: } ; Function Attrs: nounwind -define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #2 { +define internal void @"main.closureFunctionGoroutine$1"(i32 %x, ptr %context) unnamed_addr #1 { entry: store i32 7, ptr %context, align 4 ret void @@ -100,14 +101,14 @@ entry: unreachable } -declare void @runtime.printint32(i32, ptr) #1 +declare void @runtime.printint32(i32, ptr) #2 ; Function Attrs: nounwind -define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 { +define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr null, ptr undef) #9 - call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) store i32 5, ptr %0, align 4 %1 = getelementptr inbounds { i32, ptr, ptr }, ptr %0, i32 0, i32 1 store ptr %fn.context, ptr %1, align 4 @@ -131,35 +132,35 @@ entry: } ; Function Attrs: nounwind -define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #2 { +define hidden void @main.recoverBuiltinGoroutine(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 { +define hidden void @main.copyBuiltinGoroutine(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 { entry: %copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 1, ptr undef) #9 ret void } -declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1 +declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2 ; Function Attrs: nounwind -define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #2 { +define hidden void @main.closeBuiltinGoroutine(ptr dereferenceable_or_null(32) %ch, ptr %context) unnamed_addr #1 { entry: call void @runtime.chanClose(ptr %ch, ptr undef) #9 ret void } -declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #1 +declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #2 ; Function Attrs: nounwind -define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { +define hidden void @main.startInterfaceMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = call align 4 dereferenceable(16) ptr @runtime.alloc(i32 16, ptr null, ptr undef) #9 - call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #9 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) store ptr %itf.value, ptr %0, align 4 %1 = getelementptr inbounds { ptr, %runtime._string, ptr }, ptr %0, i32 0, i32 1 store ptr @"main$string", ptr %1, align 4 @@ -188,13 +189,13 @@ entry: unreachable } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" } -attributes #4 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } -attributes #5 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } -attributes #6 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" } -attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } -attributes #8 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #3 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.regularFunction" } +attributes #4 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.inlineFunctionGoroutine$1" } +attributes #5 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="main.closureFunctionGoroutine$1" } +attributes #6 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper" } +attributes #7 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Print(string)" "tinygo-methods"="reflect/methods.Print(string)" } +attributes #8 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-gowrapper"="interface:{Print:func:{basic:string}{}}.Print$invoke" } attributes #9 = { nounwind } diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index 801f370d58..632306a76b 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -20,52 +20,53 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 { +define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7 - call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null } } ; Function Attrs: nounwind -define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 { +define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7 - call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null } } ; Function Attrs: nounwind -define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 { +define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7 - call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null } } ; Function Attrs: nounwind -define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 { +define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7 - call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null } } ; Function Attrs: nounwind -define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { +define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { entry: %typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7 br i1 %typecode, label %typeassert.ok, label %typeassert.next @@ -77,10 +78,10 @@ typeassert.ok: ; preds = %entry br label %typeassert.next } -declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1 +declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #2 ; Function Attrs: nounwind -define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { +define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { entry: %0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7 br i1 %0, label %typeassert.ok, label %typeassert.next @@ -95,7 +96,7 @@ typeassert.ok: ; preds = %entry declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3 ; Function Attrs: nounwind -define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { +define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { entry: %0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7 br i1 %0, label %typeassert.ok, label %typeassert.next @@ -110,7 +111,7 @@ typeassert.ok: ; preds = %entry declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4 ; Function Attrs: nounwind -define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { +define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { entry: %0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7 ret i8 %0 @@ -119,22 +120,22 @@ entry: declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5 ; Function Attrs: nounwind -define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 { +define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7 %1 = extractvalue %runtime._string %0, 0 - call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) ret %runtime._string %0 } declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6 -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" } -attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" } -attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" } -attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #3 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" } +attributes #4 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" } +attributes #5 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" } +attributes #6 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" } attributes #7 = { nounwind } diff --git a/compiler/testdata/pointer.ll b/compiler/testdata/pointer.ll index a659b21744..0816cf50fe 100644 --- a/compiler/testdata/pointer.ll +++ b/compiler/testdata/pointer.ll @@ -6,45 +6,44 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #2 { +define hidden [0 x i32] @main.pointerDerefZero(ptr %x, ptr %context) unnamed_addr #1 { entry: ret [0 x i32] zeroinitializer } ; Function Attrs: nounwind -define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #2 { +define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) ret ptr %x } ; Function Attrs: nounwind -define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #2 { +define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) ret ptr %x } ; Function Attrs: nounwind -define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #2 { +define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 - call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) ret ptr %x } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #3 = { nounwind } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index 3ee4078f1a..57e2298527 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -14,27 +14,28 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define void @extern_func() #3 { +define void @extern_func() #2 { entry: ret void } ; Function Attrs: nounwind -define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #2 { +define hidden void @somepkg.someFunction1(ptr %context) unnamed_addr #1 { entry: ret void } -declare void @somepkg.someFunction2(ptr) #1 +declare void @somepkg.someFunction2(ptr) #3 ; Function Attrs: inlinehint nounwind define hidden void @main.inlineFunc(ptr %context) unnamed_addr #4 { @@ -70,15 +71,15 @@ entry: ret void } -declare void @main.undefinedFunctionNotInSection(ptr) #1 - -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #3 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" } -attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" } -attributes #7 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" } -attributes #8 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="foobar" "wasm-import-name"="imported" } -attributes #9 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" } +declare void @main.undefinedFunctionNotInSection(ptr) #3 + +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="extern_func" } +attributes #3 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #4 = { inlinehint nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #5 = { noinline nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #6 = { noinline nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exportedFunctionInSection" } +attributes #7 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="modulename" "wasm-import-name"="import1" } +attributes #8 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-import-module"="foobar" "wasm-import-name"="imported" } +attributes #9 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" "wasm-export-name"="exported" } diff --git a/compiler/testdata/slice.ll b/compiler/testdata/slice.ll index 092dabbff4..b19486c81a 100644 --- a/compiler/testdata/slice.ll +++ b/compiler/testdata/slice.ll @@ -6,28 +6,29 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 { +define hidden i32 @main.sliceLen(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 { entry: ret i32 %ints.len } ; Function Attrs: nounwind -define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 { +define hidden i32 @main.sliceCap(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 { entry: ret i32 %ints.cap } ; Function Attrs: nounwind -define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #2 { +define hidden i32 @main.sliceElement(ptr %ints.data, i32 %ints.len, i32 %ints.cap, i32 %index, ptr %context) unnamed_addr #1 { entry: %.not = icmp ult i32 %index, %ints.len br i1 %.not, label %lookup.next, label %lookup.throw @@ -42,14 +43,14 @@ lookup.throw: ; preds = %entry unreachable } -declare void @runtime.lookupPanic(ptr) #1 +declare void @runtime.lookupPanic(ptr) #2 ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.sliceAppendValues(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %varargs = call align 4 dereferenceable(12) ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) store i32 1, ptr %varargs, align 4 %0 = getelementptr inbounds [3 x i32], ptr %varargs, i32 0, i32 1 store i32 2, ptr %0, align 4 @@ -62,14 +63,14 @@ entry: %2 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0 %3 = insertvalue { ptr, i32, i32 } %2, i32 %append.newLen, 1 %4 = insertvalue { ptr, i32, i32 } %3, i32 %append.newCap, 2 - call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %4 } -declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr) #1 +declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, i32, i32, i32, ptr) #2 ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.sliceAppendSlice(ptr %ints.data, i32 %ints.len, i32 %ints.cap, ptr %added.data, i32 %added.len, i32 %added.cap, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %append.new = call { ptr, i32, i32 } @runtime.sliceAppend(ptr %ints.data, ptr %added.data, i32 %ints.len, i32 %ints.cap, i32 %added.len, i32 4, ptr undef) #3 @@ -79,21 +80,21 @@ entry: %0 = insertvalue { ptr, i32, i32 } undef, ptr %append.newPtr, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %append.newLen, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %append.newCap, 2 - call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %2 } ; Function Attrs: nounwind -define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #2 { +define hidden i32 @main.sliceCopy(ptr %dst.data, i32 %dst.len, i32 %dst.cap, ptr %src.data, i32 %src.len, i32 %src.cap, ptr %context) unnamed_addr #1 { entry: %copy.n = call i32 @runtime.sliceCopy(ptr %dst.data, ptr %src.data, i32 %dst.len, i32 %src.len, i32 4, ptr undef) #3 ret i32 %copy.n } -declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #1 +declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, i32, i32, i32, ptr) #2 ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.makeByteSlice(i32 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %slice.maxcap = icmp slt i32 %len, 0 @@ -104,7 +105,7 @@ slice.next: ; preds = %entry %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 - call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -112,10 +113,10 @@ slice.throw: ; preds = %entry unreachable } -declare void @runtime.slicePanic(ptr) #1 +declare void @runtime.slicePanic(ptr) #2 ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.makeInt16Slice(i32 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %slice.maxcap = icmp slt i32 %len, 0 @@ -127,7 +128,7 @@ slice.next: ; preds = %entry %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 - call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -136,7 +137,7 @@ slice.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.makeArraySlice(i32 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %slice.maxcap = icmp ugt i32 %len, 1431655765 @@ -148,7 +149,7 @@ slice.next: ; preds = %entry %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 - call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -157,7 +158,7 @@ slice.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.makeInt32Slice(i32 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %slice.maxcap = icmp ugt i32 %len, 1073741823 @@ -169,7 +170,7 @@ slice.next: ; preds = %entry %0 = insertvalue { ptr, i32, i32 } undef, ptr %makeslice.buf, 0 %1 = insertvalue { ptr, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { ptr, i32, i32 } %1, i32 %len, 2 - call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -178,26 +179,26 @@ slice.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #2 { +define hidden ptr @main.Add32(ptr %p, i32 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = getelementptr i8, ptr %p, i32 %len - call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) ret ptr %0 } ; Function Attrs: nounwind -define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #2 { +define hidden ptr @main.Add64(ptr %p, i64 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = trunc i64 %len to i32 %1 = getelementptr i8, ptr %p, i32 %0 - call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) ret ptr %1 } ; Function Attrs: nounwind -define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #2 { +define hidden ptr @main.SliceToArray(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 { entry: %0 = icmp ult i32 %s.len, 4 br i1 %0, label %slicetoarray.throw, label %slicetoarray.next @@ -210,14 +211,14 @@ slicetoarray.throw: ; preds = %entry unreachable } -declare void @runtime.sliceToArrayPointerPanic(ptr) #1 +declare void @runtime.sliceToArrayPointerPanic(ptr) #2 ; Function Attrs: nounwind -define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #2 { +define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %makeslice = call align 4 dereferenceable(24) ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3 - call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) br i1 false, label %slicetoarray.throw, label %slicetoarray.next slicetoarray.next: ; preds = %entry @@ -228,7 +229,7 @@ slicetoarray.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.SliceInt(ptr dereferenceable_or_null(4) %ptr, i32 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = icmp ugt i32 %len, 1073741823 @@ -242,7 +243,7 @@ unsafe.Slice.next: ; preds = %entry %5 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %6 = insertvalue { ptr, i32, i32 } %5, i32 %len, 1 %7 = insertvalue { ptr, i32, i32 } %6, i32 %len, 2 - call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %7 unsafe.Slice.throw: ; preds = %entry @@ -250,10 +251,10 @@ unsafe.Slice.throw: ; preds = %entry unreachable } -declare void @runtime.unsafeSlicePanic(ptr) #1 +declare void @runtime.unsafeSlicePanic(ptr) #2 ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.SliceUint16(ptr dereferenceable_or_null(1) %ptr, i16 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = icmp eq ptr %ptr, null @@ -266,7 +267,7 @@ unsafe.Slice.next: ; preds = %entry %4 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %5 = insertvalue { ptr, i32, i32 } %4, i32 %3, 1 %6 = insertvalue { ptr, i32, i32 } %5, i32 %3, 2 - call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %6 unsafe.Slice.throw: ; preds = %entry @@ -275,7 +276,7 @@ unsafe.Slice.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.SliceUint64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = icmp ugt i64 %len, 1073741823 @@ -290,7 +291,7 @@ unsafe.Slice.next: ; preds = %entry %6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1 %8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2 - call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %8 unsafe.Slice.throw: ; preds = %entry @@ -299,7 +300,7 @@ unsafe.Slice.throw: ; preds = %entry } ; Function Attrs: nounwind -define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #2 { +define hidden { ptr, i32, i32 } @main.SliceInt64(ptr dereferenceable_or_null(4) %ptr, i64 %len, ptr %context) unnamed_addr #1 { entry: %stackalloc = alloca i8, align 1 %0 = icmp ugt i64 %len, 1073741823 @@ -314,7 +315,7 @@ unsafe.Slice.next: ; preds = %entry %6 = insertvalue { ptr, i32, i32 } undef, ptr %ptr, 0 %7 = insertvalue { ptr, i32, i32 } %6, i32 %5, 1 %8 = insertvalue { ptr, i32, i32 } %7, i32 %5, 2 - call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #3 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) ret { ptr, i32, i32 } %8 unsafe.Slice.throw: ; preds = %entry @@ -322,7 +323,7 @@ unsafe.Slice.throw: ; preds = %entry unreachable } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #3 = { nounwind } diff --git a/compiler/testdata/string.ll b/compiler/testdata/string.ll index 64582dd961..ebb7916fcd 100644 --- a/compiler/testdata/string.ll +++ b/compiler/testdata/string.ll @@ -10,34 +10,35 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: nounwind -define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #2 { +define hidden %runtime._string @main.someString(ptr %context) unnamed_addr #1 { entry: ret %runtime._string { ptr @"main$string", i32 3 } } ; Function Attrs: nounwind -define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #2 { +define hidden %runtime._string @main.zeroLengthString(ptr %context) unnamed_addr #1 { entry: ret %runtime._string zeroinitializer } ; Function Attrs: nounwind -define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #2 { +define hidden i32 @main.stringLen(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #1 { entry: ret i32 %s.len } ; Function Attrs: nounwind -define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #2 { +define hidden i8 @main.stringIndex(ptr %s.data, i32 %s.len, i32 %index, ptr %context) unnamed_addr #1 { entry: %.not = icmp ult i32 %index, %s.len br i1 %.not, label %lookup.next, label %lookup.throw @@ -52,19 +53,19 @@ lookup.throw: ; preds = %entry unreachable } -declare void @runtime.lookupPanic(ptr) #1 +declare void @runtime.lookupPanic(ptr) #2 ; Function Attrs: nounwind -define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 { +define hidden i1 @main.stringCompareEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 { entry: %0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3 ret i1 %0 } -declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #1 +declare i1 @runtime.stringEqual(ptr, i32, ptr, i32, ptr) #2 ; Function Attrs: nounwind -define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 { +define hidden i1 @main.stringCompareUnequal(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 { entry: %0 = call i1 @runtime.stringEqual(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr undef) #3 %1 = xor i1 %0, true @@ -72,16 +73,16 @@ entry: } ; Function Attrs: nounwind -define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #2 { +define hidden i1 @main.stringCompareLarger(ptr %s1.data, i32 %s1.len, ptr %s2.data, i32 %s2.len, ptr %context) unnamed_addr #1 { entry: %0 = call i1 @runtime.stringLess(ptr %s2.data, i32 %s2.len, ptr %s1.data, i32 %s1.len, ptr undef) #3 ret i1 %0 } -declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #1 +declare i1 @runtime.stringLess(ptr, i32, ptr, i32, ptr) #2 ; Function Attrs: nounwind -define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #2 { +define hidden i8 @main.stringLookup(ptr %s.data, i32 %s.len, i8 %x, ptr %context) unnamed_addr #1 { entry: %0 = zext i8 %x to i32 %.not = icmp ult i32 %0, %s.len @@ -97,7 +98,7 @@ lookup.throw: ; preds = %entry unreachable } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #3 = { nounwind } diff --git a/compiler/testdata/zeromap.ll b/compiler/testdata/zeromap.ll index 4ad263130a..036823b228 100644 --- a/compiler/testdata/zeromap.ll +++ b/compiler/testdata/zeromap.ll @@ -8,16 +8,17 @@ target triple = "wasm32-unknown-wasi" ; Function Attrs: allockind("alloc,zeroed") allocsize(0) declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 +; Function Attrs: nounwind declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #1 ; Function Attrs: nounwind -define hidden void @main.init(ptr %context) unnamed_addr #2 { +define hidden void @main.init(ptr %context) unnamed_addr #1 { entry: ret void } ; Function Attrs: noinline nounwind -define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 { +define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 { entry: %hashmap.key = alloca %main.hasPadding, align 8 %hashmap.value = alloca i32, align 4 @@ -39,17 +40,17 @@ entry: } ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) -declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #4 +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #3 -declare void @runtime.memzero(ptr, i32, ptr) #1 +declare void @runtime.memzero(ptr, i32, ptr) #4 -declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #1 +declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(40), ptr, ptr, i32, ptr) #4 ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) -declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #4 +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #3 ; Function Attrs: noinline nounwind -define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #3 { +define hidden void @main.testZeroSet(ptr dereferenceable_or_null(40) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 { entry: %hashmap.key = alloca %main.hasPadding, align 8 %hashmap.value = alloca i32, align 4 @@ -70,10 +71,10 @@ entry: ret void } -declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #1 +declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(40), ptr, ptr, ptr) #4 ; Function Attrs: noinline nounwind -define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 { +define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 { entry: %hashmap.key = alloca [2 x %main.hasPadding], align 8 %hashmap.value = alloca i32, align 4 @@ -100,7 +101,7 @@ entry: } ; Function Attrs: noinline nounwind -define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #3 { +define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(40) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 { entry: %hashmap.key = alloca [2 x %main.hasPadding], align 8 %hashmap.value = alloca i32, align 4 @@ -127,14 +128,14 @@ entry: } ; Function Attrs: nounwind -define hidden void @main.main(ptr %context) unnamed_addr #2 { +define hidden void @main.main(ptr %context) unnamed_addr #1 { entry: ret void } -attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #3 = { noinline nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" } -attributes #4 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #1 = { nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #2 = { noinline nounwind "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } +attributes #3 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #4 = { "target-features"="+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext" } attributes #5 = { nounwind } diff --git a/go.mod b/go.mod index bf85ef3ad1..81e972bd6e 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( golang.org/x/sys v0.21.0 golang.org/x/tools v0.22.1-0.20240621165957-db513b091504 gopkg.in/yaml.v2 v2.4.0 - tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8 + tinygo.org/x/go-llvm v0.0.0-20240804145059-aaff3eb751f0 ) require ( diff --git a/go.sum b/go.sum index 7a6d1f4a97..4900ef1ac4 100644 --- a/go.sum +++ b/go.sum @@ -77,5 +77,5 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8 h1:bLsZXRUBavt++CJlMN7sppNziqu3LyamESLhFJcpqFQ= -tinygo.org/x/go-llvm v0.0.0-20240627184919-3b50c76783a8/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0= +tinygo.org/x/go-llvm v0.0.0-20240804145059-aaff3eb751f0 h1:aN9rSxvVWmipB13yVAFOmRj3LX4Kkfiq/fSl5EAilDA= +tinygo.org/x/go-llvm v0.0.0-20240804145059-aaff3eb751f0/go.mod h1:GFbusT2VTA4I+l4j80b17KFK+6whv69Wtny5U+T8RR0= diff --git a/main_test.go b/main_test.go index 4c11f7b18d..9421612b79 100644 --- a/main_test.go +++ b/main_test.go @@ -286,10 +286,18 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) { runTest("rand.go", options, t, nil, nil) }) } - if !isWebAssembly { - // The recover() builtin isn't supported yet on WebAssembly and Windows. + if !isWASI { + // On WebAssembly, recover() needs the exception handling proposal which + // isn't (as of 2024) implemented in wasmtime so this test is skipped on + // WASI. t.Run("recover.go", func(t *testing.T) { t.Parallel() + options := options + if isWebAssembly { + // Hack: Asyncify doesn't support exception handling + // instructions. + options.Scheduler = "none" + } runTest("recover.go", options, t, nil, nil) }) } diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 062305f15c..7a5d6605ad 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -15,11 +15,20 @@ func trap() // purposes of TinyGo. It restores the stack pointer and jumps to the given pc. // //export tinygo_longjmp -func tinygo_longjmp(frame *deferFrame) +func tinygo_longjmp(frame *deferFrameInlineAsm) // Compiler intrinsic. // Returns whether recover is supported on the current architecture. -func supportsRecover() bool +func supportsRecover() recoverType + +type recoverType uint8 + +const ( + // Note: these contants match recoverSupport in compiler/defer.go + recoverNone recoverType = iota + recoverInlineAsm + recoverWasmEH +) const ( panicStrategyPrint = 1 @@ -31,33 +40,55 @@ const ( // using the -panic= compiler flag. func panicStrategy() uint8 -// DeferFrame is a stack allocated object that stores information for the -// current "defer frame", which is used in functions that use the `defer` +// LLVM compiler intrinsic: this inserts a wasm 'throw' instruction. +// +//export llvm.wasm.throw +func wasm_throw(uint32, unsafe.Pointer) + +// DeferFrameInlineAsm is a stack allocated object that stores information for +// the current "defer frame", which is used in functions that use the `defer` // keyword. // The compiler knows about the JumpPC struct offset, so it should not be moved // without also updating compiler/defer.go. -type deferFrame struct { +type deferFrameInlineAsm struct { JumpSP unsafe.Pointer // stack pointer to return to JumpPC unsafe.Pointer // pc to return to ExtraRegs [deferExtraRegs]unsafe.Pointer // extra registers (depending on the architecture) - Previous *deferFrame // previous recover buffer pointer + Previous *deferFrameInlineAsm // previous recover buffer pointer Panicking bool // true iff this defer frame is panicking PanicValue interface{} // panic value, might be nil for panic(nil) for example } +// DeferFrameWasmEH is like deferFrameInlineAsm but for WebAssembly exception +// handling. +type deferFrameWasmEH struct { + Previous *deferFrameWasmEH // previous recover buffer pointer + Panicking bool // true iff this defer frame is panicking + PanicValue interface{} // panic value, might be nil for panic(nil) for example +} + // Builtin function panic(msg), used as a compiler intrinsic. func _panic(message interface{}) { if panicStrategy() == panicStrategyTrap { trap() } - if supportsRecover() { - frame := (*deferFrame)(task.Current().DeferFrame) + switch supportsRecover() { + case recoverInlineAsm: + frame := (*deferFrameInlineAsm)(task.Current().DeferFrame) if frame != nil { frame.PanicValue = message frame.Panicking = true tinygo_longjmp(frame) // unreachable } + case recoverWasmEH: + frame := (*deferFrameWasmEH)(task.Current().DeferFrame) + if frame != nil { + frame.PanicValue = message + frame.Panicking = true + wasm_throw(0, nil) + // unreachable + } } printstring("panic: ") printitf(message) @@ -94,21 +125,43 @@ func runtimePanicAt(addr unsafe.Pointer, msg string) { // //go:inline //go:nobounds -func setupDeferFrame(frame *deferFrame, jumpSP unsafe.Pointer) { +func setupDeferFrameInlineAsm(frame *deferFrameInlineAsm, jumpSP unsafe.Pointer) { currentTask := task.Current() - frame.Previous = (*deferFrame)(currentTask.DeferFrame) + frame.Previous = (*deferFrameInlineAsm)(currentTask.DeferFrame) frame.JumpSP = jumpSP frame.Panicking = false currentTask.DeferFrame = unsafe.Pointer(frame) } +//go:inline +//go:nobounds +func setupDeferFrameWasmEH(frame *deferFrameWasmEH) { + currentTask := task.Current() + frame.Previous = (*deferFrameWasmEH)(currentTask.DeferFrame) + frame.Panicking = false + currentTask.DeferFrame = unsafe.Pointer(frame) +} + // Called right before the return instruction. It pops the defer frame from the // linked list of defer frames. It also re-raises a panic if the goroutine is // still panicking. // //go:inline //go:nobounds -func destroyDeferFrame(frame *deferFrame) { +func destroyDeferFrameInlineAsm(frame *deferFrameInlineAsm) { + task.Current().DeferFrame = unsafe.Pointer(frame.Previous) + if frame.Panicking { + // We're still panicking! + // Re-raise the panic now. + _panic(frame.PanicValue) + } +} + +// Like destroyDeferFrameInlineAsm but for wasm. +// +//go:inline +//go:nobounds +func destroyDeferFrameWasmEH(frame *deferFrameWasmEH) { task.Current().DeferFrame = unsafe.Pointer(frame.Previous) if frame.Panicking { // We're still panicking! @@ -122,27 +175,44 @@ func destroyDeferFrame(frame *deferFrame) { // useParentFrame is set when the caller of runtime._recover has a defer frame // itself. In that case, recover() shouldn't check that frame but one frame up. func _recover(useParentFrame bool) interface{} { - if !supportsRecover() { - // Compiling without stack unwinding support, so make this a no-op. - return nil - } // TODO: somehow check that recover() is called directly by a deferred // function in a panicking goroutine. Maybe this can be done by comparing // the frame pointer? - frame := (*deferFrame)(task.Current().DeferFrame) - if useParentFrame { - // Don't recover panic from the current frame (which can't be panicking - // already), but instead from the previous frame. - frame = frame.Previous - } - if frame != nil && frame.Panicking { - // Only the first call to recover returns the panic value. It also stops - // the panicking sequence, hence setting panicking to false. - frame.Panicking = false - return frame.PanicValue + switch supportsRecover() { + case recoverInlineAsm: + frame := (*deferFrameInlineAsm)(task.Current().DeferFrame) + if useParentFrame { + // Don't recover panic from the current frame (which can't be + // panicking already), but instead from the previous frame. + frame = frame.Previous + } + if frame != nil && frame.Panicking { + // Only the first call to recover returns the panic value. It also + // stops the panicking sequence, hence setting panicking to false. + frame.Panicking = false + return frame.PanicValue + } + // Not panicking, so return a nil interface. + return nil + case recoverWasmEH: + frame := (*deferFrameWasmEH)(task.Current().DeferFrame) + if useParentFrame { + // Don't recover panic from the current frame (which can't be + // panicking already), but instead from the previous frame. + frame = frame.Previous + } + if frame != nil && frame.Panicking { + // Only the first call to recover returns the panic value. It also + // stops the panicking sequence, hence setting panicking to false. + frame.Panicking = false + return frame.PanicValue + } + // Not panicking, so return a nil interface. + return nil + default: + // Compiling without stack unwinding support, so make this a no-op. + return nil } - // Not panicking, so return a nil interface. - return nil } // Panic when trying to dereference a nil pointer. diff --git a/targets/wasm.json b/targets/wasm.json index 050ee105e0..6f9138aac8 100644 --- a/targets/wasm.json +++ b/targets/wasm.json @@ -1,7 +1,7 @@ { "llvm-target": "wasm32-unknown-wasi", "cpu": "generic", - "features": "+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext", + "features": "+bulk-memory,+exception-handling,+mutable-globals,+nontrapping-fptoint,+sign-ext", "build-tags": ["tinygo.wasm"], "goos": "js", "goarch": "wasm", @@ -12,6 +12,7 @@ "default-stack-size": 65536, "cflags": [ "-mbulk-memory", + "-mexception-handling", "-mnontrapping-fptoint", "-msign-ext" ], diff --git a/transform/gc.go b/transform/gc.go index 9d1d8f3fbb..4fe585d3da 100644 --- a/transform/gc.go +++ b/transform/gc.go @@ -177,7 +177,7 @@ func MakeGCStackSlots(mod llvm.Module) bool { // now. // With more analysis, it should be possible to optimize a // significant chunk of these away. - case llvm.Call, llvm.Load, llvm.IntToPtr: + case llvm.Call, llvm.Invoke, llvm.Load, llvm.IntToPtr: // These create new values so must be stored locally. But // perhaps some of these can be fused when they actually refer // to the same value.