runtime: require explicit GC layouts

This commit is contained in:
Jake Bailey
2026-08-07 10:21:19 -07:00
committed by Ron Evans
parent 1a4cb2032e
commit a7360d5ad3
28 changed files with 279 additions and 108 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
target package code rodata data bss target package code rodata data bss
hifive1b examples/echo 4321 323 0 2268 hifive1b examples/echo 4321 323 0 2268
microbit examples/serial 2842 382 8 2264 microbit examples/serial 2842 382 8 2264
wioterminal examples/pininterrupt 8039 1665 132 7496 wioterminal examples/pininterrupt 8039 1669 132 7496
+4 -2
View File
@@ -14,8 +14,10 @@ import (
) )
func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value { func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
elementSize := b.targetData.TypeAllocSize(b.getLLVMType(expr.Type().Underlying().(*types.Chan).Elem())) elementType := b.getLLVMType(expr.Type().Underlying().(*types.Chan).Elem())
elementSize := b.targetData.TypeAllocSize(elementType)
elementSizeValue := llvm.ConstInt(b.uintptrType, elementSize, false) elementSizeValue := llvm.ConstInt(b.uintptrType, elementSize, false)
elementLayout := b.createObjectLayout(elementType, expr.Pos())
bufSize := b.getValue(expr.Size, getPos(expr)) bufSize := b.getValue(expr.Size, getPos(expr))
b.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos()) b.createChanBoundsCheck(elementSize, bufSize, expr.Size.Type().Underlying().(*types.Basic), expr.Pos())
if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() { if bufSize.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
@@ -23,7 +25,7 @@ func (b *builder) createMakeChan(expr *ssa.MakeChan) llvm.Value {
} else if bufSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() { } else if bufSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
bufSize = b.CreateTrunc(bufSize, b.uintptrType, "") bufSize = b.CreateTrunc(bufSize, b.uintptrType, "")
} }
return b.createRuntimeCall("chanMake", []llvm.Value{elementSizeValue, bufSize}, "") return b.createRuntimeCall("chanMake", []llvm.Value{elementSizeValue, bufSize, elementLayout}, "")
} }
// createChanSend emits a pseudo chan send operation. It is lowered to the // createChanSend emits a pseudo chan send operation. It is lowered to the
+7 -1
View File
@@ -284,6 +284,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]), types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
types.NewVar(token.NoPos, nil, "sliceOf", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "sliceOf", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "layout", types.Typ[types.UnsafePointer]),
) )
case *types.Map: case *types.Map:
typeFieldTypes = append(typeFieldTypes, typeFieldTypes = append(typeFieldTypes,
@@ -291,6 +292,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "keyType", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "keyType", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "hashmapTypeInfo", types.Typ[types.UnsafePointer]),
) )
case *types.Struct: case *types.Struct:
typeFieldTypes = append(typeFieldTypes, typeFieldTypes = append(typeFieldTypes,
@@ -299,6 +301,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]), types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uint32]), types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uint32]),
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]), types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
types.NewVar(token.NoPos, nil, "layout", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))), types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
) )
if len(methods) > 0 { if len(methods) > 0 {
@@ -418,6 +421,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.getTypeCode(typ.Elem()), // elementType c.getTypeCode(typ.Elem()), // elementType
llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length llvm.ConstInt(c.uintptrType, uint64(typ.Len()), false), // length
c.getTypeCode(types.NewSlice(typ.Elem())), // slicePtr c.getTypeCode(types.NewSlice(typ.Elem())), // slicePtr
c.createObjectLayout(c.getLLVMType(typ), token.NoPos), // layout
} }
case *types.Map: case *types.Map:
typeFields = []llvm.Value{ typeFields = []llvm.Value{
@@ -425,6 +429,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
c.getTypeCode(types.NewPointer(typ)), // ptrTo c.getTypeCode(types.NewPointer(typ)), // ptrTo
c.getTypeCode(typ.Elem()), // elem c.getTypeCode(typ.Elem()), // elem
c.getTypeCode(typ.Key()), // key c.getTypeCode(typ.Key()), // key
c.getHashmapTypeInfo(typ, token.NoPos), // hashmapTypeInfo
} }
case *types.Struct: case *types.Struct:
var pkgpath string var pkgpath string
@@ -450,6 +455,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
pkgPathPtr, pkgPathPtr,
llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size llvm.ConstInt(c.ctx.Int32Type(), uint64(size), false), // size
llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields llvm.ConstInt(c.ctx.Int16Type(), uint64(typ.NumFields()), false), // numFields
c.createObjectLayout(llvmStructType, token.NoPos), // layout
} }
structFieldType := c.getLLVMRuntimeType("structField") structFieldType := c.getLLVMRuntimeType("structField")
@@ -510,7 +516,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))} typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
// TODO: params, return values, etc // TODO: params, return values, etc
} }
// Prepend metadata byte. // Prepend the common RawType field.
typeFields = append([]llvm.Value{ typeFields = append([]llvm.Value{
llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false), llvm.ConstInt(c.ctx.Int8Type(), uint64(metabyte), false),
}, typeFields...) }, typeFields...)
+69
View File
@@ -13,6 +13,12 @@ import (
const hashArrayUnrollLimit = 4 const hashArrayUnrollLimit = 4
const (
hashmapBucketSlots = 8
hashmapMaxKeySize = 128
hashmapMaxValueSize = 128
)
// createMakeMap creates a new map object (runtime.hashmap) by allocating and // createMakeMap creates a new map object (runtime.hashmap) by allocating and
// initializing an appropriately sized object. // initializing an appropriately sized object.
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) { func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
@@ -25,6 +31,8 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
valueSize := b.targetData.TypeAllocSize(llvmValueType) valueSize := b.targetData.TypeAllocSize(llvmValueType)
llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false) llvmKeySize := llvm.ConstInt(b.uintptrType, keySize, false)
llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false) llvmValueSize := llvm.ConstInt(b.uintptrType, valueSize, false)
mapLayout := b.getHashmapTypeInfo(mapType, expr.Pos())
sizeHint := llvm.ConstInt(b.uintptrType, 8, false) sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
if expr.Reserve != nil { if expr.Reserve != nil {
sizeHint = b.getValue(expr.Reserve, getPos(expr)) sizeHint = b.getValue(expr.Reserve, getPos(expr))
@@ -54,11 +62,72 @@ func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
hashmap := b.createRuntimeCall("hashmapMakeGeneric", []llvm.Value{ hashmap := b.createRuntimeCall("hashmapMakeGeneric", []llvm.Value{
llvmKeySize, llvmValueSize, sizeHint, llvmKeySize, llvmValueSize, sizeHint,
mapLayout,
hashFn, equalFn, hashFn, equalFn,
}, "") }, "")
return hashmap, nil return hashmap, nil
} }
func (c *compilerContext) getHashmapTypeInfo(mapType *types.Map, pos token.Pos) llvm.Value {
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
llvmValueType := c.getLLVMType(mapType.Elem().Underlying())
keySize := c.targetData.TypeAllocSize(llvmKeyType)
valueSize := c.targetData.TypeAllocSize(llvmValueType)
keyLayout := c.createObjectLayout(llvmKeyType, pos)
valueLayout := c.createObjectLayout(llvmValueType, pos)
llvmKeySlotType := llvmKeyType
if keySize > hashmapMaxKeySize {
llvmKeySlotType = c.dataPtrType
}
llvmValueSlotType := llvmValueType
if valueSize > hashmapMaxValueSize {
llvmValueSlotType = c.dataPtrType
}
// Keep this in sync with runtime.hashmapBucket and
// runtime.hashmapBucketHeaderSize.
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
headerSize := (uint64(8) + pointerSize + 7) &^ 7
headerPadding := headerSize - uint64(8) - pointerSize
bucketFields := []llvm.Type{
llvm.ArrayType(c.ctx.Int8Type(), hashmapBucketSlots),
c.dataPtrType,
}
if headerPadding != 0 {
bucketFields = append(bucketFields, llvm.ArrayType(c.ctx.Int8Type(), int(headerPadding)))
}
bucketFields = append(bucketFields,
llvm.ArrayType(llvmKeySlotType, hashmapBucketSlots),
llvm.ArrayType(llvmValueSlotType, hashmapBucketSlots),
)
bucketType := c.ctx.StructType(bucketFields, true)
bucketSize := headerSize +
c.targetData.TypeAllocSize(llvmKeySlotType)*hashmapBucketSlots +
c.targetData.TypeAllocSize(llvmValueSlotType)*hashmapBucketSlots
if c.targetData.TypeAllocSize(bucketType) != bucketSize {
panic("compiler hashmap bucket layout does not match runtime")
}
bucketLayout := c.createObjectLayout(bucketType, pos)
mapLayoutName := "runtime.hashmapType:" +
hashmapCanonicalTypeName(mapType.Key()) + ":" +
hashmapCanonicalTypeName(mapType.Elem())
mapLayout := c.mod.NamedGlobal(mapLayoutName)
if mapLayout.IsNil() {
initializer := c.ctx.ConstStruct([]llvm.Value{
keyLayout,
valueLayout,
bucketLayout,
}, false)
mapLayout = llvm.AddGlobal(c.mod, initializer.Type(), mapLayoutName)
mapLayout.SetInitializer(initializer)
mapLayout.SetGlobalConstant(true)
mapLayout.SetUnnamedAddr(true)
mapLayout.SetLinkage(llvm.LinkOnceODRLinkage)
}
return mapLayout
}
// getRuntimeFunctionValue returns a TinyGo function value (with nil context) // getRuntimeFunctionValue returns a TinyGo function value (with nil context)
// for the named runtime function. // for the named runtime function.
func (b *builder) getRuntimeFunctionValue(name string, sig *types.Signature) llvm.Value { func (b *builder) getRuntimeFunctionValue(name string, sig *types.Signature) llvm.Value {
+2 -2
View File
@@ -166,13 +166,13 @@ entry:
} }
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden void @main.clearMap(ptr dereferenceable_or_null(48) %m, ptr %context) unnamed_addr #1 { define hidden void @main.clearMap(ptr dereferenceable_or_null(52) %m, ptr %context) unnamed_addr #1 {
entry: entry:
call void @runtime.hashmapClear(ptr %m, ptr undef) #4 call void @runtime.hashmapClear(ptr %m, ptr undef) #4
ret void ret void
} }
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(48), ptr) #0 declare void @runtime.hashmapClear(ptr dereferenceable_or_null(52), ptr) #0
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" } attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
+1 -1
View File
@@ -14,7 +14,7 @@ target triple = "wasm32-unknown-wasi"
@"main$string" = internal unnamed_addr constant [18 x i8] c"main.genericMethod", align 1 @"main$string" = internal unnamed_addr constant [18 x i8] c"main.genericMethod", align 1
@"main$string.1" = internal unnamed_addr constant [7 x i8] c"Regular", align 1 @"main$string.1" = internal unnamed_addr constant [7 x i8] c"Regular", align 1
@"pointer:named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(*main.genericMethod).Regular" } } @"pointer:named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(*main.genericMethod).Regular" } }
@"reflect/types.type:struct:{}" = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, [0 x %runtime.structField] } { i8 90, i16 0, ptr @"reflect/types.type:pointer:struct:{}", ptr @"reflect/types.type.pkgpath.empty", i32 0, i16 0, [0 x %runtime.structField] zeroinitializer }, align 4 @"reflect/types.type:struct:{}" = linkonce_odr constant { i8, i16, ptr, ptr, i32, i16, ptr, [0 x %runtime.structField] } { i8 90, i16 0, ptr @"reflect/types.type:pointer:struct:{}", ptr @"reflect/types.type.pkgpath.empty", i32 0, i16 0, ptr inttoptr (i32 3 to ptr), [0 x %runtime.structField] zeroinitializer }, align 4
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1 @"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
@"reflect/types.type:pointer:struct:{}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:struct:{}" }, align 4 @"reflect/types.type:pointer:struct:{}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:struct:{}" }, align 4
@"named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(main.genericMethod).Regular$invoke" } } @"named:main.genericMethod$methodset" = linkonce_odr unnamed_addr constant { i32, [1 x ptr], { ptr } } { i32 1, [1 x ptr] [ptr @"reflect/methods.Regular:func:{basic:int}{basic:int}"], { ptr } { ptr @"(main.genericMethod).Regular$invoke" } }
+5 -4
View File
@@ -9,6 +9,7 @@ target triple = "wasm32-unknown-wasi"
@"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002" = linkonce_odr unnamed_addr constant { i32, [33 x i8] } { i32 258, [33 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\02" } @"runtime/gc.layout:258-000000000000000000000000000000000000000000000000000000000000000002" = linkonce_odr unnamed_addr constant { i32, [33 x i8] } { i32 258, [33 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\02" }
@"reflect/types.typeid:named:main.largeValue" = external constant i8 @"reflect/types.typeid:named:main.largeValue" = external constant i8
@"runtime.hashmapType:[1025]byte:[1025]byte" = linkonce_odr unnamed_addr constant { ptr, ptr, ptr } { ptr inttoptr (i32 3 to ptr), ptr inttoptr (i32 3 to ptr), ptr inttoptr (i32 67108137 to ptr) }
@llvm.used = appending global [15 x ptr] [ptr @"(main.largeReceiver).makeLargeValue", ptr @"(main.largeReceiver).readLargeValue", ptr @main.makeLargeValue, ptr @main.makeZeroLargeValue, ptr @main.readLargeValue, ptr @main.deferLargeValue, ptr @main.goLargeValue, ptr @main.makeLargeResults, ptr @main.makeTwoLargeResults, ptr @main.makeMixedLargeResults, ptr @main.chooseLargeValue, ptr @main.makePointerLargeValue, ptr @main.useLargeMap, ptr @main.useLargeChannel, ptr @main.selectLargeChannel] @llvm.used = appending global [15 x ptr] [ptr @"(main.largeReceiver).makeLargeValue", ptr @"(main.largeReceiver).readLargeValue", ptr @main.makeLargeValue, ptr @main.makeZeroLargeValue, ptr @main.readLargeValue, ptr @main.deferLargeValue, ptr @main.goLargeValue, ptr @main.makeLargeResults, ptr @main.makeTwoLargeResults, ptr @main.makeMixedLargeResults, ptr @main.chooseLargeValue, ptr @main.makePointerLargeValue, ptr @main.useLargeMap, ptr @main.useLargeChannel, ptr @main.selectLargeChannel]
@"main$string" = internal unnamed_addr constant [31 x i8] c"blocking select matched no case", align 1 @"main$string" = internal unnamed_addr constant [31 x i8] c"blocking select matched no case", align 1
@"main$pack" = internal unnamed_addr constant { %runtime._string } { %runtime._string { ptr @"main$string", i32 31 } } @"main$pack" = internal unnamed_addr constant { %runtime._string } { %runtime._string { ptr @"main$string", i32 31 } }
@@ -350,7 +351,7 @@ declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #7
define hidden i8 @main.useLargeMap(ptr readonly dereferenceable_or_null(1025) %key, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 { define hidden i8 @main.useLargeMap(ptr readonly dereferenceable_or_null(1025) %key, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 1025, i32 1025, i32 1, ptr null, ptr nonnull @runtime.hash32, ptr null, ptr nonnull @runtime.memequal, ptr undef) #9 %0 = call ptr @runtime.hashmapMakeGeneric(i32 1025, i32 1025, i32 1, ptr nonnull @"runtime.hashmapType:[1025]byte:[1025]byte", ptr null, ptr nonnull @runtime.hash32, ptr null, ptr nonnull @runtime.memequal, ptr undef) #9
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #9 call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #9
call void @runtime.hashmapBinarySet(ptr %0, ptr %key, ptr %value, ptr undef) #9 call void @runtime.hashmapBinarySet(ptr %0, ptr %key, ptr %value, ptr undef) #9
%result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9 %result = call align 1 dereferenceable(1025) ptr @runtime.alloc(i32 1025, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #9
@@ -381,11 +382,11 @@ declare i32 @runtime.hash32(ptr, i32, i32, ptr) #0
declare i1 @runtime.memequal(ptr, ptr, i32, ptr) #0 declare i1 @runtime.memequal(ptr, ptr, i32, ptr) #0
declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr) #0 declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr) #0
declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(48), ptr, ptr, ptr) #0 declare void @runtime.hashmapBinarySet(ptr dereferenceable_or_null(52), ptr, ptr, ptr) #0
declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(48), ptr, ptr, i32, ptr) #0 declare i1 @runtime.hashmapBinaryGet(ptr dereferenceable_or_null(52), ptr, ptr, i32, ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
define hidden i8 @main.useLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 { define hidden i8 @main.useLargeChannel(ptr dereferenceable_or_null(36) %ch, ptr readonly dereferenceable_or_null(1025) %value, ptr %context) unnamed_addr #1 {
+16 -10
View File
@@ -6,6 +6,12 @@ target triple = "wasm32-unknown-wasi"
%main.hasPadding = type { i1, i32, i1 } %main.hasPadding = type { i1, i32, i1 }
%runtime._string = type { ptr, i32 } %runtime._string = type { ptr, i32 }
@"runtime/gc.layout:44-545555550500" = linkonce_odr unnamed_addr constant { i32, [6 x i8] } { i32 44, [6 x i8] c"TUUU\05\00" }
@"runtime.hashmapType:struct{string; string}:int" = linkonce_odr unnamed_addr constant { ptr, ptr, ptr } { ptr inttoptr (i32 329 to ptr), ptr inttoptr (i32 3 to ptr), ptr @"runtime/gc.layout:44-545555550500" }
@"runtime.hashmapType:[2]string:int" = linkonce_odr unnamed_addr constant { ptr, ptr, ptr } { ptr inttoptr (i32 69 to ptr), ptr inttoptr (i32 3 to ptr), ptr @"runtime/gc.layout:44-545555550500" }
@"runtime/gc.layout:92-545555555555555555550500" = linkonce_odr unnamed_addr constant { i32, [12 x i8] } { i32 92, [12 x i8] c"TUUUUUUUUU\05\00" }
@"runtime.hashmapType:[5]string:int" = linkonce_odr unnamed_addr constant { ptr, ptr, ptr } { ptr inttoptr (i32 69 to ptr), ptr inttoptr (i32 3 to ptr), ptr @"runtime/gc.layout:92-545555555555555555550500" }
declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0 declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
; Function Attrs: nounwind ; Function Attrs: nounwind
@@ -15,7 +21,7 @@ entry:
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 { define hidden i32 @main.testZeroGet(ptr dereferenceable_or_null(52) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry: entry:
%hashmap.key = alloca %main.hasPadding, align 8 %hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
@@ -35,13 +41,13 @@ entry:
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.start.p0(ptr nocapture) #3 declare void @llvm.lifetime.start.p0(ptr nocapture) #3
declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, i32, ptr) #0 declare i1 @runtime.hashmapGenericGet(ptr dereferenceable_or_null(52), ptr nocapture, ptr nocapture, i32, ptr) #0
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite)
declare void @llvm.lifetime.end.p0(ptr nocapture) #3 declare void @llvm.lifetime.end.p0(ptr nocapture) #3
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.testZeroSet(ptr dereferenceable_or_null(48) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 { define hidden void @main.testZeroSet(ptr dereferenceable_or_null(52) %m, i1 %s.b1, i32 %s.i, i1 %s.b2, ptr %context) unnamed_addr #2 {
entry: entry:
%hashmap.key = alloca %main.hasPadding, align 8 %hashmap.key = alloca %main.hasPadding, align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
@@ -58,10 +64,10 @@ entry:
ret void ret void
} }
declare void @runtime.hashmapGenericSet(ptr dereferenceable_or_null(48), ptr nocapture, ptr nocapture, ptr) #0 declare void @runtime.hashmapGenericSet(ptr dereferenceable_or_null(52), ptr nocapture, ptr nocapture, ptr) #0
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 { define hidden i32 @main.testZeroArrayGet(ptr dereferenceable_or_null(52) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry: entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8 %hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
@@ -80,7 +86,7 @@ entry:
} }
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(48) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 { define hidden void @main.testZeroArraySet(ptr dereferenceable_or_null(52) %m, [2 x %main.hasPadding] %s, ptr %context) unnamed_addr #2 {
entry: entry:
%hashmap.key = alloca [2 x %main.hasPadding], align 8 %hashmap.key = alloca [2 x %main.hasPadding], align 8
%hashmap.value = alloca i32, align 4 %hashmap.value = alloca i32, align 4
@@ -102,7 +108,7 @@ entry:
define hidden ptr @main.makeStringStructMap(ptr %context) unnamed_addr #2 { define hidden ptr @main.makeStringStructMap(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 16, i32 4, i32 8, ptr null, ptr nonnull @"hashmapKeyHash.struct{string; string}", ptr null, ptr nonnull @"hashmapKeyEqual.struct{string; string}", ptr undef) #4 %0 = call ptr @runtime.hashmapMakeGeneric(i32 16, i32 4, i32 8, ptr nonnull @"runtime.hashmapType:struct{string; string}:int", ptr null, ptr nonnull @"hashmapKeyHash.struct{string; string}", ptr null, ptr nonnull @"hashmapKeyEqual.struct{string; string}", ptr undef) #4
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4 call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4
ret ptr %0 ret ptr %0
} }
@@ -145,13 +151,13 @@ entry:
declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0 declare i1 @runtime.stringEqual(ptr readonly, i32, ptr readonly, i32, ptr) #0
declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr) #0 declare ptr @runtime.hashmapMakeGeneric(i32, i32, i32, ptr, ptr, ptr, ptr, ptr, ptr) #0
; Function Attrs: noinline nounwind ; Function Attrs: noinline nounwind
define hidden ptr @main.makeShortStringArrayMap(ptr %context) unnamed_addr #2 { define hidden ptr @main.makeShortStringArrayMap(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 16, i32 4, i32 8, ptr null, ptr nonnull @"hashmapKeyHash.[2]string", ptr null, ptr nonnull @"hashmapKeyEqual.[2]string", ptr undef) #4 %0 = call ptr @runtime.hashmapMakeGeneric(i32 16, i32 4, i32 8, ptr nonnull @"runtime.hashmapType:[2]string:int", ptr null, ptr nonnull @"hashmapKeyHash.[2]string", ptr null, ptr nonnull @"hashmapKeyEqual.[2]string", ptr undef) #4
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4 call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4
ret ptr %0 ret ptr %0
} }
@@ -194,7 +200,7 @@ entry:
define hidden ptr @main.makeLongStringArrayMap(ptr %context) unnamed_addr #2 { define hidden ptr @main.makeLongStringArrayMap(ptr %context) unnamed_addr #2 {
entry: entry:
%stackalloc = alloca i8, align 1 %stackalloc = alloca i8, align 1
%0 = call ptr @runtime.hashmapMakeGeneric(i32 40, i32 4, i32 8, ptr null, ptr nonnull @"hashmapKeyHash.[5]string", ptr null, ptr nonnull @"hashmapKeyEqual.[5]string", ptr undef) #4 %0 = call ptr @runtime.hashmapMakeGeneric(i32 40, i32 4, i32 8, ptr nonnull @"runtime.hashmapType:[5]string:int", ptr null, ptr nonnull @"hashmapKeyHash.[5]string", ptr null, ptr nonnull @"hashmapKeyEqual.[5]string", ptr undef) #4
call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4 call void @runtime.trackPointer(ptr %0, ptr nonnull %stackalloc, ptr undef) #4
ret ptr %0 ret ptr %0
} }
+4 -8
View File
@@ -1278,13 +1278,14 @@ func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
// integer value, or can be nil. // integer value, or can be nil.
ptr, err := layoutValue.asPointer(r) ptr, err := layoutValue.asPointer(r)
if err == errIntegerAsPointer { if err == errIntegerAsPointer {
// It's an integer, which means it's a small object or unknown. // It's an integer, which means it's a small object.
layout := layoutValue.Uint(r) layout := layoutValue.Uint(r)
if layout == 0 { if layout == 0 {
// Nil pointer, which means the layout is unknown. panic("runtime.alloc called without a GC layout")
return 0, nil
} }
if layout%2 != 1 { if layout%2 != 1 {
// Conservative layouts are reserved for stack storage and cannot
// reach interpreted heap allocations.
// Sanity check: the least significant bit must be set. This is how // Sanity check: the least significant bit must be set. This is how
// the runtime can separate pointers from integers. // the runtime can separate pointers from integers.
panic("unexpected layout") panic("unexpected layout")
@@ -1331,11 +1332,6 @@ func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
// have some additional repetition, for example in the buffer of a slice. // have some additional repetition, for example in the buffer of a slice.
func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type { func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type {
objectSizeWords, bitmap := r.readObjectLayout(layoutValue) objectSizeWords, bitmap := r.readObjectLayout(layoutValue)
if bitmap == nil {
// No information available.
return llvm.Type{}
}
if bitmap.BitLen() == 0 { if bitmap.BitLen() == 0 {
// There are no pointers in this object, so treat this as a raw byte // There are no pointers in this object, so treat this as a raw byte
// buffer. This is important because objects without pointers may have // buffer. This is important because objects without pointers may have
+5
View File
@@ -11,6 +11,7 @@ target triple = "wasm32--wasi"
@layout3 = global ptr null @layout3 = global ptr null
@layout4 = global ptr null @layout4 = global ptr null
@bigobj1 = global ptr null @bigobj1 = global ptr null
@pointerFree10 = global ptr null
declare ptr @runtime.alloc(i32, ptr) unnamed_addr declare ptr @runtime.alloc(i32, ptr) unnamed_addr
@@ -49,5 +50,9 @@ define internal void @main.init() unnamed_addr {
; Large object that needs to be stored in a separate global. ; Large object that needs to be stored in a separate global.
%bigobj1 = call ptr @runtime.alloc(i32 248, ptr @"runtime/gc.layout:62-2000000000000001") %bigobj1 = call ptr @runtime.alloc(i32 248, ptr @"runtime/gc.layout:62-2000000000000001")
store ptr %bigobj1, ptr @bigobj1 store ptr %bigobj1, ptr @bigobj1
; Another pointer-free object.
%pointerFree10 = call ptr @runtime.alloc(i32 10, ptr inttoptr (i32 3 to ptr))
store ptr %pointerFree10, ptr @pointerFree10
ret void ret void
} }
+2
View File
@@ -10,6 +10,7 @@ target triple = "wasm32--wasi"
@layout3 = local_unnamed_addr global ptr @"main$alloc.6" @layout3 = local_unnamed_addr global ptr @"main$alloc.6"
@layout4 = local_unnamed_addr global ptr @"main$alloc.7" @layout4 = local_unnamed_addr global ptr @"main$alloc.7"
@bigobj1 = local_unnamed_addr global ptr @"main$alloc.8" @bigobj1 = local_unnamed_addr global ptr @"main$alloc.8"
@pointerFree10 = local_unnamed_addr global ptr @"main$alloc.9"
@"main$alloc" = internal global [12 x i8] zeroinitializer, align 4 @"main$alloc" = internal global [12 x i8] zeroinitializer, align 4
@"main$alloc.1" = internal global [7 x i8] zeroinitializer, align 4 @"main$alloc.1" = internal global [7 x i8] zeroinitializer, align 4
@"main$alloc.2" = internal global [3 x i8] zeroinitializer, align 4 @"main$alloc.2" = internal global [3 x i8] zeroinitializer, align 4
@@ -19,6 +20,7 @@ target triple = "wasm32--wasi"
@"main$alloc.6" = internal global { ptr, ptr, ptr, i32, i32, ptr, ptr, i32, i32, i32, i32, i32, i32, ptr, ptr, i32, i32, i32, ptr, ptr, i32, i32, ptr, i32, i32, ptr } zeroinitializer, align 4 @"main$alloc.6" = internal global { ptr, ptr, ptr, i32, i32, ptr, ptr, i32, i32, i32, i32, i32, i32, ptr, ptr, i32, i32, i32, ptr, ptr, i32, i32, ptr, i32, i32, ptr } zeroinitializer, align 4
@"main$alloc.7" = internal global [3 x { ptr, ptr, ptr, i32, i32, ptr, ptr, i32, i32, i32, i32, i32, i32, ptr, ptr, i32, i32, i32, ptr, ptr, i32, i32, ptr, i32, i32, ptr }] zeroinitializer, align 4 @"main$alloc.7" = internal global [3 x { ptr, ptr, ptr, i32, i32, ptr, ptr, i32, i32, i32, i32, i32, i32, ptr, ptr, i32, i32, i32, ptr, ptr, i32, i32, ptr, i32, i32, ptr }] zeroinitializer, align 4
@"main$alloc.8" = internal global { ptr, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, ptr } zeroinitializer, align 4 @"main$alloc.8" = internal global { ptr, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, ptr } zeroinitializer, align 4
@"main$alloc.9" = internal global [10 x i8] zeroinitializer, align 4
define void @runtime.initAll() unnamed_addr { define void @runtime.initAll() unnamed_addr {
ret void ret void
+5
View File
@@ -19,8 +19,13 @@ const (
NoPtrs = Layout((0 << sizeShift) | (1 << 1) | 1) NoPtrs = Layout((0 << sizeShift) | (1 << 1) | 1)
Pointer = Layout((1 << sizeShift) | ((unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1) Pointer = Layout((1 << sizeShift) | ((unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1)
PointerPair = Layout((3 << sizeShift) | ((2 * unsafe.Sizeof(unsafe.Pointer(nil)) / ptrAlign) << 1) | 1)
String = Layout((1 << sizeShift) | ((unsafe.Sizeof("") / ptrAlign) << 1) | 1) String = Layout((1 << sizeShift) | ((unsafe.Sizeof("") / ptrAlign) << 1) | 1)
Slice = Layout((1 << sizeShift) | ((unsafe.Sizeof([]byte{}) / ptrAlign) << 1) | 1) Slice = Layout((1 << sizeShift) | ((unsafe.Sizeof([]byte{}) / ptrAlign) << 1) | 1)
// Conservative is reserved for stack storage, which does not have an
// ordinary Go object layout.
Conservative = Layout(2)
) )
func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) } func (l Layout) AsPtr() unsafe.Pointer { return unsafe.Pointer(l) }
+27 -4
View File
@@ -166,6 +166,11 @@ type RawType struct {
meta uint8 // metadata byte, contains kind and flags (see constants above) meta uint8 // metadata byte, contains kind and flags (see constants above)
} }
type basicType struct {
RawType
ptrTo *RawType
}
// All types that have an element type: named, chan, slice, array, map (but not // All types that have an element type: named, chan, slice, array, map (but not
// pointer because it doesn't have ptrTo). // pointer because it doesn't have ptrTo).
type elemType struct { type elemType struct {
@@ -200,6 +205,7 @@ type arrayType struct {
elem *RawType elem *RawType
arrayLen uintptr arrayLen uintptr
slicePtr *RawType slicePtr *RawType
layout unsafe.Pointer
} }
type mapType struct { type mapType struct {
@@ -208,6 +214,7 @@ type mapType struct {
ptrTo *RawType ptrTo *RawType
elem *RawType elem *RawType
key *RawType key *RawType
typeInfo unsafe.Pointer
} }
// namedType is the type descriptor for named types. The numMethod field uses // namedType is the type descriptor for named types. The numMethod field uses
@@ -243,6 +250,7 @@ type structType struct {
pkgpath *byte pkgpath *byte
size uint32 size uint32
numField uint16 numField uint16
layout unsafe.Pointer
fields [1]structField // the remaining fields are all of type structField fields [1]structField // the remaining fields are all of type structField
// methods methodSet follows after fields, only when numMethod & numMethodHasMethodSet != 0 // methods methodSet follows after fields, only when numMethod & numMethodHasMethodSet != 0
} }
@@ -298,6 +306,8 @@ func pointerTo(t *RawType) *RawType {
} }
switch t.Kind() { switch t.Kind() {
case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr, Complex64, Complex128, Float32, Float64, String, UnsafePointer:
return (*basicType)(unsafe.Pointer(t)).ptrTo
case Pointer: case Pointer:
if tag := t.ptrtag(); tag < 3 { if tag := t.ptrtag(); tag < 3 {
return (*RawType)(unsafe.Add(unsafe.Pointer(t), 1)) return (*RawType)(unsafe.Add(unsafe.Pointer(t), 1))
@@ -306,6 +316,8 @@ func pointerTo(t *RawType) *RawType {
// TODO(dgryski): This is blocking https://github.com/tinygo-org/tinygo/issues/3131 // TODO(dgryski): This is blocking https://github.com/tinygo-org/tinygo/issues/3131
// We need to be able to create types that match existing types to prevent typecode equality. // We need to be able to create types that match existing types to prevent typecode equality.
panic("reflect: cannot make *****T type") panic("reflect: cannot make *****T type")
case Interface, Func:
return (*interfaceType)(unsafe.Pointer(t)).ptrTo
case Struct: case Struct:
return (*structType)(unsafe.Pointer(t)).ptrTo return (*structType)(unsafe.Pointer(t)).ptrTo
default: default:
@@ -729,6 +741,7 @@ func (t *RawType) Align() int {
} }
func (r *RawType) gcLayout() unsafe.Pointer { func (r *RawType) gcLayout() unsafe.Pointer {
r = r.underlying()
kind := r.Kind() kind := r.Kind()
if kind < String { if kind < String {
@@ -736,16 +749,26 @@ func (r *RawType) gcLayout() unsafe.Pointer {
} }
switch kind { switch kind {
case Pointer, UnsafePointer, Chan, Map:
return gclayout.Pointer.AsPtr()
case String: case String:
return gclayout.String.AsPtr() return gclayout.String.AsPtr()
case UnsafePointer, Chan, Pointer, Map:
return gclayout.Pointer.AsPtr()
case Interface, Func:
return gclayout.PointerPair.AsPtr()
case Slice: case Slice:
return gclayout.Slice.AsPtr() return gclayout.Slice.AsPtr()
case Array:
return (*arrayType)(unsafe.Pointer(r)).layout
case Struct:
return (*structType)(unsafe.Pointer(r)).layout
default:
panic("reflect: invalid GC layout kind")
}
} }
// Unknown (for now); let the conservative pointer scanning handle it func (r *RawType) hashmapTypeInfo() unsafe.Pointer {
return nil r = r.underlying()
return (*mapType)(unsafe.Pointer(r)).typeInfo
} }
// FieldAlign returns the alignment if this type is used in a struct field. It // FieldAlign returns the alignment if this type is used in a struct field. It
+14 -12
View File
@@ -1,6 +1,7 @@
package reflectlite package reflectlite
import ( import (
"internal/gclayout"
"math" "math"
"unsafe" "unsafe"
) )
@@ -1644,7 +1645,7 @@ func makeInt(flags valueFlags, bits uint64, t *RawType) Value {
ptr := unsafe.Pointer(&v.value) ptr := unsafe.Pointer(&v.value)
if size > unsafe.Sizeof(uintptr(0)) { if size > unsafe.Sizeof(uintptr(0)) {
ptr = alloc(size, nil) ptr = alloc(size, gclayout.NoPtrs.AsPtr())
v.value = ptr v.value = ptr
} }
@@ -1671,7 +1672,7 @@ func makeFloat(flags valueFlags, f float64, t *RawType) Value {
ptr := unsafe.Pointer(&v.value) ptr := unsafe.Pointer(&v.value)
if size > unsafe.Sizeof(uintptr(0)) { if size > unsafe.Sizeof(uintptr(0)) {
ptr = alloc(size, nil) ptr = alloc(size, gclayout.NoPtrs.AsPtr())
v.value = ptr v.value = ptr
} }
@@ -1703,7 +1704,7 @@ func makeComplex(flags valueFlags, f complex128, t *RawType) Value {
ptr := unsafe.Pointer(&v.value) ptr := unsafe.Pointer(&v.value)
if size > unsafe.Sizeof(uintptr(0)) { if size > unsafe.Sizeof(uintptr(0)) {
ptr = alloc(size, nil) ptr = alloc(size, gclayout.NoPtrs.AsPtr())
v.value = ptr v.value = ptr
} }
@@ -1834,7 +1835,7 @@ func Zero(typ Type) Value {
return Value{ return Value{
typecode: typ.(*RawType), typecode: typ.(*RawType),
value: alloc(size, nil), value: alloc(size, typ.(*RawType).gcLayout()),
flags: valueFlagExported | valueFlagRO, flags: valueFlagExported | valueFlagRO,
} }
} }
@@ -1844,7 +1845,7 @@ func Zero(typ Type) Value {
func New(typ Type) Value { func New(typ Type) Value {
return Value{ return Value{
typecode: pointerTo(typ.(*RawType)), typecode: pointerTo(typ.(*RawType)),
value: alloc(typ.Size(), nil), value: alloc(typ.Size(), typ.(*RawType).gcLayout()),
flags: valueFlagExported, flags: valueFlagExported,
} }
} }
@@ -2203,13 +2204,13 @@ func (v Value) FieldByNameFunc(match func(string) bool) Value {
} }
//go:linkname hashmapMake runtime.hashmapMake //go:linkname hashmapMake runtime.hashmapMake
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) unsafe.Pointer func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, typeInfo unsafe.Pointer, alg uint8) unsafe.Pointer
//go:linkname hashmapMakeReflect runtime.hashmapMakeReflect //go:linkname hashmapMakeReflect runtime.hashmapMakeReflect
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) unsafe.Pointer func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, typeInfo, keyType unsafe.Pointer) unsafe.Pointer
//go:linkname chanMake runtime.chanMake //go:linkname chanMake runtime.chanMake
func chanMake(elementSize uintptr, bufSize uintptr) unsafe.Pointer func chanMake(elementSize uintptr, bufSize uintptr, elementLayout unsafe.Pointer) unsafe.Pointer
// MakeMapWithSize creates a new map with the specified type and initial space // MakeMapWithSize creates a new map with the specified type and initial space
// for approximately n elements. // for approximately n elements.
@@ -2231,18 +2232,19 @@ func MakeMapWithSize(typ Type, n int) Value {
key := typ.Key().(*RawType) key := typ.Key().(*RawType)
val := typ.Elem().(*RawType) val := typ.Elem().(*RawType)
typeInfo := typ.(*RawType).hashmapTypeInfo()
var m unsafe.Pointer var m unsafe.Pointer
if key.Kind() == String { if key.Kind() == String {
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmString) m = hashmapMake(key.Size(), val.Size(), uintptr(n), typeInfo, hashmapAlgorithmString)
} else if key.isBinary() { } else if key.isBinary() {
m = hashmapMake(key.Size(), val.Size(), uintptr(n), hashmapAlgorithmBinary) m = hashmapMake(key.Size(), val.Size(), uintptr(n), typeInfo, hashmapAlgorithmBinary)
} else { } else {
// Composite key type (struct with strings, floats, etc.). // Composite key type (struct with strings, floats, etc.).
// Use runtime-generated hash/equal closures that walk the // Use runtime-generated hash/equal closures that walk the
// type structure, matching the compiler-generated functions. // type structure, matching the compiler-generated functions.
m = hashmapMakeReflect(key.Size(), val.Size(), uintptr(n), unsafe.Pointer(key)) m = hashmapMakeReflect(key.Size(), val.Size(), uintptr(n), typeInfo, unsafe.Pointer(key))
} }
return Value{ return Value{
@@ -2269,7 +2271,7 @@ func MakeChan(typ Type, size int) Value {
panic("reflect.MakeChan: unidirectional channel type") panic("reflect.MakeChan: unidirectional channel type")
} }
elem := typ.Elem().(*RawType) elem := typ.Elem().(*RawType)
ch := chanMake(elem.Size(), uintptr(size)) ch := chanMake(elem.Size(), uintptr(size), elem.gcLayout())
return Value{ return Value{
typecode: typ.(*RawType), typecode: typ.(*RawType),
value: ch, value: ch,
+2 -1
View File
@@ -3,6 +3,7 @@
package task package task
import ( import (
"internal/gclayout"
"unsafe" "unsafe"
) )
@@ -73,7 +74,7 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
s.args = args s.args = args
// Create a stack. // Create a stack.
stack := runtime_alloc(stackSize, nil) stack := runtime_alloc(stackSize, gclayout.Conservative.AsPtr())
// Set up the stack canary, a random number that should be checked when // Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer // switching from the task back to the scheduler. The stack canary pointer
+2 -1
View File
@@ -3,6 +3,7 @@
package task package task
import ( import (
"internal/gclayout"
"unsafe" "unsafe"
) )
@@ -36,7 +37,7 @@ func taskExit() {
// initialize the state and prepare to call the specified function with the specified argument bundle. // initialize the state and prepare to call the specified function with the specified argument bundle.
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) { func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// Create a stack. // Create a stack.
stack := runtime_alloc(stackSize, nil) stack := runtime_alloc(stackSize, gclayout.Conservative.AsPtr())
// Set up the stack canary, a random number that should be checked when // Set up the stack canary, a random number that should be checked when
// switching from the task back to the scheduler. The stack canary pointer // switching from the task back to the scheduler. The stack canary pointer
+30
View File
@@ -994,6 +994,20 @@ func TestTypeAssertPanic(t *testing.T) {
}) })
} }
type tinyMakeChanElement struct {
ptr *int
text string
}
var tinyMakeChanChurn []*int
//go:noinline
func fillTinyMakeChan(ch chan tinyMakeChanElement) {
value := new(int)
*value = 42
ch <- tinyMakeChanElement{ptr: value, text: "hello"}
}
func TestTinyMakeChan(t *testing.T) { func TestTinyMakeChan(t *testing.T) {
// Value.Send and Value.Recv are not implemented yet, so the channel is // Value.Send and Value.Recv are not implemented yet, so the channel is
// exercised through Interface(): that proves MakeChan returns a working // exercised through Interface(): that proves MakeChan returns a working
@@ -1027,6 +1041,22 @@ func TestTinyMakeChan(t *testing.T) {
} }
}) })
t.Run("buffered pointers survive GC", func(t *testing.T) {
v := MakeChan(TypeOf(make(chan tinyMakeChanElement)), 1)
ch := v.Interface().(chan tinyMakeChanElement)
fillTinyMakeChan(ch)
runtime.GC()
tinyMakeChanChurn = make([]*int, 128)
for i := range tinyMakeChanChurn {
tinyMakeChanChurn[i] = new(int)
}
got := <-ch
if *got.ptr != 42 || got.text != "hello" {
t.Errorf("<-ch=%v, want {42 hello}", got)
}
})
t.Run("unbuffered", func(t *testing.T) { t.Run("unbuffered", func(t *testing.T) {
v := MakeChan(TypeOf(make(chan string)), 0) v := MakeChan(TypeOf(make(chan string)), 0)
if got, want := v.Cap(), 0; got != want { if got, want := v.Cap(), 0; got != want {
+6 -3
View File
@@ -2,7 +2,10 @@
package runtime package runtime
import "unsafe" import (
"internal/gclayout"
"unsafe"
)
// The below functions override the default allocator of wasi-libc. This ensures // The below functions override the default allocator of wasi-libc. This ensures
// code linked from other languages can allocate memory without colliding with // code linked from other languages can allocate memory without colliding with
@@ -21,7 +24,7 @@ func libc_malloc(size uintptr) unsafe.Pointer {
if size == 0 { if size == 0 {
return nil return nil
} }
ptr := alloc(size, nil) ptr := alloc(size, gclayout.NoPtrs.AsPtr())
allocs[(*byte)(ptr)] = size allocs[(*byte)(ptr)] = size
return ptr return ptr
} }
@@ -54,7 +57,7 @@ func libc_realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer {
// It's hard to optimize this to expand the current buffer with our GC, but // It's hard to optimize this to expand the current buffer with our GC, but
// it is theoretically possible. For now, just always allocate fresh. // it is theoretically possible. For now, just always allocate fresh.
// TODO: we could skip this if the new allocation is smaller than the old. // TODO: we could skip this if the new allocation is smaller than the old.
ptr := alloc(size, nil) ptr := alloc(size, gclayout.NoPtrs.AsPtr())
if oldPtr != nil { if oldPtr != nil {
if oldSize, ok := allocs[(*byte)(oldPtr)]; ok { if oldSize, ok := allocs[(*byte)(oldPtr)]; ok {
+2 -1
View File
@@ -3,6 +3,7 @@
package runtime package runtime
import ( import (
"internal/gclayout"
"sync/atomic" "sync/atomic"
"unsafe" "unsafe"
) )
@@ -11,7 +12,7 @@ import (
func libc_malloc(size uintptr) unsafe.Pointer { func libc_malloc(size uintptr) unsafe.Pointer {
// Note: this zeroes the returned buffer which is not necessary. // Note: this zeroes the returned buffer which is not necessary.
// The same goes for bytealg.MakeNoZero. // The same goes for bytealg.MakeNoZero.
return alloc(size, nil) return alloc(size, gclayout.NoPtrs.AsPtr())
} }
//export calloc //export calloc
+2 -2
View File
@@ -137,11 +137,11 @@ type chanSelectState struct {
value unsafe.Pointer value unsafe.Pointer
} }
func chanMake(elementSize uintptr, bufSize uintptr) *channel { func chanMake(elementSize uintptr, bufSize uintptr, elementLayout unsafe.Pointer) *channel {
return &channel{ return &channel{
elementSize: elementSize, elementSize: elementSize,
bufCap: bufSize, bufCap: bufSize,
buf: alloc(elementSize*bufSize, nil), buf: alloc(elementSize*bufSize, elementLayout),
} }
} }
+3 -2
View File
@@ -31,6 +31,7 @@ package runtime
// Moss. // Moss.
import ( import (
"internal/gclayout"
"internal/reflectlite" "internal/reflectlite"
"internal/task" "internal/task"
"runtime/interrupt" "runtime/interrupt"
@@ -501,7 +502,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer { func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
if ptr == nil { if ptr == nil {
return alloc(size, nil) return alloc(size, gclayout.NoPtrs.AsPtr())
} }
// Find the first block of the original allocation. // Find the first block of the original allocation.
@@ -526,7 +527,7 @@ func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
} }
// Create a new allocation and copy the old data. // Create a new allocation and copy the old data.
newAlloc := alloc(size, nil) newAlloc := alloc(size, gclayout.NoPtrs.AsPtr())
memcpy(newAlloc, ptr, oldSize) memcpy(newAlloc, ptr, oldSize)
free(ptr) free(ptr)
+2 -1
View File
@@ -7,6 +7,7 @@ package runtime
// may be the only memory allocator possible. // may be the only memory allocator possible.
import ( import (
"internal/gclayout"
"internal/task" "internal/task"
"sync/atomic" "sync/atomic"
"unsafe" "unsafe"
@@ -69,7 +70,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
} }
func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer { func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
newAlloc := alloc(size, nil) newAlloc := alloc(size, gclayout.NoPtrs.AsPtr())
if ptr == nil { if ptr == nil {
return newAlloc return newAlloc
} }
+5 -4
View File
@@ -55,7 +55,10 @@
package runtime package runtime
import "unsafe" import (
"internal/gclayout"
"unsafe"
)
const sizeFieldBits = 4 + (unsafe.Sizeof(uintptr(0)) / 4) const sizeFieldBits = 4 + (unsafe.Sizeof(uintptr(0)) / 4)
@@ -76,9 +79,7 @@ func (layout gcLayout) pointerFree() bool {
// The length is rounded down to a multiple of the element size. // The length is rounded down to a multiple of the element size.
func (layout gcLayout) scan(start, len uintptr) { func (layout gcLayout) scan(start, len uintptr) {
switch { switch {
case layout == 0: case layout == gcLayout(gclayout.Conservative):
// This is an unknown layout.
// Scan conservatively.
// NOTE: This is *NOT* equivalent to a slice of pointers on AVR. // NOTE: This is *NOT* equivalent to a slice of pointers on AVR.
scanConservative(start, len) scanConservative(start, len)
+27 -12
View File
@@ -14,6 +14,7 @@ import (
// The underlying hashmap structure for Go. // The underlying hashmap structure for Go.
type hashmap struct { type hashmap struct {
buckets unsafe.Pointer // pointer to array of buckets buckets unsafe.Pointer // pointer to array of buckets
typeInfo *hashmapTypeInfo
seed uintptr seed uintptr
count uintptr count uintptr
keySize uintptr keySize uintptr
@@ -26,6 +27,17 @@ type hashmap struct {
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32 keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
} }
type hashmapTypeInfo struct {
keyLayout unsafe.Pointer
valueLayout unsafe.Pointer
bucketLayout unsafe.Pointer
}
//go:inline
func hashmapType(m *hashmap) *hashmapTypeInfo {
return m.typeInfo
}
const ( const (
hashmapMaxKeySize = 128 hashmapMaxKeySize = 128
hashmapMaxValueSize = 128 hashmapMaxValueSize = 128
@@ -113,7 +125,7 @@ func hashmapTopHash(hash uint32) uint8 {
} }
// Create a new hashmap with the given keySize and valueSize. // Create a new hashmap with the given keySize and valueSize.
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashmap { func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, typeInfo unsafe.Pointer, alg uint8) *hashmap {
bucketBits := uint8(0) bucketBits := uint8(0)
for hashmapHasSpaceToGrow(bucketBits) && hashmapOverLoadFactor(sizeHint, bucketBits) { for hashmapHasSpaceToGrow(bucketBits) && hashmapOverLoadFactor(sizeHint, bucketBits) {
bucketBits++ bucketBits++
@@ -132,13 +144,14 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm
} }
bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8 bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8
buckets := alloc(bucketBufSize*(1<<bucketBits), nil) buckets := alloc(bucketBufSize*(1<<bucketBits), (*hashmapTypeInfo)(typeInfo).bucketLayout)
keyHash := hashmapKeyHashAlg(tinygo.HashmapAlgorithm(alg)) keyHash := hashmapKeyHashAlg(tinygo.HashmapAlgorithm(alg))
keyEqual := hashmapKeyEqualAlg(tinygo.HashmapAlgorithm(alg)) keyEqual := hashmapKeyEqualAlg(tinygo.HashmapAlgorithm(alg))
return &hashmap{ return &hashmap{
buckets: buckets, buckets: buckets,
typeInfo: (*hashmapTypeInfo)(typeInfo),
seed: uintptr(fastrand()), seed: uintptr(fastrand()),
keySize: keySize, keySize: keySize,
valueSize: valueSize, valueSize: valueSize,
@@ -325,7 +338,7 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
//go:inline //go:inline
func hashmapStoreKey(m *hashmap, slotKey, key unsafe.Pointer) { func hashmapStoreKey(m *hashmap, slotKey, key unsafe.Pointer) {
if m.flags&hashmapFlagIndirectKey != 0 { if m.flags&hashmapFlagIndirectKey != 0 {
p := alloc(m.keySize, nil) p := alloc(m.keySize, hashmapType(m).keyLayout)
memcpy(p, key, m.keySize) memcpy(p, key, m.keySize)
*(*unsafe.Pointer)(slotKey) = p *(*unsafe.Pointer)(slotKey) = p
} else { } else {
@@ -343,7 +356,7 @@ func hashmapStoreValue(m *hashmap, slotValue, value unsafe.Pointer) {
p := *(*unsafe.Pointer)(slotValue) p := *(*unsafe.Pointer)(slotValue)
if p == nil { if p == nil {
// First insert: allocate backing storage. // First insert: allocate backing storage.
p = alloc(m.valueSize, nil) p = alloc(m.valueSize, hashmapType(m).valueLayout)
*(*unsafe.Pointer)(slotValue) = p *(*unsafe.Pointer)(slotValue) = p
} }
memcpy(p, value, m.valueSize) memcpy(p, value, m.valueSize)
@@ -356,7 +369,7 @@ func hashmapStoreValue(m *hashmap, slotValue, value unsafe.Pointer) {
// value into the bucket, and returns a pointer to this bucket. // value into the bucket, and returns a pointer to this bucket.
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket { func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
bucketBufSize := hashmapBucketSize(m) bucketBufSize := hashmapBucketSize(m)
bucketBuf := alloc(bucketBufSize, nil) bucketBuf := alloc(bucketBufSize, hashmapType(m).bucketLayout)
bucket := (*hashmapBucket)(bucketBuf) bucket := (*hashmapBucket)(bucketBuf)
// Insert into the first slot, which is empty as it has just been allocated. // Insert into the first slot, which is empty as it has just been allocated.
@@ -392,13 +405,13 @@ func hashmapCopy(m *hashmap, sizeBits uint8) hashmap {
n.bucketBits = sizeBits n.bucketBits = sizeBits
numBuckets := uintptr(1) << n.bucketBits numBuckets := uintptr(1) << n.bucketBits
bucketBufSize := hashmapBucketSize(m) bucketBufSize := hashmapBucketSize(m)
n.buckets = alloc(bucketBufSize*numBuckets, nil) n.buckets = alloc(bucketBufSize*numBuckets, hashmapType(m).bucketLayout)
// use a hashmap iterator to go through the old map // use a hashmap iterator to go through the old map
var it hashmapIterator var it hashmapIterator
var key = alloc(m.keySize, nil) var key = alloc(m.keySize, hashmapType(m).keyLayout)
var value = alloc(m.valueSize, nil) var value = alloc(m.valueSize, hashmapType(m).valueLayout)
for hashmapNext(m, &it, key, value) { for hashmapNext(m, &it, key, value) {
h := n.keyHash(key, uintptr(n.keySize), n.seed) h := n.keyHash(key, uintptr(n.keySize), n.seed)
@@ -624,6 +637,7 @@ func hashmapGenericDelete(m *hashmap, key unsafe.Pointer) {
// equal functions. This avoids the interface/reflection path for composite // equal functions. This avoids the interface/reflection path for composite
// key types like structs containing strings. // key types like structs containing strings.
func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr, func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr,
typeInfo unsafe.Pointer,
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32, keyHash func(key unsafe.Pointer, size, seed uintptr) uint32,
keyEqual func(x, y unsafe.Pointer, n uintptr) bool) *hashmap { keyEqual func(x, y unsafe.Pointer, n uintptr) bool) *hashmap {
bucketBits := uint8(0) bucketBits := uint8(0)
@@ -644,10 +658,11 @@ func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr,
} }
bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8 bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8
buckets := alloc(bucketBufSize*(1<<bucketBits), nil) buckets := alloc(bucketBufSize*(1<<bucketBits), (*hashmapTypeInfo)(typeInfo).bucketLayout)
return &hashmap{ return &hashmap{
buckets: buckets, buckets: buckets,
typeInfo: (*hashmapTypeInfo)(typeInfo),
seed: uintptr(fastrand()), seed: uintptr(fastrand()),
keySize: keySize, keySize: keySize,
valueSize: valueSize, valueSize: valueSize,
@@ -663,12 +678,12 @@ func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr,
// hashmapMakeReflect creates a hashmap for reflect.MakeMapWithSize using // hashmapMakeReflect creates a hashmap for reflect.MakeMapWithSize using
// closures that reconstruct interface{} values from raw key bytes, // closures that reconstruct interface{} values from raw key bytes,
// delegating to hashmapInterfaceHash for hashing and == for equality. // delegating to hashmapInterfaceHash for hashing and == for equality.
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) *hashmap { func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, typeInfo, keyType unsafe.Pointer) *hashmap {
t := (*reflectlite.RawType)(keyType) t := (*reflectlite.RawType)(keyType)
if t.Kind() == reflectlite.Interface { if t.Kind() == reflectlite.Interface {
// Interface keys are already stored as interface values in the // Interface keys are already stored as interface values in the
// bucket; use the existing interface hash/equal directly. // bucket; use the existing interface hash/equal directly.
return hashmapMakeGeneric(keySize, valueSize, sizeHint, return hashmapMakeGeneric(keySize, valueSize, sizeHint, typeInfo,
hashmapInterfacePtrHash, hashmapInterfaceEqual) hashmapInterfacePtrHash, hashmapInterfaceEqual)
} }
keyHash := func(key unsafe.Pointer, size, seed uintptr) uint32 { keyHash := func(key unsafe.Pointer, size, seed uintptr) uint32 {
@@ -677,7 +692,7 @@ func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Poi
keyEqual := func(x, y unsafe.Pointer, n uintptr) bool { keyEqual := func(x, y unsafe.Pointer, n uintptr) bool {
return rawToInterface(t, x) == rawToInterface(t, y) return rawToInterface(t, x) == rawToInterface(t, y)
} }
return hashmapMakeGeneric(keySize, valueSize, sizeHint, keyHash, keyEqual) return hashmapMakeGeneric(keySize, valueSize, sizeHint, typeInfo, keyHash, keyEqual)
} }
// rawToInterface reconstructs an interface{} from raw bytes at ptr. // rawToInterface reconstructs an interface{} from raw bytes at ptr.
+13 -13
View File
@@ -7,7 +7,7 @@ declare nonnull ptr @runtime.alloc(i32, ptr)
; Test allocating a single int (i32) that should be allocated on the stack. ; Test allocating a single int (i32) that should be allocated on the stack.
define void @testInt() { define void @testInt() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
store i32 5, ptr %alloc store i32 5, ptr %alloc
ret void ret void
} }
@@ -15,7 +15,7 @@ define void @testInt() {
; Test allocating an array of 3 i16 values that should be allocated on the ; Test allocating an array of 3 i16 values that should be allocated on the
; stack. ; stack.
define i16 @testArray() { define i16 @testArray() {
%alloc = call align 2 ptr @runtime.alloc(i32 6, ptr null) %alloc = call align 2 ptr @runtime.alloc(i32 6, ptr inttoptr (i32 3 to ptr))
%alloc.1 = getelementptr i16, ptr %alloc, i32 1 %alloc.1 = getelementptr i16, ptr %alloc, i32 1
store i16 5, ptr %alloc.1 store i16 5, ptr %alloc.1
%alloc.2 = getelementptr i16, ptr %alloc, i32 2 %alloc.2 = getelementptr i16, ptr %alloc, i32 2
@@ -25,15 +25,15 @@ define i16 @testArray() {
; Test allocating objects with an unknown alignment. ; Test allocating objects with an unknown alignment.
define void @testUnknownAlign() { define void @testUnknownAlign() {
%alloc32 = call ptr @runtime.alloc(i32 32, ptr null) %alloc32 = call ptr @runtime.alloc(i32 32, ptr inttoptr (i32 3 to ptr))
store i8 5, ptr %alloc32 store i8 5, ptr %alloc32
%alloc24 = call ptr @runtime.alloc(i32 24, ptr null) %alloc24 = call ptr @runtime.alloc(i32 24, ptr inttoptr (i32 3 to ptr))
store i16 5, ptr %alloc24 store i16 5, ptr %alloc24
%alloc12 = call ptr @runtime.alloc(i32 12, ptr null) %alloc12 = call ptr @runtime.alloc(i32 12, ptr inttoptr (i32 3 to ptr))
store i16 5, ptr %alloc12 store i16 5, ptr %alloc12
%alloc6 = call ptr @runtime.alloc(i32 6, ptr null) %alloc6 = call ptr @runtime.alloc(i32 6, ptr inttoptr (i32 3 to ptr))
store i16 5, ptr %alloc6 store i16 5, ptr %alloc6
%alloc3 = call ptr @runtime.alloc(i32 3, ptr null) %alloc3 = call ptr @runtime.alloc(i32 3, ptr inttoptr (i32 3 to ptr))
store i16 5, ptr %alloc3 store i16 5, ptr %alloc3
ret void ret void
} }
@@ -41,27 +41,27 @@ define void @testUnknownAlign() {
; Call a function that will let the pointer escape, so the heap-to-stack ; Call a function that will let the pointer escape, so the heap-to-stack
; transform shouldn't be applied. ; transform shouldn't be applied.
define void @testEscapingCall() { define void @testEscapingCall() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%val = call ptr @escapeIntPtr(ptr %alloc) %val = call ptr @escapeIntPtr(ptr %alloc)
ret void ret void
} }
define void @testEscapingCall2() { define void @testEscapingCall2() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%val = call ptr @escapeIntPtrSometimes(ptr %alloc, ptr %alloc) %val = call ptr @escapeIntPtrSometimes(ptr %alloc, ptr %alloc)
ret void ret void
} }
; Call a function that doesn't let the pointer escape. ; Call a function that doesn't let the pointer escape.
define void @testNonEscapingCall() { define void @testNonEscapingCall() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%val = call ptr @noescapeIntPtr(ptr %alloc) %val = call ptr @noescapeIntPtr(ptr %alloc)
ret void ret void
} }
; Return the allocated value, which lets it escape. ; Return the allocated value, which lets it escape.
define ptr @testEscapingReturn() { define ptr @testEscapingReturn() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
ret ptr %alloc ret ptr %alloc
} }
@@ -70,7 +70,7 @@ define void @testNonEscapingLoop() {
entry: entry:
br label %loop br label %loop
loop: loop:
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%ptr = call ptr @noescapeIntPtr(ptr %alloc) %ptr = call ptr @noescapeIntPtr(ptr %alloc)
%result = icmp eq ptr null, %ptr %result = icmp eq ptr null, %ptr
br i1 %result, label %loop, label %end br i1 %result, label %loop, label %end
@@ -80,7 +80,7 @@ end:
; Test a zero-sized allocation. ; Test a zero-sized allocation.
define void @testZeroSizedAlloc() { define void @testZeroSizedAlloc() {
%alloc = call align 1 ptr @runtime.alloc(i32 0, ptr null) %alloc = call align 1 ptr @runtime.alloc(i32 0, ptr inttoptr (i32 3 to ptr))
%ptr = call ptr @noescapeIntPtr(ptr %alloc) %ptr = call ptr @noescapeIntPtr(ptr %alloc)
ret void ret void
} }
+3 -3
View File
@@ -42,13 +42,13 @@ define void @testUnknownAlign() {
} }
define void @testEscapingCall() { define void @testEscapingCall() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%val = call ptr @escapeIntPtr(ptr %alloc) %val = call ptr @escapeIntPtr(ptr %alloc)
ret void ret void
} }
define void @testEscapingCall2() { define void @testEscapingCall2() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%val = call ptr @escapeIntPtrSometimes(ptr %alloc, ptr %alloc) %val = call ptr @escapeIntPtrSometimes(ptr %alloc, ptr %alloc)
ret void ret void
} }
@@ -61,7 +61,7 @@ define void @testNonEscapingCall() {
} }
define ptr @testEscapingReturn() { define ptr @testEscapingReturn() {
%alloc = call align 4 ptr @runtime.alloc(i32 4, ptr null) %alloc = call align 4 ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
ret ptr %alloc ret ptr %alloc
} }
+8 -8
View File
@@ -18,7 +18,7 @@ define ptr @getPointer() {
define ptr @needsStackSlots() { define ptr @needsStackSlots() {
; Tracked pointer. Although, in this case the value is immediately returned ; Tracked pointer. Although, in this case the value is immediately returned
; so tracking it is not really necessary. ; so tracking it is not really necessary.
%ptr = call ptr @runtime.alloc(i32 4, ptr null) %ptr = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %ptr) call void @runtime.trackPointer(ptr %ptr)
call void @someArbitraryFunction() call void @someArbitraryFunction()
%val = load i8, ptr @someGlobal %val = load i8, ptr @someGlobal
@@ -39,7 +39,7 @@ define ptr @needsStackSlots2() {
call void @runtime.trackPointer(ptr %ptr2) call void @runtime.trackPointer(ptr %ptr2)
; Here is finally the point where an allocation happens. ; Here is finally the point where an allocation happens.
%unused = call ptr @runtime.alloc(i32 4, ptr null) %unused = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %unused) call void @runtime.trackPointer(ptr %unused)
ret ptr %ptr1 ret ptr %ptr1
@@ -57,7 +57,7 @@ define ptr @fibNext(ptr %x, ptr %y) {
%x.val = load i8, ptr %x %x.val = load i8, ptr %x
%y.val = load i8, ptr %y %y.val = load i8, ptr %y
%out.val = add i8 %x.val, %y.val %out.val = add i8 %x.val, %y.val
%out.alloc = call ptr @runtime.alloc(i32 1, ptr null) %out.alloc = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %out.alloc) call void @runtime.trackPointer(ptr %out.alloc)
store i8 %out.val, ptr %out.alloc store i8 %out.val, ptr %out.alloc
ret ptr %out.alloc ret ptr %out.alloc
@@ -65,9 +65,9 @@ define ptr @fibNext(ptr %x, ptr %y) {
define ptr @allocLoop() { define ptr @allocLoop() {
entry: entry:
%entry.x = call ptr @runtime.alloc(i32 1, ptr null) %entry.x = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %entry.x) call void @runtime.trackPointer(ptr %entry.x)
%entry.y = call ptr @runtime.alloc(i32 1, ptr null) %entry.y = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %entry.y) call void @runtime.trackPointer(ptr %entry.y)
store i8 1, ptr %entry.y store i8 1, ptr %entry.y
br label %loop br label %loop
@@ -93,7 +93,7 @@ define void @testGEPBitcast() {
%arr = call ptr @arrayAlloc() %arr = call ptr @arrayAlloc()
%arr.bitcast = getelementptr [32 x i8], ptr %arr, i32 0, i32 0 %arr.bitcast = getelementptr [32 x i8], ptr %arr, i32 0, i32 0
call void @runtime.trackPointer(ptr %arr.bitcast) call void @runtime.trackPointer(ptr %arr.bitcast)
%other = call ptr @runtime.alloc(i32 1, ptr null) %other = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %other) call void @runtime.trackPointer(ptr %other)
ret void ret void
} }
@@ -103,7 +103,7 @@ define void @someArbitraryFunction() {
} }
define void @earlyPopRegression() { define void @earlyPopRegression() {
%x.alloc = call ptr @runtime.alloc(i32 4, ptr null) %x.alloc = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %x.alloc) call void @runtime.trackPointer(ptr %x.alloc)
; At this point the pass used to pop the stack chain, resulting in a potential use-after-free during allocAndSave. ; At this point the pass used to pop the stack chain, resulting in a potential use-after-free during allocAndSave.
musttail call void @allocAndSave(ptr %x.alloc) musttail call void @allocAndSave(ptr %x.alloc)
@@ -111,7 +111,7 @@ define void @earlyPopRegression() {
} }
define void @allocAndSave(ptr %x) { define void @allocAndSave(ptr %x) {
%y = call ptr @runtime.alloc(i32 4, ptr null) %y = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
call void @runtime.trackPointer(ptr %y) call void @runtime.trackPointer(ptr %y)
store ptr %y, ptr %x store ptr %y, ptr %x
store ptr %x, ptr @ptrGlobal store ptr %x, ptr @ptrGlobal
+8 -8
View File
@@ -21,7 +21,7 @@ define ptr @needsStackSlots() {
%2 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 0 %2 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 0
store ptr %1, ptr %2, align 4 store ptr %1, ptr %2, align 4
store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4 store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4
%ptr = call ptr @runtime.alloc(i32 4, ptr null) %ptr = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2 %3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2
store ptr %ptr, ptr %3, align 4 store ptr %ptr, ptr %3, align 4
call void @someArbitraryFunction() call void @someArbitraryFunction()
@@ -47,7 +47,7 @@ define ptr @needsStackSlots2() {
%ptr2 = getelementptr i8, ptr @someGlobal, i32 0 %ptr2 = getelementptr i8, ptr @someGlobal, i32 0
%6 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 5 %6 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 5
store ptr %ptr2, ptr %6, align 4 store ptr %ptr2, ptr %6, align 4
%unused = call ptr @runtime.alloc(i32 4, ptr null) %unused = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%7 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 6 %7 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 6
store ptr %unused, ptr %7, align 4 store ptr %unused, ptr %7, align 4
store ptr %1, ptr @runtime.stackChainStart, align 4 store ptr %1, ptr @runtime.stackChainStart, align 4
@@ -69,7 +69,7 @@ define ptr @fibNext(ptr %x, ptr %y) {
%x.val = load i8, ptr %x, align 1 %x.val = load i8, ptr %x, align 1
%y.val = load i8, ptr %y, align 1 %y.val = load i8, ptr %y, align 1
%out.val = add i8 %x.val, %y.val %out.val = add i8 %x.val, %y.val
%out.alloc = call ptr @runtime.alloc(i32 1, ptr null) %out.alloc = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
%3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2 %3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2
store ptr %out.alloc, ptr %3, align 4 store ptr %out.alloc, ptr %3, align 4
store i8 %out.val, ptr %out.alloc, align 1 store i8 %out.val, ptr %out.alloc, align 1
@@ -85,10 +85,10 @@ entry:
%1 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 0 %1 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 0
store ptr %0, ptr %1, align 4 store ptr %0, ptr %1, align 4
store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4 store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4
%entry.x = call ptr @runtime.alloc(i32 1, ptr null) %entry.x = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
%2 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 2 %2 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 2
store ptr %entry.x, ptr %2, align 4 store ptr %entry.x, ptr %2, align 4
%entry.y = call ptr @runtime.alloc(i32 1, ptr null) %entry.y = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
%3 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 3 %3 = getelementptr { ptr, i32, ptr, ptr, ptr, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 3
store ptr %entry.y, ptr %3, align 4 store ptr %entry.y, ptr %3, align 4
store i8 1, ptr %entry.y, align 1 store i8 1, ptr %entry.y, align 1
@@ -126,7 +126,7 @@ define void @testGEPBitcast() {
%arr.bitcast = getelementptr [32 x i8], ptr %arr, i32 0, i32 0 %arr.bitcast = getelementptr [32 x i8], ptr %arr, i32 0, i32 0
%3 = getelementptr { ptr, i32, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 2 %3 = getelementptr { ptr, i32, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 2
store ptr %arr.bitcast, ptr %3, align 4 store ptr %arr.bitcast, ptr %3, align 4
%other = call ptr @runtime.alloc(i32 1, ptr null) %other = call ptr @runtime.alloc(i32 1, ptr inttoptr (i32 3 to ptr))
%4 = getelementptr { ptr, i32, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 3 %4 = getelementptr { ptr, i32, ptr, ptr }, ptr %gc.stackobject, i32 0, i32 3
store ptr %other, ptr %4, align 4 store ptr %other, ptr %4, align 4
store ptr %1, ptr @runtime.stackChainStart, align 4 store ptr %1, ptr @runtime.stackChainStart, align 4
@@ -144,7 +144,7 @@ define void @earlyPopRegression() {
%2 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 0 %2 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 0
store ptr %1, ptr %2, align 4 store ptr %1, ptr %2, align 4
store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4 store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4
%x.alloc = call ptr @runtime.alloc(i32 4, ptr null) %x.alloc = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2 %3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2
store ptr %x.alloc, ptr %3, align 4 store ptr %x.alloc, ptr %3, align 4
call void @allocAndSave(ptr %x.alloc) call void @allocAndSave(ptr %x.alloc)
@@ -159,7 +159,7 @@ define void @allocAndSave(ptr %x) {
%2 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 0 %2 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 0
store ptr %1, ptr %2, align 4 store ptr %1, ptr %2, align 4
store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4 store ptr %gc.stackobject, ptr @runtime.stackChainStart, align 4
%y = call ptr @runtime.alloc(i32 4, ptr null) %y = call ptr @runtime.alloc(i32 4, ptr inttoptr (i32 3 to ptr))
%3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2 %3 = getelementptr { ptr, i32, ptr }, ptr %gc.stackobject, i32 0, i32 2
store ptr %y, ptr %3, align 4 store ptr %y, ptr %3, align 4
store ptr %y, ptr %x, align 4 store ptr %y, ptr %x, align 4