mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-17 11:13:27 +00:00
compiler: simplify createObjectLayout
This simplifies the process of constructing and encoding layout bitmaps. Instead of creating big integers and merging them, we can create a pre-sized bitmap and set positions within it. This also changes the encoding logic to allow larger layouts to be encoded inline. We would previously not encode a layout inline unless the size was less than the width of the data field. This is overly conservative. A layout can be encoded inline as long as: 1. The size fits within the size field. 2. All set bits in the bitmap fit into the data field.
This commit is contained in:
committed by
deadprogram
parent
c98972a3e0
commit
d8b6b257f4
@@ -99,6 +99,9 @@ func typeHasPointers(t llvm.Type) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
|
if t.ArrayLength() == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if typeHasPointers(t.ElementType()) {
|
if typeHasPointers(t.ElementType()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+90
-90
@@ -1,10 +1,10 @@
|
|||||||
package compiler
|
package compiler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"go/types"
|
"go/types"
|
||||||
"math/big"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/compileopts"
|
"github.com/tinygo-org/tinygo/compileopts"
|
||||||
@@ -231,6 +231,12 @@ func (c *compilerContext) makeGlobalArray(buf []byte, name string, elementType l
|
|||||||
//
|
//
|
||||||
// For details on what's in this value, see src/runtime/gc_precise.go.
|
// For details on what's in this value, see src/runtime/gc_precise.go.
|
||||||
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Value {
|
||||||
|
if !typeHasPointers(t) {
|
||||||
|
// There are no pointers in this type, so we can simplify the layout.
|
||||||
|
layout := (uint64(1) << 1) | 1
|
||||||
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
|
}
|
||||||
|
|
||||||
// Use the element type for arrays. This works even for nested arrays.
|
// Use the element type for arrays. This works even for nested arrays.
|
||||||
for {
|
for {
|
||||||
kind := t.TypeKind()
|
kind := t.TypeKind()
|
||||||
@@ -248,54 +254,29 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a few checks to see whether we need to generate any object layout
|
// Create the pointer bitmap.
|
||||||
// information at all.
|
|
||||||
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
objectSizeBytes := c.targetData.TypeAllocSize(t)
|
||||||
|
pointerAlignment := uint64(c.targetData.PrefTypeAlignment(c.dataPtrType))
|
||||||
|
bitmapLen := objectSizeBytes / pointerAlignment
|
||||||
|
bitmapBytes := (bitmapLen + 7) / 8
|
||||||
|
bitmap := make([]byte, bitmapBytes, max(bitmapBytes, 8))
|
||||||
|
c.buildPointerBitmap(bitmap, pointerAlignment, pos, t, 0)
|
||||||
|
|
||||||
|
// Try to encode the layout inline.
|
||||||
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
pointerSize := c.targetData.TypeAllocSize(c.dataPtrType)
|
||||||
pointerAlignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
|
||||||
if objectSizeBytes < pointerSize {
|
|
||||||
// Too small to contain a pointer.
|
|
||||||
layout := (uint64(1) << 1) | 1
|
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
|
||||||
}
|
|
||||||
bitmap := c.getPointerBitmap(t, pos)
|
|
||||||
if bitmap.BitLen() == 0 {
|
|
||||||
// There are no pointers in this type, so we can simplify the layout.
|
|
||||||
// TODO: this can be done in many other cases, e.g. when allocating an
|
|
||||||
// array (like [4][]byte, which repeats a slice 4 times).
|
|
||||||
layout := (uint64(1) << 1) | 1
|
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
|
||||||
}
|
|
||||||
if objectSizeBytes%uint64(pointerAlignment) != 0 {
|
|
||||||
// This shouldn't happen except for packed structs, which aren't
|
|
||||||
// currently used.
|
|
||||||
c.addError(pos, "internal error: unexpected object size for object with pointer field")
|
|
||||||
return llvm.ConstNull(c.dataPtrType)
|
|
||||||
}
|
|
||||||
objectSizeWords := objectSizeBytes / uint64(pointerAlignment)
|
|
||||||
|
|
||||||
pointerBits := pointerSize * 8
|
pointerBits := pointerSize * 8
|
||||||
var sizeFieldBits uint64
|
if bitmapLen < pointerBits {
|
||||||
switch pointerBits {
|
rawMask := binary.LittleEndian.Uint64(bitmap[0:8])
|
||||||
case 16:
|
layout := rawMask*pointerBits + bitmapLen
|
||||||
sizeFieldBits = 4
|
layout <<= 1
|
||||||
case 32:
|
layout |= 1
|
||||||
sizeFieldBits = 5
|
|
||||||
case 64:
|
|
||||||
sizeFieldBits = 6
|
|
||||||
default:
|
|
||||||
panic("unknown pointer size")
|
|
||||||
}
|
|
||||||
layoutFieldBits := pointerBits - 1 - sizeFieldBits
|
|
||||||
|
|
||||||
// Try to emit the value as an inline integer. This is possible in most
|
// Check if the layout fits.
|
||||||
// cases.
|
layout &= 1<<pointerBits - 1
|
||||||
if objectSizeWords < layoutFieldBits {
|
if (layout>>1)/pointerBits == rawMask {
|
||||||
// If it can be stored directly in the pointer value, do so.
|
// No set bits were shifted off.
|
||||||
// The runtime knows that if the least significant bit of the pointer is
|
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
||||||
// set, the pointer contains the value itself.
|
}
|
||||||
layout := bitmap.Uint64()<<(sizeFieldBits+1) | (objectSizeWords << 1) | 1
|
|
||||||
return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, layout, false), c.dataPtrType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
// Unfortunately, the object layout is too big to fit in a pointer-sized
|
||||||
@@ -303,25 +284,24 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
|
|
||||||
// Try first whether the global already exists. All objects with a
|
// Try first whether the global already exists. All objects with a
|
||||||
// particular name have the same type, so this is possible.
|
// particular name have the same type, so this is possible.
|
||||||
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", objectSizeWords, (objectSizeWords+15)/16, bitmap)
|
globalName := "runtime/gc.layout:" + fmt.Sprintf("%d-%0*x", bitmapLen, (bitmapLen+15)/16, bitmap)
|
||||||
global := c.mod.NamedGlobal(globalName)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if !global.IsNil() {
|
if !global.IsNil() {
|
||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the global initializer.
|
// Create the global initializer.
|
||||||
bitmapBytes := make([]byte, int(objectSizeWords+7)/8)
|
bitmapByteValues := make([]llvm.Value, bitmapBytes)
|
||||||
bitmap.FillBytes(bitmapBytes)
|
i8 := c.ctx.Int8Type()
|
||||||
reverseBytes(bitmapBytes) // big-endian to little-endian
|
for i, b := range bitmap {
|
||||||
var bitmapByteValues []llvm.Value
|
bitmapByteValues[i] = llvm.ConstInt(i8, uint64(b), false)
|
||||||
for _, b := range bitmapBytes {
|
|
||||||
bitmapByteValues = append(bitmapByteValues, llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false))
|
|
||||||
}
|
}
|
||||||
initializer := c.ctx.ConstStruct([]llvm.Value{
|
initializer := c.ctx.ConstStruct([]llvm.Value{
|
||||||
llvm.ConstInt(c.uintptrType, objectSizeWords, false),
|
llvm.ConstInt(c.uintptrType, bitmapLen, false),
|
||||||
llvm.ConstArray(c.ctx.Int8Type(), bitmapByteValues),
|
llvm.ConstArray(i8, bitmapByteValues),
|
||||||
}, false)
|
}, false)
|
||||||
|
|
||||||
|
// Create the actual global.
|
||||||
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
global = llvm.AddGlobal(c.mod, initializer.Type(), globalName)
|
||||||
global.SetInitializer(initializer)
|
global.SetInitializer(initializer)
|
||||||
global.SetUnnamedAddr(true)
|
global.SetUnnamedAddr(true)
|
||||||
@@ -329,6 +309,7 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
if c.targetData.PrefTypeAlignment(c.uintptrType) < 2 {
|
||||||
// AVR doesn't have alignment by default.
|
// AVR doesn't have alignment by default.
|
||||||
|
// The lowest bit must be unset to distinguish this from an inline layout.
|
||||||
global.SetAlignment(2)
|
global.SetAlignment(2)
|
||||||
}
|
}
|
||||||
if c.Debug && pos != token.NoPos {
|
if c.Debug && pos != token.NoPos {
|
||||||
@@ -360,52 +341,71 @@ func (c *compilerContext) createObjectLayout(t llvm.Type, pos token.Pos) llvm.Va
|
|||||||
return global
|
return global
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
// buildPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||||
// bigint at the word offset that contains a pointer. This scan is recursive.
|
// bitmap at the word offset that contains a pointer. This scan is recursive.
|
||||||
func (c *compilerContext) getPointerBitmap(typ llvm.Type, pos token.Pos) *big.Int {
|
func (c *compilerContext) buildPointerBitmap(
|
||||||
alignment := c.targetData.PrefTypeAlignment(c.dataPtrType)
|
dst []byte,
|
||||||
switch typ.TypeKind() {
|
ptrAlign uint64,
|
||||||
|
pos token.Pos,
|
||||||
|
t llvm.Type,
|
||||||
|
offset uint64,
|
||||||
|
) {
|
||||||
|
switch t.TypeKind() {
|
||||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||||
return big.NewInt(0)
|
// These types do not contain pointers.
|
||||||
|
|
||||||
case llvm.PointerTypeKind:
|
case llvm.PointerTypeKind:
|
||||||
return big.NewInt(1)
|
// Set the corresponding position in the bitmap.
|
||||||
|
dst[offset/8] |= 1 << (offset % 8)
|
||||||
|
|
||||||
case llvm.StructTypeKind:
|
case llvm.StructTypeKind:
|
||||||
ptrs := big.NewInt(0)
|
// Recurse over struct elements.
|
||||||
for i, subtyp := range typ.StructElementTypes() {
|
for i, et := range t.StructElementTypes() {
|
||||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
eo := c.targetData.ElementOffset(t, i)
|
||||||
if subptrs.BitLen() == 0 {
|
if eo%uint64(ptrAlign) != 0 {
|
||||||
|
if typeHasPointers(et) {
|
||||||
|
// This error will let the compilation fail, but by continuing
|
||||||
|
// the error can still easily be shown.
|
||||||
|
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
offset := c.targetData.ElementOffset(typ, i)
|
c.buildPointerBitmap(
|
||||||
if offset%uint64(alignment) != 0 {
|
dst,
|
||||||
// This error will let the compilation fail, but by continuing
|
ptrAlign,
|
||||||
// the error can still easily be shown.
|
pos,
|
||||||
c.addError(pos, "internal error: allocated struct contains unaligned pointer")
|
et,
|
||||||
continue
|
offset+(eo/ptrAlign),
|
||||||
}
|
)
|
||||||
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
|
||||||
ptrs.Or(ptrs, subptrs)
|
|
||||||
}
|
}
|
||||||
return ptrs
|
|
||||||
case llvm.ArrayTypeKind:
|
case llvm.ArrayTypeKind:
|
||||||
subtyp := typ.ElementType()
|
// Recurse over array elements.
|
||||||
subptrs := c.getPointerBitmap(subtyp, pos)
|
len := t.ArrayLength()
|
||||||
ptrs := big.NewInt(0)
|
if len <= 0 {
|
||||||
if subptrs.BitLen() == 0 {
|
return
|
||||||
return ptrs
|
|
||||||
}
|
}
|
||||||
elementSize := c.targetData.TypeAllocSize(subtyp)
|
et := t.ElementType()
|
||||||
if elementSize%uint64(alignment) != 0 {
|
elementSize := c.targetData.TypeAllocSize(et)
|
||||||
// This error will let the compilation fail (but continues so that
|
if elementSize%ptrAlign != 0 {
|
||||||
// other errors can be shown).
|
if typeHasPointers(et) {
|
||||||
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
// This error will let the compilation fail (but continues so that
|
||||||
return ptrs
|
// other errors can be shown).
|
||||||
|
c.addError(pos, "internal error: allocated array contains unaligned pointer")
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
for i := 0; i < typ.ArrayLength(); i++ {
|
elementSize /= ptrAlign
|
||||||
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
for i := 0; i < len; i++ {
|
||||||
ptrs.Or(ptrs, subptrs)
|
c.buildPointerBitmap(
|
||||||
|
dst,
|
||||||
|
ptrAlign,
|
||||||
|
pos,
|
||||||
|
et,
|
||||||
|
offset+uint64(i)*elementSize,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return ptrs
|
|
||||||
default:
|
default:
|
||||||
// Should not happen.
|
// Should not happen.
|
||||||
panic("unknown LLVM type")
|
panic("unknown LLVM type")
|
||||||
|
|||||||
Vendored
+8
@@ -24,6 +24,10 @@ var (
|
|||||||
x *byte
|
x *byte
|
||||||
y [61]uintptr
|
y [61]uintptr
|
||||||
}
|
}
|
||||||
|
struct5 *struct {
|
||||||
|
x *byte
|
||||||
|
y [30]uintptr
|
||||||
|
}
|
||||||
|
|
||||||
slice1 []byte
|
slice1 []byte
|
||||||
slice2 []*int
|
slice2 []*int
|
||||||
@@ -58,6 +62,10 @@ func newStruct() {
|
|||||||
x *byte
|
x *byte
|
||||||
y [61]uintptr
|
y [61]uintptr
|
||||||
})
|
})
|
||||||
|
struct5 = new(struct {
|
||||||
|
x *byte
|
||||||
|
y [30]uintptr
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFuncValue() *func() {
|
func newFuncValue() *func() {
|
||||||
|
|||||||
Vendored
+8
-4
@@ -16,11 +16,12 @@ target triple = "wasm32-unknown-wasi"
|
|||||||
@main.struct2 = hidden global ptr null, align 4
|
@main.struct2 = hidden global ptr null, align 4
|
||||||
@main.struct3 = hidden global ptr null, align 4
|
@main.struct3 = hidden global ptr null, align 4
|
||||||
@main.struct4 = hidden global ptr null, align 4
|
@main.struct4 = hidden global ptr null, align 4
|
||||||
|
@main.struct5 = hidden global ptr null, align 4
|
||||||
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice1 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice2 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
@main.slice3 = hidden global { ptr, i32, i32 } zeroinitializer, align 4
|
||||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
@"runtime/gc.layout:62-0100000000000020" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||||
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
@"runtime/gc.layout:62-0100000000000000" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||||
|
|
||||||
@@ -80,12 +81,15 @@ entry:
|
|||||||
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
%new1 = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new1, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new1, ptr @main.struct2, align 4
|
store ptr %new1, ptr @main.struct2, align 4
|
||||||
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-2000000000000001", ptr undef) #3
|
%new2 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000020", ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new2, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new2, ptr @main.struct3, align 4
|
store ptr %new2, ptr @main.struct3, align 4
|
||||||
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0001", ptr undef) #3
|
%new3 = call align 4 dereferenceable(248) ptr @runtime.alloc(i32 248, ptr nonnull @"runtime/gc.layout:62-0100000000000000", ptr undef) #3
|
||||||
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
|
call void @runtime.trackPointer(ptr nonnull %new3, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
store ptr %new3, ptr @main.struct4, align 4
|
store ptr %new3, ptr @main.struct4, align 4
|
||||||
|
%new4 = call align 4 dereferenceable(124) ptr @runtime.alloc(i32 124, ptr nonnull inttoptr (i32 127 to ptr), ptr undef) #3
|
||||||
|
call void @runtime.trackPointer(ptr nonnull %new4, ptr nonnull %stackalloc, ptr undef) #3
|
||||||
|
store ptr %new4, ptr @main.struct5, align 4
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user