@@ -18,6 +18,7 @@ import (
18
18
"encoding/binary"
19
19
"errors"
20
20
"math"
21
+ "math/big"
21
22
"strconv"
22
23
"strings"
23
24
@@ -27,17 +28,20 @@ import (
27
28
// An object is a memory buffer that may be an already existing global or a
28
29
// global created with runtime.alloc or the alloca instruction. If llvmGlobal is
29
30
// set, that's the global for this object, otherwise it needs to be created (if
30
- // it is still reachable when the package initializer returns).
31
+ // it is still reachable when the package initializer returns). The
32
+ // llvmLayoutType is not necessarily a complete type: it may need to be
33
+ // repeated (for example, for a slice value).
31
34
//
32
35
// Objects are copied in a memory view when they are stored to, to provide the
33
36
// ability to roll back interpreting a function.
34
37
type object struct {
35
- llvmGlobal llvm.Value
36
- llvmType llvm.Type // must match llvmGlobal.Type() if both are set, may be unset if llvmGlobal is set
37
- globalName string // name, if not yet created (not guaranteed to be the final name)
38
- buffer value // buffer with value as given by interp, nil if external
39
- size uint32 // must match buffer.len(), if available
40
- marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
38
+ llvmGlobal llvm.Value
39
+ llvmType llvm.Type // must match llvmGlobal.Type() if both are set, may be unset if llvmGlobal is set
40
+ llvmLayoutType llvm.Type // LLVM type based on runtime.alloc layout parameter, if available
41
+ globalName string // name, if not yet created (not guaranteed to be the final name)
42
+ buffer value // buffer with value as given by interp, nil if external
43
+ size uint32 // must match buffer.len(), if available
44
+ marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
41
45
}
42
46
43
47
// clone() returns a cloned version of this object, for when an object needs to
@@ -529,7 +533,7 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
529
533
// runtime.alloc.
530
534
// First allocate a new global for this object.
531
535
obj := mem .get (v .index ())
532
- if obj .llvmType .IsNil () {
536
+ if obj .llvmType .IsNil () && obj . llvmLayoutType . IsNil () {
533
537
// Create an initializer without knowing the global type.
534
538
// This is probably the result of a runtime.alloc call.
535
539
initializer , err := obj .buffer .asRawValue (mem .r ).rawLLVMValue (mem )
@@ -543,7 +547,23 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
543
547
obj .llvmGlobal = llvmValue
544
548
mem .put (v .index (), obj )
545
549
} else {
546
- globalType := obj .llvmType .ElementType ()
550
+ // The global type is known, or at least its structure.
551
+ var globalType llvm.Type
552
+ if ! obj .llvmType .IsNil () {
553
+ // The exact type is known.
554
+ globalType = obj .llvmType .ElementType ()
555
+ } else { // !obj.llvmLayoutType.IsNil()
556
+ // The exact type isn't known, but the object layout is known.
557
+ globalType = obj .llvmLayoutType
558
+ // The layout may not span the full size of the global because
559
+ // of repetition. One example would be make([]string, 5) which
560
+ // would be 10 words in size but the layout would only be two
561
+ // words (for the string type).
562
+ typeSize := mem .r .targetData .TypeAllocSize (globalType )
563
+ if typeSize != uint64 (obj .size ) {
564
+ globalType = llvm .ArrayType (globalType , int (uint64 (obj .size )/ typeSize ))
565
+ }
566
+ }
547
567
if checks && mem .r .targetData .TypeAllocSize (globalType ) != uint64 (obj .size ) {
548
568
panic ("size of the globalType isn't the same as the object size" )
549
569
}
@@ -562,6 +582,11 @@ func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Val
562
582
return llvm.Value {}, errors .New ("interp: allocated value does not match allocated type" )
563
583
}
564
584
llvmValue .SetInitializer (initializer )
585
+ if obj .llvmType .IsNil () {
586
+ // The exact type isn't known (only the layout), so use the
587
+ // alignment that would normally be expected from runtime.alloc.
588
+ llvmValue .SetAlignment (mem .r .maxAlign )
589
+ }
565
590
}
566
591
567
592
// It should be included in r.globals because otherwise markExternal
@@ -1209,3 +1234,108 @@ func (r *runner) getValue(llvmValue llvm.Value) value {
1209
1234
panic ("unknown value" )
1210
1235
}
1211
1236
}
1237
+
1238
+ // readObjectLayout reads the object layout as it is stored by the compiler. It
1239
+ // returns the size in the number of words and the bitmap.
1240
+ func (r * runner ) readObjectLayout (layoutValue value ) (uint64 , * big.Int ) {
1241
+ pointerSize := layoutValue .len (r )
1242
+ if checks && uint64 (pointerSize ) != r .targetData .TypeAllocSize (r .i8ptrType ) {
1243
+ panic ("inconsistent pointer size" )
1244
+ }
1245
+
1246
+ // The object layout can be stored in a global variable, directly as an
1247
+ // integer value, or can be nil.
1248
+ ptr , err := layoutValue .asPointer (r )
1249
+ if err == errIntegerAsPointer {
1250
+ // It's an integer, which means it's a small object or unknown.
1251
+ layout := layoutValue .Uint ()
1252
+ if layout == 0 {
1253
+ // Nil pointer, which means the layout is unknown.
1254
+ return 0 , nil
1255
+ }
1256
+ if layout % 2 != 1 {
1257
+ // Sanity check: the least significant bit must be set. This is how
1258
+ // the runtime can separate pointers from integers.
1259
+ panic ("unexpected layout" )
1260
+ }
1261
+
1262
+ // Determine format of bitfields in the integer.
1263
+ pointerBits := uint64 (pointerSize * 8 )
1264
+ var sizeFieldBits uint64
1265
+ switch pointerBits {
1266
+ case 16 :
1267
+ sizeFieldBits = 4
1268
+ case 32 :
1269
+ sizeFieldBits = 5
1270
+ case 64 :
1271
+ sizeFieldBits = 6
1272
+ default :
1273
+ panic ("unknown pointer size" )
1274
+ }
1275
+
1276
+ // Extract fields.
1277
+ objectSizeWords := (layout >> 1 ) & (1 << sizeFieldBits - 1 )
1278
+ bitmap := new (big.Int ).SetUint64 (layout >> (1 + sizeFieldBits ))
1279
+ return objectSizeWords , bitmap
1280
+ }
1281
+
1282
+ // Read the object size in words and the bitmap from the global.
1283
+ buf := r .objects [ptr .index ()].buffer .(rawValue )
1284
+ objectSizeWords := rawValue {buf : buf .buf [:r .pointerSize ]}.Uint ()
1285
+ rawByteValues := buf .buf [r .pointerSize :]
1286
+ rawBytes := make ([]byte , len (rawByteValues ))
1287
+ for i , v := range rawByteValues {
1288
+ if uint64 (byte (v )) != v {
1289
+ panic ("found pointer in data array?" ) // sanity check
1290
+ }
1291
+ rawBytes [i ] = byte (v )
1292
+ }
1293
+ bitmap := new (big.Int ).SetBytes (rawBytes )
1294
+ return objectSizeWords , bitmap
1295
+ }
1296
+
1297
+ // getLLVMTypeFromLayout returns the 'layout type', which is an approximation of
1298
+ // the real type. Pointers are in the correct location but the actual object may
1299
+ // have some additional repetition, for example in the buffer of a slice.
1300
+ func (r * runner ) getLLVMTypeFromLayout (layoutValue value ) llvm.Type {
1301
+ objectSizeWords , bitmap := r .readObjectLayout (layoutValue )
1302
+ if bitmap == nil {
1303
+ // No information available.
1304
+ return llvm.Type {}
1305
+ }
1306
+
1307
+ if bitmap .BitLen () == 0 {
1308
+ // There are no pointers in this object, so treat this as a raw byte
1309
+ // buffer. This is important because objects without pointers may have
1310
+ // lower alignment.
1311
+ return r .mod .Context ().Int8Type ()
1312
+ }
1313
+
1314
+ // Create the LLVM type.
1315
+ pointerSize := layoutValue .len (r )
1316
+ pointerAlignment := r .targetData .PrefTypeAlignment (r .i8ptrType )
1317
+ var fields []llvm.Type
1318
+ for i := 0 ; i < int (objectSizeWords ); {
1319
+ if bitmap .Bit (i ) != 0 {
1320
+ // Pointer field.
1321
+ fields = append (fields , r .i8ptrType )
1322
+ i += int (pointerSize / uint32 (pointerAlignment ))
1323
+ } else {
1324
+ // Byte/word field.
1325
+ fields = append (fields , r .mod .Context ().IntType (pointerAlignment * 8 ))
1326
+ i += 1
1327
+ }
1328
+ }
1329
+ var llvmLayoutType llvm.Type
1330
+ if len (fields ) == 1 {
1331
+ llvmLayoutType = fields [0 ]
1332
+ } else {
1333
+ llvmLayoutType = r .mod .Context ().StructType (fields , false )
1334
+ }
1335
+
1336
+ objectSizeBytes := objectSizeWords * uint64 (pointerAlignment )
1337
+ if checks && r .targetData .TypeAllocSize (llvmLayoutType ) != objectSizeBytes {
1338
+ panic ("unexpected size" ) // sanity check
1339
+ }
1340
+ return llvmLayoutType
1341
+ }
0 commit comments