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