From c41a212712e411952d5685605e082da65e1f75b6 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 17 Jan 2023 22:53:38 +0100 Subject: [PATCH] wasm: avoid miscompile with ThinLTO I found that when I enable ThinLTO, a miscompilation triggers that had been hidden all the time previously. The bug appears to happen as follows: 1. TinyGo generates a function with a runtime.trackPointer call, but without an alloca (or the alloca gets optimized away). 2. LLVM sees that no alloca needs to be kept alive across the runtime.trackPointer call, and therefore it adds the 'tail' flag. One of the effects of this flag is that it makes it undefined behavior to keep allocas alive across the call (which is still safe at that point). 3. The GC lowering pass adds a stack slot alloca and converts runtime.trackPointer calls into alloca stores. The last step triggers the bug: the compiler inserts an alloca where there was none before but that's not valid as long as the 'tail' flag is present. This patch fixes the bug in a somewhat dirty way, by always creating a dummy alloca so that LLVM won't do the optimization in step 2 (and possibly other optimizations that rely on there being no alloca instruction). --- compiler/compiler.go | 8 ++++ compiler/gc.go | 2 +- compiler/llvm.go | 6 +-- compiler/testdata/basic.ll | 8 ++-- compiler/testdata/channel.ll | 5 ++- compiler/testdata/float.ll | 2 +- compiler/testdata/func.ll | 2 +- compiler/testdata/gc.ll | 42 +++++++++++-------- compiler/testdata/go1.20.ll | 11 +++-- compiler/testdata/goroutine-wasm-asyncify.ll | 17 ++++---- compiler/testdata/interface.ll | 17 +++++--- compiler/testdata/pointer.ll | 32 +++++++++------ compiler/testdata/pragma.ll | 2 +- compiler/testdata/slice.ll | 43 +++++++++++++------- compiler/testdata/string.ll | 2 +- src/runtime/gc_stack_portable.go | 2 +- 16 files changed, 122 insertions(+), 79 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index aad5e0d9..53086478 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -152,6 +152,7 @@ type builder struct { phis []phiNode deferPtr llvm.Value deferFrame llvm.Value + stackChainAlloca llvm.Value landingpad llvm.BasicBlock difunc llvm.Metadata dilocals map[*types.Var]llvm.Metadata @@ -1226,6 +1227,13 @@ func (b *builder) createFunctionStart(intrinsic bool) { // them. b.deferInitFunc() } + + if b.NeedsStackObjects { + // Create a dummy alloca that will be used in runtime.trackPointer. + // It is necessary to pass a dummy alloca to runtime.trackPointer + // because runtime.trackPointer is replaced by an alloca store. + b.stackChainAlloca = b.CreateAlloca(b.ctx.Int8Type(), "stackalloc") + } } // createFunction builds the LLVM IR implementation for this function. The diff --git a/compiler/gc.go b/compiler/gc.go index 6aa0b45a..0fd6f5aa 100644 --- a/compiler/gc.go +++ b/compiler/gc.go @@ -84,7 +84,7 @@ func (b *builder) trackPointer(value llvm.Value) { if value.Type() != b.i8ptrType { value = b.CreateBitCast(value, b.i8ptrType, "") } - b.createRuntimeCall("trackPointer", []llvm.Value{value}, "") + b.createRuntimeCall("trackPointer", []llvm.Value{value, b.stackChainAlloca}, "") } // typeHasPointers returns whether this type is a pointer or contains pointers. diff --git a/compiler/llvm.go b/compiler/llvm.go index 0dddb330..33c6603e 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -136,11 +136,7 @@ func (b *builder) emitPointerPack(values []llvm.Value) llvm.Value { llvm.Undef(b.i8ptrType), // unused context parameter }, "") if b.NeedsStackObjects { - trackPointer := b.mod.NamedFunction("runtime.trackPointer") - b.CreateCall(trackPointer.GlobalValueType(), trackPointer, []llvm.Value{ - packedHeapAlloc, - llvm.Undef(b.i8ptrType), // unused context parameter - }, "") + b.trackPointer(packedHeapAlloc) } packedAlloc := b.CreateBitCast(packedHeapAlloc, llvm.PointerType(packedType, 0), "") diff --git a/compiler/testdata/basic.ll b/compiler/testdata/basic.ll index 4daf7b7c..52a638db 100644 --- a/compiler/testdata/basic.ll +++ b/compiler/testdata/basic.ll @@ -12,7 +12,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -196,8 +196,9 @@ entry: define hidden void @main.foo(ptr %context) unnamed_addr #1 { entry: %complit = alloca %main.kv.0, align 8 + %stackalloc = alloca i8, align 1 store %main.kv.0 zeroinitializer, ptr %complit, align 8 - call void @runtime.trackPointer(ptr nonnull %complit, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #2 call void @"main.foo$1"(%main.kv.0 zeroinitializer, ptr undef) ret void } @@ -206,8 +207,9 @@ entry: define internal void @"main.foo$1"(%main.kv.0 %b, ptr %context) unnamed_addr #1 { entry: %b1 = alloca %main.kv.0, align 8 + %stackalloc = alloca i8, align 1 store %main.kv.0 zeroinitializer, ptr %b1, align 8 - call void @runtime.trackPointer(ptr nonnull %b1, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %b1, ptr nonnull %stackalloc, ptr undef) #2 store %main.kv.0 %b, ptr %b1, align 8 ret void } diff --git a/compiler/testdata/channel.ll b/compiler/testdata/channel.ll index 8f4ed74e..6fdb1407 100644 --- a/compiler/testdata/channel.ll +++ b/compiler/testdata/channel.ll @@ -8,7 +8,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -58,7 +58,8 @@ define hidden void @main.chanZeroSend(ptr dereferenceable_or_null(32) %ch, ptr % entry: %complit = alloca {}, align 8 %chan.blockedList = alloca %runtime.channelBlockedList, align 8 - call void @runtime.trackPointer(ptr nonnull %complit, ptr undef) #3 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr nonnull %complit, ptr nonnull %stackalloc, ptr undef) #3 call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %chan.blockedList) call void @runtime.chanSend(ptr %ch, ptr null, ptr nonnull %chan.blockedList, ptr undef) #3 call void @llvm.lifetime.end.p0(i64 24, ptr nonnull %chan.blockedList) diff --git a/compiler/testdata/float.ll b/compiler/testdata/float.ll index 2acc2ed4..38ccc9d3 100644 --- a/compiler/testdata/float.ll +++ b/compiler/testdata/float.ll @@ -5,7 +5,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { diff --git a/compiler/testdata/func.ll b/compiler/testdata/func.ll index 227e52c1..2df92345 100644 --- a/compiler/testdata/func.ll +++ b/compiler/testdata/func.ll @@ -5,7 +5,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { diff --git a/compiler/testdata/gc.ll b/compiler/testdata/gc.ll index 4044ae72..a59a546f 100644 --- a/compiler/testdata/gc.ll +++ b/compiler/testdata/gc.ll @@ -27,7 +27,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -38,17 +38,18 @@ entry: ; Function Attrs: nounwind define hidden void @main.newScalar(ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %new = call ptr @runtime.alloc(i32 1, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new, ptr @main.scalar1, align 4 %new1 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new1, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new1, ptr @main.scalar2, align 4 %new2 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new2, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new2, ptr @main.scalar3, align 4 %new3 = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new3, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new3, ptr @main.scalar4, align 4 ret void } @@ -56,14 +57,15 @@ entry: ; Function Attrs: nounwind define hidden void @main.newArray(ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %new = call ptr @runtime.alloc(i32 3, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new, ptr @main.array1, align 4 %new1 = call ptr @runtime.alloc(i32 71, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new1, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new1, ptr @main.array2, align 4 %new2 = call ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new2, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new2, ptr @main.array3, align 4 ret void } @@ -71,17 +73,18 @@ entry: ; Function Attrs: nounwind define hidden void @main.newStruct(ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %new = call ptr @runtime.alloc(i32 0, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new, ptr @main.struct1, align 4 %new1 = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new1, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new1, ptr @main.struct2, align 4 %new2 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new2, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new2, ptr @main.struct3, align 4 %new3 = call ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new3, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #2 store ptr %new3, ptr @main.struct4, align 4 ret void } @@ -89,26 +92,28 @@ entry: ; Function Attrs: nounwind define hidden ptr @main.newFuncValue(ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %new = call ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 197 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %new, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %new, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %new } ; Function Attrs: nounwind define hidden void @main.makeSlice(ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %makeslice = call ptr @runtime.alloc(i32 5, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %makeslice, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2 store ptr %makeslice, ptr @main.slice1, align 8 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 8 %makeslice1 = call ptr @runtime.alloc(i32 20, ptr nonnull inttoptr (i32 67 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice1, ptr nonnull %stackalloc, ptr undef) #2 store ptr %makeslice1, ptr @main.slice2, align 8 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 8 %makeslice3 = call ptr @runtime.alloc(i32 60, ptr nonnull inttoptr (i32 71 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice3, ptr nonnull %stackalloc, ptr undef) #2 store ptr %makeslice3, ptr @main.slice3, align 8 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 8 @@ -118,13 +123,14 @@ entry: ; Function Attrs: nounwind 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 ptr @runtime.alloc(i32 16, ptr null, ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %0, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 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 { i32 ptrtoint (ptr @"reflect/types.type:basic:complex128" to i32), ptr undef }, ptr %0, 1 - call void @runtime.trackPointer(ptr nonnull %0, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 ret %runtime._interface %1 } diff --git a/compiler/testdata/go1.20.ll b/compiler/testdata/go1.20.ll index fc66b47c..16e8904c 100644 --- a/compiler/testdata/go1.20.ll +++ b/compiler/testdata/go1.20.ll @@ -7,7 +7,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -18,13 +18,15 @@ entry: ; Function Attrs: nounwind define hidden ptr @main.unsafeSliceData(ptr %s.data, i32 %s.len, i32 %s.cap, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %s.data, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2 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 #1 { entry: + %stackalloc = alloca i8, align 1 %0 = icmp slt i16 %len, 0 %1 = icmp eq ptr %ptr, null %2 = icmp ne i16 %len, 0 @@ -36,7 +38,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 undef) #2 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 ret %runtime._string %7 unsafe.String.throw: ; preds = %entry @@ -49,7 +51,8 @@ declare void @runtime.unsafeSlicePanic(ptr) #0 ; Function Attrs: nounwind define hidden ptr @main.unsafeStringData(ptr %s.data, i32 %s.len, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %s.data, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %s.data, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %s.data } diff --git a/compiler/testdata/goroutine-wasm-asyncify.ll b/compiler/testdata/goroutine-wasm-asyncify.ll index 280f216a..0f38e181 100644 --- a/compiler/testdata/goroutine-wasm-asyncify.ll +++ b/compiler/testdata/goroutine-wasm-asyncify.ll @@ -9,7 +9,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -64,13 +64,14 @@ entry: ; Function Attrs: nounwind define hidden void @main.closureFunctionGoroutine(ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %n = call ptr @runtime.alloc(i32 4, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #8 - call void @runtime.trackPointer(ptr nonnull %n, ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8 store i32 3, ptr %n, align 4 - call void @runtime.trackPointer(ptr nonnull %n, ptr undef) #8 - call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull %n, ptr nonnull %stackalloc, ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull @"main.closureFunctionGoroutine$1", ptr nonnull %stackalloc, ptr undef) #8 %0 = call ptr @runtime.alloc(i32 8, ptr null, ptr undef) #8 - call void @runtime.trackPointer(ptr nonnull %0, ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8 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 @@ -103,8 +104,9 @@ declare void @runtime.printint32(i32, ptr) #0 ; Function Attrs: nounwind define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %0 = call ptr @runtime.alloc(i32 12, ptr null, ptr undef) #8 - call void @runtime.trackPointer(ptr nonnull %0, ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8 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 @@ -154,8 +156,9 @@ declare void @runtime.chanClose(ptr dereferenceable_or_null(32), ptr) #0 ; Function Attrs: nounwind define hidden void @main.startInterfaceMethod(i32 %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %0 = call ptr @runtime.alloc(i32 16, ptr null, ptr undef) #8 - call void @runtime.trackPointer(ptr nonnull %0, ptr undef) #8 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #8 store ptr %itf.value, ptr %0, align 4 %1 = getelementptr inbounds { ptr, %runtime._string, i32 }, ptr %0, i32 0, i32 1 store ptr @"main$string", ptr %1, align 4 diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index 38d4e567..2ddaf4ec 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -23,7 +23,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -34,21 +34,24 @@ entry: ; Function Attrs: nounwind define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr null, ptr undef) #6 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:basic:int" to i32), ptr null } } ; Function Attrs: nounwind define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr null, ptr undef) #6 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:basic:int" to i32), ptr null } } ; Function Attrs: nounwind define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr null, ptr undef) #6 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:named:error" to i32), ptr null } } @@ -57,7 +60,8 @@ declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #2 ; Function Attrs: nounwind define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr null, ptr undef) #6 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._interface { i32 ptrtoint (ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), ptr null } } @@ -116,9 +120,10 @@ declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{b ; Function Attrs: nounwind define hidden %runtime._string @main.callErrorMethod(i32 %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, i32 %itf.typecode, ptr undef) #6 %1 = extractvalue %runtime._string %0, 0 - call void @runtime.trackPointer(ptr %1, ptr undef) #6 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #6 ret %runtime._string %0 } diff --git a/compiler/testdata/pointer.ll b/compiler/testdata/pointer.ll index 9bc8bd35..ac1a4bc7 100644 --- a/compiler/testdata/pointer.ll +++ b/compiler/testdata/pointer.ll @@ -5,7 +5,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -22,52 +22,58 @@ entry: ; Function Attrs: nounwind define hidden ptr @main.pointerCastFromUnsafe(ptr %x, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %x, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %x } ; Function Attrs: nounwind define hidden ptr @main.pointerCastToUnsafe(ptr dereferenceable_or_null(4) %x, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %x, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %x } ; Function Attrs: nounwind define hidden ptr @main.pointerCastToUnsafeNoop(ptr dereferenceable_or_null(1) %x, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %x, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %x, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %x } ; Function Attrs: nounwind define hidden ptr @main.pointerUnsafeGEPFixedOffset(ptr dereferenceable_or_null(1) %ptr, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %ptr, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %0 = getelementptr inbounds i8, ptr %ptr, i32 10 - call void @runtime.trackPointer(ptr nonnull %0, ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %0, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %0, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %0 } ; Function Attrs: nounwind define hidden ptr @main.pointerUnsafeGEPByteOffset(ptr dereferenceable_or_null(1) %ptr, i32 %offset, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %ptr, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %0 = getelementptr inbounds i8, ptr %ptr, i32 %offset - call void @runtime.trackPointer(ptr %0, ptr undef) #2 - call void @runtime.trackPointer(ptr %0, ptr undef) #2 + call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2 + call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %0 } ; Function Attrs: nounwind define hidden ptr @main.pointerUnsafeGEPIntOffset(ptr dereferenceable_or_null(4) %ptr, i32 %offset, ptr %context) unnamed_addr #1 { entry: - call void @runtime.trackPointer(ptr %ptr, ptr undef) #2 + %stackalloc = alloca i8, align 1 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 %0 = shl i32 %offset, 2 %1 = getelementptr inbounds i8, ptr %ptr, i32 %0 - call void @runtime.trackPointer(ptr %1, ptr undef) #2 - call void @runtime.trackPointer(ptr %1, ptr undef) #2 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %1 } diff --git a/compiler/testdata/pragma.ll b/compiler/testdata/pragma.ll index c49c83bd..00c619d5 100644 --- a/compiler/testdata/pragma.ll +++ b/compiler/testdata/pragma.ll @@ -12,7 +12,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { diff --git a/compiler/testdata/slice.ll b/compiler/testdata/slice.ll index 56e40f5f..01410e50 100644 --- a/compiler/testdata/slice.ll +++ b/compiler/testdata/slice.ll @@ -5,7 +5,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { @@ -46,8 +46,9 @@ declare void @runtime.lookupPanic(ptr) #0 ; Function Attrs: nounwind 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 ptr @runtime.alloc(i32 12, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %varargs, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %varargs, ptr nonnull %stackalloc, ptr undef) #2 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 @@ -60,7 +61,7 @@ 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 undef) #2 + call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %4 } @@ -69,6 +70,7 @@ declare { ptr, i32, i32 } @runtime.sliceAppend(ptr, ptr nocapture readonly, i32, ; 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 #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) #2 %append.newPtr = extractvalue { ptr, i32, i32 } %append.new, 0 %append.newLen = extractvalue { ptr, i32, i32 } %append.new, 1 @@ -76,7 +78,7 @@ 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 undef) #2 + call void @runtime.trackPointer(ptr %append.newPtr, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %2 } @@ -92,6 +94,7 @@ declare i32 @runtime.sliceCopy(ptr nocapture writeonly, ptr nocapture readonly, ; Function Attrs: nounwind 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 br i1 %slice.maxcap, label %slice.throw, label %slice.next @@ -100,7 +103,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 undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -113,6 +116,7 @@ declare void @runtime.slicePanic(ptr) #0 ; Function Attrs: nounwind 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 br i1 %slice.maxcap, label %slice.throw, label %slice.next @@ -122,7 +126,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 undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -133,6 +137,7 @@ slice.throw: ; preds = %entry ; Function Attrs: nounwind 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 br i1 %slice.maxcap, label %slice.throw, label %slice.next @@ -142,7 +147,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 undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -153,6 +158,7 @@ slice.throw: ; preds = %entry ; Function Attrs: nounwind 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 br i1 %slice.maxcap, label %slice.throw, label %slice.next @@ -162,7 +168,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 undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice.buf, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %2 slice.throw: ; preds = %entry @@ -173,17 +179,19 @@ slice.throw: ; preds = %entry ; Function Attrs: nounwind 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 undef) #2 + call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %0 } ; Function Attrs: nounwind 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 undef) #2 + call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #2 ret ptr %1 } @@ -206,8 +214,9 @@ declare void @runtime.sliceToArrayPointerPanic(ptr) #0 ; Function Attrs: nounwind define hidden ptr @main.SliceToArrayConst(ptr %context) unnamed_addr #1 { entry: + %stackalloc = alloca i8, align 1 %makeslice = call ptr @runtime.alloc(i32 24, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #2 - call void @runtime.trackPointer(ptr nonnull %makeslice, ptr undef) #2 + call void @runtime.trackPointer(ptr nonnull %makeslice, ptr nonnull %stackalloc, ptr undef) #2 br i1 false, label %slicetoarray.throw, label %slicetoarray.next slicetoarray.next: ; preds = %entry @@ -220,6 +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 #1 { entry: + %stackalloc = alloca i8, align 1 %0 = icmp ugt i32 %len, 1073741823 %1 = icmp eq ptr %ptr, null %2 = icmp ne i32 %len, 0 @@ -231,7 +241,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 undef) #2 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %7 unsafe.Slice.throw: ; preds = %entry @@ -244,6 +254,7 @@ declare void @runtime.unsafeSlicePanic(ptr) #0 ; Function Attrs: nounwind 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 %1 = icmp ne i16 %len, 0 %2 = and i1 %0, %1 @@ -254,7 +265,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 undef) #2 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %6 unsafe.Slice.throw: ; preds = %entry @@ -265,6 +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 #1 { entry: + %stackalloc = alloca i8, align 1 %0 = icmp ugt i64 %len, 1073741823 %1 = icmp eq ptr %ptr, null %2 = icmp ne i64 %len, 0 @@ -277,7 +289,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 undef) #2 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %8 unsafe.Slice.throw: ; preds = %entry @@ -288,6 +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 #1 { entry: + %stackalloc = alloca i8, align 1 %0 = icmp ugt i64 %len, 1073741823 %1 = icmp eq ptr %ptr, null %2 = icmp ne i64 %len, 0 @@ -300,7 +313,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 undef) #2 + call void @runtime.trackPointer(ptr %ptr, ptr nonnull %stackalloc, ptr undef) #2 ret { ptr, i32, i32 } %8 unsafe.Slice.throw: ; preds = %entry diff --git a/compiler/testdata/string.ll b/compiler/testdata/string.ll index bd3b8f5a..e6491e23 100644 --- a/compiler/testdata/string.ll +++ b/compiler/testdata/string.ll @@ -9,7 +9,7 @@ target triple = "wasm32-unknown-wasi" declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0 -declare void @runtime.trackPointer(ptr nocapture readonly, ptr) #0 +declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 ; Function Attrs: nounwind define hidden void @main.init(ptr %context) unnamed_addr #1 { diff --git a/src/runtime/gc_stack_portable.go b/src/runtime/gc_stack_portable.go index 871f1c61..ab921a99 100644 --- a/src/runtime/gc_stack_portable.go +++ b/src/runtime/gc_stack_portable.go @@ -53,7 +53,7 @@ func markStack() { // trackPointer is a stub function call inserted by the compiler during IR // construction. Calls to it are later replaced with regular stack bookkeeping // code. -func trackPointer(ptr unsafe.Pointer) +func trackPointer(ptr, alloca unsafe.Pointer) // swapStackChain swaps the stack chain. // This is called from internal/task when switching goroutines.