mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-11 06:59:31 +00:00
runtime: require explicit GC layouts
This commit is contained in:
Vendored
+1
-1
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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...)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Vendored
+2
-2
@@ -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" }
|
||||||
|
|||||||
Vendored
+1
-1
@@ -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" } }
|
||||||
|
|||||||
Vendored
+5
-4
@@ -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 {
|
||||||
|
|||||||
Vendored
+16
-10
@@ -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
@@ -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
|
||||||
|
|||||||
Vendored
+5
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+2
@@ -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
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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.
|
||||||
|
|||||||
Vendored
+13
-13
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3
-3
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+8
-8
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user