From 0704794def59aa78b4735124cebeef59713f40b4 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 9 Jul 2021 23:41:50 +0200 Subject: [PATCH] compiler: add object layout information to heap allocations This commit adds object layout information to new heap allocations. It is not yet used anywhere: the next commit will make use of it. Object layout information will eventually be used for a (mostly) precise garbage collector. This is what the data is made for. However, it is also useful in the interp package which can work better if it knows the memory layout and thus the approximate LLVM type of heap-allocated objects. --- compiler/compiler.go | 10 +- compiler/compiler_test.go | 1 + compiler/llvm.go | 201 +++++++++++++++++++ compiler/testdata/gc.go | 72 +++++++ compiler/testdata/gc.ll | 113 +++++++++++ compiler/testdata/go1.17.ll | 2 +- compiler/testdata/goroutine-cortex-m-qemu.ll | 2 +- compiler/testdata/goroutine-wasm.ll | 2 +- compiler/testdata/slice.ll | 10 +- 9 files changed, 400 insertions(+), 13 deletions(-) create mode 100644 compiler/testdata/gc.go create mode 100644 compiler/testdata/gc.ll diff --git a/compiler/compiler.go b/compiler/compiler.go index c42e40c4..2c79047d 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 23 // last change: fix recursive function types +const Version = 24 // last change: add layout param to runtime.alloc calls func init() { llvm.InitializeAllTargets() @@ -1524,8 +1524,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size)) } sizeValue := llvm.ConstInt(b.uintptrType, size, false) - nilPtr := llvm.ConstNull(b.i8ptrType) - buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, nilPtr}, expr.Comment) + layoutValue := b.createObjectLayout(typ, expr.Pos()) + buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment) buf = b.CreateBitCast(buf, llvm.PointerType(typ, 0), "") return buf, nil } else { @@ -1737,8 +1737,8 @@ func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) { return llvm.Value{}, err } sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap") - nilPtr := llvm.ConstNull(b.i8ptrType) - slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, nilPtr}, "makeslice.buf") + layoutValue := b.createObjectLayout(llvmElemType, expr.Pos()) + slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf") slicePtr = b.CreateBitCast(slicePtr, llvm.PointerType(llvmElemType, 0), "makeslice.array") // Extend or truncate if necessary. This is safe as we've already done diff --git a/compiler/compiler_test.go b/compiler/compiler_test.go index c80b822c..f1a0e00c 100644 --- a/compiler/compiler_test.go +++ b/compiler/compiler_test.go @@ -54,6 +54,7 @@ func TestCompiler(t *testing.T) { {"channel.go", ""}, {"intrinsics.go", "cortex-m-qemu"}, {"intrinsics.go", "wasm"}, + {"gc.go", ""}, } _, minor, err := goenv.GetGorootVersion(goenv.Get("GOROOT")) diff --git a/compiler/llvm.go b/compiler/llvm.go index da4013e6..17f642d7 100644 --- a/compiler/llvm.go +++ b/compiler/llvm.go @@ -1,6 +1,11 @@ package compiler import ( + "fmt" + "go/token" + "go/types" + "math/big" + "github.com/tinygo-org/tinygo/compiler/llvmutil" "tinygo.org/x/go-llvm" ) @@ -52,3 +57,199 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l global.SetInitializer(value) return global } + +// createObjectLayout returns a LLVM value (of type i8*) that describes where +// there are pointers in the type t. If all the data fits in a word, it is +// returned as a word. Otherwise it will store the data in a global. +// +// The value contains two pieces of information: the length of the object and +// which words contain a pointer (indicated by setting the given bit to 1). For +// arrays, only the element is stored. This works because the GC knows the +// object size and can therefore know how this value is repeated in the object. +func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value { + // Use the element type for arrays. This works even for nested arrays. + for { + kind := t.TypeKind() + if kind == llvm.ArrayTypeKind { + t = t.ElementType() + continue + } + if kind == llvm.StructTypeKind { + fields := t.StructElementTypes() + if len(fields) == 1 { + t = fields[0] + continue + } + } + break + } + + // Do a few checks to see whether we need to generate any object layout + // information at all. + objectSizeBytes := c.targetData.TypeAllocSize(t) + pointerSize := c.targetData.TypeAllocSize(c.i8ptrType) + pointerAlignment := c.targetData.PrefTypeAlignment(c.i8ptrType) + if objectSizeBytes < pointerSize { + // Too small to contain a pointer. + layout := (uint64(1) << 1) | 1 + return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) + } + bitmap := c.getPointerBitmap(t, pos) + if bitmap.BitLen() == 0 { + // There are no pointers in this type, so we can simplify the layout. + // TODO: this can be done in many other cases, e.g. when allocating an + // array (like [4][]byte, which repeats a slice 4 times). + layout := (uint64(1) << 1) | 1 + return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) + } + if objectSizeBytes%uint64(pointerAlignment) != 0 { + // This shouldn't happen except for packed structs, which aren't + // currently used. + c.addError(pos, "internal error: unexpected object size for object with pointer field") + return llvm.ConstNull(c.i8ptrType) + } + objectSizeWords := objectSizeBytes / uint64(pointerAlignment) + + pointerBits := pointerSize * 8 + var sizeFieldBits uint64 + switch pointerBits { + case 16: + sizeFieldBits = 4 + case 32: + sizeFieldBits = 5 + case 64: + sizeFieldBits = 6 + default: + panic("unknown pointer size") + } + layoutFieldBits := pointerBits - 1 - sizeFieldBits + + // Try to emit the value as an inline integer. This is possible in most + // cases. + if objectSizeWords < layoutFieldBits { + // If it can be stored directly in the pointer value, do so. + // The runtime knows that if the least significant bit of the pointer is + // set, the pointer contains the value itself. + layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1 + return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.i8ptrType) + } + + // Unfortunately, the object layout is too big to fit in a pointer-sized + // integer. Store it in a global instead. + + // Try first whether the global already exists. All objects with a + // particular name have the same type, so this is possible. + globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap) + global := c.mod.NamedGlobal(globalName) + if !global.IsNil() { + return llvm.ConstBitCast(global, c.i8ptrType) + } + + // Create the global initializer. + bitmapBytes := make([]byte, int(objectSizeWords+7)/8) + copy(bitmapBytes, bitmap.Bytes()) + var bitmapByteValues []llvm.Value + for _, b := range bitmapBytes { + bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false)) + } + initializer := c.ctx.ConstStruct([]llvm.Value{ + llvm.ConstInt(c.uintptrType, objectSizeWords, false), + llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues), + }, false) + + global = llvm.AddGlobal(c.mod, initializer.Type(), globalName) + global.SetInitializer(initializer) + global.SetUnnamedAddr(true) + global.SetGlobalConstant(true) + global.SetLinkage(llvm.LinkOnceODRLinkage) + if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 { + // AVR doesn't have alignment by default. + global.SetAlignment(2) + } + if c.Debug && pos != token.NoPos { + // Creating a fake global so that the value can be inspected in GDB. + // For example, the layout for strings.stringFinder (as of Go version + // 1.15) has the following type according to GDB: + // type = struct { + // uintptr numBits; + // uint8 data[33]; + // } + // ...that's sort of a mixed C/Go type, but it is readable. More + // importantly, these object layout globals can be read and printed by + // GDB which may be useful for debugging. + position := c.program.Fset.Position(pos) + diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[position.Filename], llvm.DIGlobalVariableExpression{ + Name: globalName, + File: c.getDIFile(position.Filename), + Line: position.Line, + Type: c.getDIType(types.NewStruct([]*types.Var{ + types.NewVar(pos, nil, "numBits", types.Typ[types.Uintptr]), + types.NewVar(pos, nil, "data", types.NewArray(types.Typ[types.Byte], int64(len(bitmapByteValues)))), + }, nil)), + LocalToUnit: false, + Expr: c.dibuilder.CreateExpression(nil), + }) + global.AddMetadata(0, diglobal) + } + + return llvm.ConstBitCast(global, c.i8ptrType) +} + +// getPointerBitmap scans the given LLVM type for pointers and sets bits in a +// bigint at the word offset that contains a pointer. This scan is recursive. +func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int { + alignment := c.targetData.PrefTypeAlignment(c.i8ptrType) + switch typ.TypeKind() { + case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind: + return big.NewInt(0) + case llvm.PointerTypeKind: + return big.NewInt(1) + case llvm.StructTypeKind: + ptrs := big.NewInt(0) + if typ.StructName() == "runtime.funcValue" { + // Hack: the type runtime.funcValue contains an 'id' field which is + // of type uintptr, but before the LowerFuncValues pass it actually + // contains a pointer (ptrtoint) to a global. This trips up the + // interp package. Therefore, make the id field a pointer for now. + typ = c.ctx.StructType([]llvm.Type{c.i8ptrType, c.i8ptrType}, false) + } + for i, subtyp := range typ.StructElementTypes() { + subptrs := c.getPointerBitmap(subtyp, pos) + if subptrs.BitLen() == 0 { + continue + } + offset := c.targetData.ElementOffset(typ, i) + if offset%uint64(alignment) != 0 { + // This error will let the compilation fail, but by continuing + // the error can still easily be shown. + c.addError(pos, "internal error: allocated struct contains unaligned pointer") + continue + } + subptrs.Lsh(subptrs, uint(offset)/uint(alignment)) + ptrs.Or(ptrs, subptrs) + } + return ptrs + case llvm.ArrayTypeKind: + subtyp := typ.ElementType() + subptrs := c.getPointerBitmap(subtyp, pos) + ptrs := big.NewInt(0) + if subptrs.BitLen() == 0 { + return ptrs + } + elementSize := c.targetData.TypeAllocSize(subtyp) + if elementSize%uint64(alignment) != 0 { + // This error will let the compilation fail (but continues so that + // other errors can be shown). + c.addError(pos, "internal error: allocated array contains unaligned pointer") + return ptrs + } + for i := 0; i < typ.ArrayLength(); i++ { + ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment)) + ptrs.Or(ptrs, subptrs) + } + return ptrs + default: + // Should not happen. + panic("unknown LLVM type") + } +} diff --git a/compiler/testdata/gc.go b/compiler/testdata/gc.go new file mode 100644 index 00000000..74e0358a --- /dev/null +++ b/compiler/testdata/gc.go @@ -0,0 +1,72 @@ +package main + +var ( + scalar1 *byte + scalar2 *int32 + scalar3 *int64 + scalar4 *float32 + + array1 *[3]byte + array2 *[71]byte + array3 *[3]*byte + + struct1 *struct{} + struct2 *struct { + x int + y int + } + struct3 *struct { + x *byte + y [60]uintptr + z *byte + } + + slice1 []byte + slice2 []*int + slice3 [][]byte +) + +func newScalar() { + scalar1 = new(byte) + scalar2 = new(int32) + scalar3 = new(int64) + scalar4 = new(float32) +} + +func newArray() { + array1 = new([3]byte) + array2 = new([71]byte) + array3 = new([3]*byte) +} + +func newStruct() { + struct1 = new(struct{}) + struct2 = new(struct { + x int + y int + }) + struct3 = new(struct { + x *byte + y [60]uintptr + z *byte + }) +} + +func newFuncValue() *func() { + // On some platforms that use runtime.funcValue ("switch" style) function + // values, a func value is allocated as having two pointer words while the + // struct looks like {unsafe.Pointer; uintptr}. This is so that the interp + // package won't get confused, see getPointerBitmap in compiler/llvm.go for + // details. + return new(func()) +} + +func makeSlice() { + slice1 = make([]byte, 5) + slice2 = make([]*int, 5) + slice3 = make([][]byte, 5) +} + +func makeInterface(v complex128) interface{} { + return v // always stored in an allocation +} diff --git a/compiler/testdata/gc.ll b/compiler/testdata/gc.ll new file mode 100644 index 00000000..7ac0aa1b --- /dev/null +++ b/compiler/testdata/gc.ll @@ -0,0 +1,113 @@ +; ModuleID = 'gc.go' +source_filename = "gc.go" +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-wasi" + +%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 } +%runtime.interfaceMethodInfo = type { i8*, i32 } +%runtime.funcValue = type { i8*, i32 } +%runtime._interface = type { i32, i8* } + +@main.scalar1 = hidden global i8* null, align 4 +@main.scalar2 = hidden global i32* null, align 4 +@main.scalar3 = hidden global i64* null, align 4 +@main.scalar4 = hidden global float* null, align 4 +@main.array1 = hidden global [3 x i8]* null, align 4 +@main.array2 = hidden global [71 x i8]* null, align 4 +@main.array3 = hidden global [3 x i8*]* null, align 4 +@main.struct1 = hidden global {}* null, align 4 +@main.struct2 = hidden global { i32, i32 }* null, align 4 +@main.struct3 = hidden global { i8*, [60 x i32], i8* }* null, align 4 +@main.slice1 = hidden global { i8*, i32, i32 } zeroinitializer, align 8 +@main.slice2 = hidden global { i32**, i32, i32 } zeroinitializer, align 8 +@main.slice3 = hidden global { { i8*, i32, i32 }*, i32, i32 } zeroinitializer, align 8 +@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c" \00\00\00\00\00\00\01" } +@"reflect/types.type:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:complex128", i32 0 } +@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:complex128", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 } + +declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*, i8*) + +; Function Attrs: nounwind +define hidden void @main.init(i8* %context, i8* %parentHandle) unnamed_addr #0 { +entry: + ret void +} + +; Function Attrs: nounwind +define hidden void @main.newScalar(i8* %context, i8* %parentHandle) unnamed_addr #0 { +entry: + %new = call i8* @runtime.alloc(i32 1, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new, i8** @main.scalar1, align 4 + %new1 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new1, i8** bitcast (i32** @main.scalar2 to i8**), align 4 + %new2 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new2, i8** bitcast (i64** @main.scalar3 to i8**), align 4 + %new3 = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new3, i8** bitcast (float** @main.scalar4 to i8**), align 4 + ret void +} + +; Function Attrs: nounwind +define hidden void @main.newArray(i8* %context, i8* %parentHandle) unnamed_addr #0 { +entry: + %new = call i8* @runtime.alloc(i32 3, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new, i8** bitcast ([3 x i8]** @main.array1 to i8**), align 4 + %new1 = call i8* @runtime.alloc(i32 71, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new1, i8** bitcast ([71 x i8]** @main.array2 to i8**), align 4 + %new2 = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 67 to i8*), i8* undef, i8* null) #0 + store i8* %new2, i8** bitcast ([3 x i8*]** @main.array3 to i8**), align 4 + ret void +} + +; Function Attrs: nounwind +define hidden void @main.newStruct(i8* %context, i8* %parentHandle) unnamed_addr #0 { +entry: + %new = call i8* @runtime.alloc(i32 0, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new, i8** bitcast ({}** @main.struct1 to i8**), align 4 + %new1 = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %new1, i8** bitcast ({ i32, i32 }** @main.struct2 to i8**), align 4 + %new2 = call i8* @runtime.alloc(i32 248, i8* bitcast ({ i32, [8 x i8] }* @"runtime/gc.layout:62-2000000000000001" to i8*), i8* undef, i8* null) #0 + store i8* %new2, i8** bitcast ({ i8*, [60 x i32], i8* }** @main.struct3 to i8**), align 4 + ret void +} + +; Function Attrs: nounwind +define hidden %runtime.funcValue* @main.newFuncValue(i8* %context, i8* %parentHandle) unnamed_addr #0 { +entry: + %new = call i8* @runtime.alloc(i32 8, i8* nonnull inttoptr (i32 197 to i8*), i8* undef, i8* null) #0 + %0 = bitcast i8* %new to %runtime.funcValue* + ret %runtime.funcValue* %0 +} + +; Function Attrs: nounwind +define hidden void @main.makeSlice(i8* %context, i8* %parentHandle) unnamed_addr #0 { +entry: + %makeslice = call i8* @runtime.alloc(i32 5, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 + store i8* %makeslice, i8** getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 0), align 8 + store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 1), align 4 + store i32 5, i32* getelementptr inbounds ({ i8*, i32, i32 }, { i8*, i32, i32 }* @main.slice1, i32 0, i32 2), align 8 + %makeslice1 = call i8* @runtime.alloc(i32 20, i8* nonnull inttoptr (i32 67 to i8*), i8* undef, i8* null) #0 + store i8* %makeslice1, i8** bitcast ({ i32**, i32, i32 }* @main.slice2 to i8**), align 8 + store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 1), align 4 + store i32 5, i32* getelementptr inbounds ({ i32**, i32, i32 }, { i32**, i32, i32 }* @main.slice2, i32 0, i32 2), align 8 + %makeslice3 = call i8* @runtime.alloc(i32 60, i8* nonnull inttoptr (i32 71 to i8*), i8* undef, i8* null) #0 + store i8* %makeslice3, i8** bitcast ({ { i8*, i32, i32 }*, i32, i32 }* @main.slice3 to i8**), align 8 + store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 1), align 4 + store i32 5, i32* getelementptr inbounds ({ { i8*, i32, i32 }*, i32, i32 }, { { i8*, i32, i32 }*, i32, i32 }* @main.slice3, i32 0, i32 2), align 8 + ret void +} + +; Function Attrs: nounwind +define hidden %runtime._interface @main.makeInterface(double %v.r, double %v.i, i8* %context, i8* %parentHandle) unnamed_addr #0 { +entry: + %0 = call i8* @runtime.alloc(i32 16, i8* null, i8* undef, i8* null) #0 + %.repack = bitcast i8* %0 to double* + store double %v.r, double* %.repack, align 8 + %.repack1 = getelementptr inbounds i8, i8* %0, i32 8 + %1 = bitcast i8* %.repack1 to double* + store double %v.i, double* %1, align 8 + %2 = insertvalue %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:complex128" to i32), i8* undef }, i8* %0, 1 + ret %runtime._interface %2 +} + +attributes #0 = { nounwind } diff --git a/compiler/testdata/go1.17.ll b/compiler/testdata/go1.17.ll index 3e131236..5c26166a 100644 --- a/compiler/testdata/go1.17.ll +++ b/compiler/testdata/go1.17.ll @@ -46,7 +46,7 @@ declare void @runtime.sliceToArrayPointerPanic(i8*, i8*) ; Function Attrs: nounwind define hidden [4 x i32]* @main.SliceToArrayConst(i8* %context, i8* %parentHandle) unnamed_addr #0 { entry: - %makeslice = call i8* @runtime.alloc(i32 24, i8* null, i8* undef, i8* null) #0 + %makeslice = call i8* @runtime.alloc(i32 24, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 br i1 false, label %slicetoarray.throw, label %slicetoarray.next slicetoarray.throw: ; preds = %entry diff --git a/compiler/testdata/goroutine-cortex-m-qemu.ll b/compiler/testdata/goroutine-cortex-m-qemu.ll index efc51861..079d47f6 100644 --- a/compiler/testdata/goroutine-cortex-m-qemu.ll +++ b/compiler/testdata/goroutine-cortex-m-qemu.ll @@ -66,7 +66,7 @@ entry: ; Function Attrs: nounwind define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 { entry: - %n = call i8* @runtime.alloc(i32 4, i8* null, i8* undef, i8* null) #0 + %n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 %0 = bitcast i8* %n to i32* store i32 3, i32* %0, align 4 %1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef, i8* null) #0 diff --git a/compiler/testdata/goroutine-wasm.ll b/compiler/testdata/goroutine-wasm.ll index 4a346c11..553eb1cc 100644 --- a/compiler/testdata/goroutine-wasm.ll +++ b/compiler/testdata/goroutine-wasm.ll @@ -51,7 +51,7 @@ entry: ; Function Attrs: nounwind define hidden void @main.closureFunctionGoroutine(i8* %context, i8* %parentHandle) unnamed_addr #0 { entry: - %n = call i8* @runtime.alloc(i32 4, i8* null, i8* undef, i8* null) #0 + %n = call i8* @runtime.alloc(i32 4, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 %0 = bitcast i8* %n to i32* store i32 3, i32* %0, align 4 %1 = call i8* @runtime.alloc(i32 8, i8* null, i8* undef, i8* null) #0 diff --git a/compiler/testdata/slice.ll b/compiler/testdata/slice.ll index da257c4c..847d29fb 100644 --- a/compiler/testdata/slice.ll +++ b/compiler/testdata/slice.ll @@ -44,7 +44,7 @@ declare void @runtime.lookupPanic(i8*, i8*) ; Function Attrs: nounwind define hidden { i32*, i32, i32 } @main.sliceAppendValues(i32* %ints.data, i32 %ints.len, i32 %ints.cap, i8* %context, i8* %parentHandle) unnamed_addr #0 { entry: - %varargs = call i8* @runtime.alloc(i32 12, i8* null, i8* undef, i8* null) #0 + %varargs = call i8* @runtime.alloc(i32 12, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 %0 = bitcast i8* %varargs to i32* store i32 1, i32* %0, align 4 %1 = getelementptr inbounds i8, i8* %varargs, i32 4 @@ -105,7 +105,7 @@ slice.throw: ; preds = %entry unreachable slice.next: ; preds = %entry - %makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* null, i8* undef, i8* null) #0 + %makeslice.buf = call i8* @runtime.alloc(i32 %len, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 %0 = insertvalue { i8*, i32, i32 } undef, i8* %makeslice.buf, 0 %1 = insertvalue { i8*, i32, i32 } %0, i32 %len, 1 %2 = insertvalue { i8*, i32, i32 } %1, i32 %len, 2 @@ -126,7 +126,7 @@ slice.throw: ; preds = %entry slice.next: ; preds = %entry %makeslice.cap = shl i32 %len, 1 - %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* null, i8* undef, i8* null) #0 + %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 %makeslice.array = bitcast i8* %makeslice.buf to i16* %0 = insertvalue { i16*, i32, i32 } undef, i16* %makeslice.array, 0 %1 = insertvalue { i16*, i32, i32 } %0, i32 %len, 1 @@ -146,7 +146,7 @@ slice.throw: ; preds = %entry slice.next: ; preds = %entry %makeslice.cap = mul i32 %len, 3 - %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* null, i8* undef, i8* null) #0 + %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 %makeslice.array = bitcast i8* %makeslice.buf to [3 x i8]* %0 = insertvalue { [3 x i8]*, i32, i32 } undef, [3 x i8]* %makeslice.array, 0 %1 = insertvalue { [3 x i8]*, i32, i32 } %0, i32 %len, 1 @@ -166,7 +166,7 @@ slice.throw: ; preds = %entry slice.next: ; preds = %entry %makeslice.cap = shl i32 %len, 2 - %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* null, i8* undef, i8* null) #0 + %makeslice.buf = call i8* @runtime.alloc(i32 %makeslice.cap, i8* nonnull inttoptr (i32 3 to i8*), i8* undef, i8* null) #0 %makeslice.array = bitcast i8* %makeslice.buf to i32* %0 = insertvalue { i32*, i32, i32 } undef, i32* %makeslice.array, 0 %1 = insertvalue { i32*, i32, i32 } %0, i32 %len, 1