Skip to content

Commit 631478c

Browse files
committed
Rework static memory segment creation; Fix stdlib gc hooks not marking own fields; Align everything to 8 bytes that might be touched by GC
1 parent 61de7cf commit 631478c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+11434
-4776
lines changed

dist/asc.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/asc.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assemblyscript.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/assemblyscript.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/compiler.ts

Lines changed: 106 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
compileCall as compileBuiltinCall,
88
compileAllocate,
99
compileAbort,
10-
compileIterateRoots
10+
compileIterateRoots,
11+
ensureGCHook
1112
} from "./builtins";
1213

1314
import {
@@ -6226,53 +6227,51 @@ export class Compiler extends DiagnosticEmitter {
62266227
/** Ensures that the specified string exists in static memory and returns a pointer to it. */
62276228
ensureStaticString(stringValue: string): ExpressionRef {
62286229
var program = this.program;
6229-
var module = this.module;
6230-
var options = this.options;
6231-
var stringSegments = this.stringSegments;
6232-
var needsGCHeader = program.hasGC;
6230+
var hasGC = program.hasGC;
6231+
var gcHeaderSize = program.gcHeaderSize;
62336232

6233+
var stringInstance = assert(program.stringInstance);
62346234
var stringSegment: MemorySegment;
6235-
var stringOffset: I64;
6236-
if (!stringSegments.has(stringValue)) {
6237-
let stringLength = stringValue.length;
6238-
let stringSize = 4 + stringLength * 2;
6239-
let offset = 0;
6240-
let gcHeaderSize = program.gcHeaderSize;
6241-
if (needsGCHeader) {
6242-
stringSize += gcHeaderSize;
6243-
offset += gcHeaderSize;
6244-
}
6245-
let stringBuffer = new Uint8Array(stringSize);
6246-
stringBuffer[offset ] = stringLength & 0xff;
6247-
stringBuffer[offset + 1] = (stringLength >>> 8) & 0xff;
6248-
stringBuffer[offset + 2] = (stringLength >>> 16) & 0xff;
6249-
stringBuffer[offset + 3] = (stringLength >>> 24) & 0xff;
6250-
for (let i = 0; i < stringLength; ++i) {
6251-
stringBuffer[offset + 4 + i * 2] = stringValue.charCodeAt(i) & 0xff;
6252-
stringBuffer[offset + 5 + i * 2] = (stringValue.charCodeAt(i) >>> 8) & 0xff;
6253-
}
6254-
stringSegment = this.addMemorySegment(stringBuffer, options.usizeType.byteSize);
6255-
stringSegments.set(stringValue, stringSegment);
6256-
if (needsGCHeader) {
6257-
stringOffset = i64_add(stringSegment.offset, i64_new(gcHeaderSize, 0));
6235+
6236+
// if the string already exists, reuse it
6237+
var segments = this.stringSegments;
6238+
if (segments.has(stringValue)) {
6239+
stringSegment = <MemorySegment>segments.get(stringValue);
6240+
6241+
// otherwise create it
6242+
} else {
6243+
let length = stringValue.length;
6244+
let headerSize = (stringInstance.currentMemoryOffset + 1) & ~1;
6245+
let totalSize = headerSize + length * 2;
6246+
6247+
let buf: Uint8Array;
6248+
let pos: u32;
6249+
6250+
if (hasGC) {
6251+
buf = new Uint8Array(gcHeaderSize + totalSize);
6252+
pos = gcHeaderSize;
6253+
writeI32(ensureGCHook(this, stringInstance), buf, program.gcHookOffset);
62586254
} else {
6259-
stringOffset = stringSegment.offset;
6255+
buf = new Uint8Array(totalSize);
6256+
pos = 0;
62606257
}
6261-
} else {
6262-
stringSegment = <MemorySegment>stringSegments.get(stringValue);
6263-
stringOffset = stringSegment.offset;
6264-
}
6265-
if (program.typesLookup.has("string")) {
6266-
let stringType = <Type>program.typesLookup.get("string");
6267-
this.currentType = stringType;
6268-
} else {
6269-
this.currentType = options.usizeType;
6258+
writeI32(length, buf, pos + stringInstance.offsetof("length"));
6259+
pos += headerSize;
6260+
for (let i = 0; i < length; ++i) {
6261+
writeI16(stringValue.charCodeAt(i), buf, pos + (i << 1));
6262+
}
6263+
stringSegment = this.addMemorySegment(buf);
6264+
segments.set(stringValue, stringSegment);
62706265
}
6271-
if (options.isWasm64) {
6272-
return module.createI64(i64_low(stringOffset), i64_high(stringOffset));
6266+
var stringOffset = stringSegment.offset;
6267+
if (hasGC) stringOffset = i64_add(stringOffset, i64_new(gcHeaderSize));
6268+
6269+
this.currentType = stringInstance.type;
6270+
if (this.options.isWasm64) {
6271+
return this.module.createI64(i64_low(stringOffset), i64_high(stringOffset));
62736272
} else {
6274-
assert(i64_is_i32(stringOffset));
6275-
return module.createI32(i64_low(stringOffset));
6273+
assert(i64_is_u32(stringOffset));
6274+
return this.module.createI32(i64_low(stringOffset));
62766275
}
62776276
}
62786277

@@ -6282,52 +6281,32 @@ export class Compiler extends DiagnosticEmitter {
62826281

62836282
/** Ensures that the specified array exists in static memory and returns a pointer to it. */
62846283
ensureStaticArray(elementType: Type, values: ExpressionRef[]): ExpressionRef {
6284+
var program = this.program;
6285+
var hasGC = program.hasGC;
6286+
var gcHeaderSize = program.gcHeaderSize;
6287+
62856288
var length = values.length;
62866289
var byteSize = elementType.byteSize;
62876290
var byteLength = length * byteSize;
62886291
var usizeTypeSize = this.options.usizeType.byteSize;
62896292

6290-
// determine the size of the Array header
6291-
var arrayHeaderSize = (usizeTypeSize + 4 + 7) & ~7; // .buffer_ + .length_ + alignment
6292-
var arrayTotalSize = arrayHeaderSize;
6293-
6294-
// determine the size of the ArrayBuffer
6295-
var bufferHeaderSize = (4 + 7) & ~7; // .byteLength + alignment
6296-
var bufferTotalSize = 1 << (32 - clz(byteLength + bufferHeaderSize - 1)); // see internals
6297-
6298-
var program = this.program;
6299-
var needsGC = program.hasGC;
6300-
var gcHeaderSize = program.gcHeaderSize;
6301-
6302-
var offset = 0;
6303-
if (needsGC) {
6304-
offset += gcHeaderSize; // start writing after GC header
6305-
arrayTotalSize += gcHeaderSize;
6306-
bufferTotalSize += gcHeaderSize;
6307-
}
6308-
6309-
// create a compound segment holding both the the Array header and the ArrayBuffer
6310-
var buffer = new Uint8Array(arrayHeaderSize + bufferTotalSize);
6311-
var segment = this.addMemorySegment(buffer);
6312-
6313-
// write the Array header first
6314-
if (usizeTypeSize == 8) {
6315-
writeI64(i64_add(segment.offset, i64_new(arrayHeaderSize)), buffer, offset); // .buffer_
6316-
offset += 8;
6293+
var buf: Uint8Array;
6294+
var pos: u32;
6295+
6296+
// create the backing ArrayBuffer segment
6297+
var bufferInstance = assert(program.arrayBufferInstance);
6298+
var bufferHeaderSize = (bufferInstance.currentMemoryOffset + 7) & ~7;
6299+
var bufferTotalSize = 1 << (32 - clz(bufferHeaderSize + byteLength - 1));
6300+
if (hasGC) {
6301+
buf = new Uint8Array(gcHeaderSize + bufferTotalSize);
6302+
pos = gcHeaderSize;
6303+
writeI32(ensureGCHook(this, bufferInstance), buf, program.gcHookOffset);
63176304
} else {
6318-
assert(i64_is_u32(segment.offset));
6319-
writeI32(i64_low(segment.offset) + arrayHeaderSize, buffer, offset); // .buffer_
6320-
offset += 4;
6305+
buf = new Uint8Array(bufferTotalSize);
6306+
pos = 0;
63216307
}
6322-
writeI32(length, buffer, offset); // .length_
6323-
offset += 4;
6324-
assert(((offset + 7) & ~7) == arrayTotalSize); // incl. GC header if applicable
6325-
6326-
// append the ArrayBuffer
6327-
offset = arrayTotalSize;
6328-
if (needsGC) offset += gcHeaderSize;
6329-
writeI32(byteLength, buffer, offset); // .byteLength
6330-
offset += bufferHeaderSize; // align
6308+
writeI32(byteLength, buf, pos + bufferInstance.offsetof("byteLength"));
6309+
pos += bufferHeaderSize;
63316310
var nativeType = elementType.toNativeType();
63326311
switch (nativeType) {
63336312
case NativeType.I32: {
@@ -6337,8 +6316,8 @@ export class Compiler extends DiagnosticEmitter {
63376316
let value = values[i];
63386317
assert(getExpressionType(value) == nativeType);
63396318
assert(getExpressionId(value) == ExpressionId.Const);
6340-
writeI8(getConstValueI32(value), buffer, offset);
6341-
offset += 1;
6319+
writeI8(getConstValueI32(value), buf, pos);
6320+
pos += 1;
63426321
}
63436322
break;
63446323
}
@@ -6347,8 +6326,8 @@ export class Compiler extends DiagnosticEmitter {
63476326
let value = values[i];
63486327
assert(getExpressionType(value) == nativeType);
63496328
assert(getExpressionId(value) == ExpressionId.Const);
6350-
writeI16(getConstValueI32(value), buffer, offset);
6351-
offset += 2;
6329+
writeI16(getConstValueI32(value), buf, pos);
6330+
pos += 2;
63526331
}
63536332
break;
63546333
}
@@ -6357,8 +6336,8 @@ export class Compiler extends DiagnosticEmitter {
63576336
let value = values[i];
63586337
assert(getExpressionType(value) == nativeType);
63596338
assert(getExpressionId(value) == ExpressionId.Const);
6360-
writeI32(getConstValueI32(value), buffer, offset);
6361-
offset += 4;
6339+
writeI32(getConstValueI32(value), buf, pos);
6340+
pos += 4;
63626341
}
63636342
break;
63646343
}
@@ -6371,8 +6350,8 @@ export class Compiler extends DiagnosticEmitter {
63716350
let value = values[i];
63726351
assert(getExpressionType(value) == nativeType);
63736352
assert(getExpressionId(value) == ExpressionId.Const);
6374-
writeI64(i64_new(getConstValueI64Low(value), getConstValueI64High(value)), buffer, offset);
6375-
offset += 8;
6353+
writeI64(i64_new(getConstValueI64Low(value), getConstValueI64High(value)), buf, pos);
6354+
pos += 8;
63766355
}
63776356
break;
63786357
}
@@ -6381,8 +6360,8 @@ export class Compiler extends DiagnosticEmitter {
63816360
let value = values[i];
63826361
assert(getExpressionType(value) == nativeType);
63836362
assert(getExpressionId(value) == ExpressionId.Const);
6384-
writeF32(getConstValueF32(value), buffer, offset);
6385-
offset += 4;
6363+
writeF32(getConstValueF32(value), buf, pos);
6364+
pos += 4;
63866365
}
63876366
break;
63886367
}
@@ -6391,35 +6370,43 @@ export class Compiler extends DiagnosticEmitter {
63916370
let value = values[i];
63926371
assert(getExpressionType(value) == nativeType);
63936372
assert(getExpressionId(value) == ExpressionId.Const);
6394-
writeF64(getConstValueF64(value), buffer, offset);
6395-
offset += 8;
6373+
writeF64(getConstValueF64(value), buf, pos);
6374+
pos += 8;
63966375
}
63976376
break;
63986377
}
63996378
default: assert(false);
64006379
}
6401-
assert(offset <= arrayTotalSize + bufferTotalSize); // might have empty trailing space
6402-
6403-
var arrayPrototype = this.program.arrayPrototype;
6404-
if (arrayPrototype) {
6405-
let arrayInstance = this.resolver.resolveClass(arrayPrototype, [ elementType ], null, ReportMode.REPORT);
6406-
if (!arrayInstance) {
6407-
this.currentType = this.options.usizeType;
6408-
return this.module.createUnreachable();
6409-
}
6410-
this.currentType = arrayInstance.type;
6380+
var bufferSegment = this.addMemorySegment(buf);
6381+
var bufferOffset = bufferSegment.offset;
6382+
if (hasGC) bufferOffset = i64_add(bufferOffset, i64_new(gcHeaderSize));
6383+
6384+
// create the Array segment and return a pointer to it
6385+
var arrayPrototype = assert(program.arrayPrototype);
6386+
var arrayInstance = assert(this.resolver.resolveClass(arrayPrototype, [ elementType ]));
6387+
var arrayHeaderSize = (arrayInstance.currentMemoryOffset + 7) & ~7;
6388+
if (hasGC) {
6389+
buf = new Uint8Array(gcHeaderSize + arrayHeaderSize);
6390+
pos = gcHeaderSize;
6391+
writeI32(ensureGCHook(this, arrayInstance), buf, program.gcHookOffset);
64116392
} else {
6412-
this.currentType = this.options.usizeType;
6393+
buf = new Uint8Array(arrayHeaderSize);
6394+
pos = 0;
64136395
}
6414-
6415-
// return a pointer at the array header (skip GC header if present)
6416-
var address = segment.offset;
6417-
if (needsGC) address = i64_add(address, i64_new(gcHeaderSize, 0));
6396+
var arraySegment = this.addMemorySegment(buf);
6397+
var arrayOffset = arraySegment.offset;
6398+
if (hasGC) arrayOffset = i64_add(arrayOffset, i64_new(gcHeaderSize));
6399+
this.currentType = arrayInstance.type;
64186400
if (usizeTypeSize == 8) {
6419-
return this.module.createI64(i64_low(address), i64_high(address));
6401+
writeI64(bufferOffset, buf, pos + arrayInstance.offsetof("buffer_"));
6402+
writeI32(length, buf, pos + arrayInstance.offsetof("length_"));
6403+
return this.module.createI64(i64_low(arrayOffset), i64_high(arrayOffset));
64206404
} else {
6421-
assert(i64_is_u32(address));
6422-
return this.module.createI32(i64_low(address));
6405+
assert(i64_is_u32(bufferOffset));
6406+
writeI32(i64_low(bufferOffset), buf, pos + arrayInstance.offsetof("buffer_"));
6407+
writeI32(length, buf, pos + arrayInstance.offsetof("length_"));
6408+
assert(i64_is_u32(arrayOffset));
6409+
return this.module.createI32(i64_low(arrayOffset));
64236410
}
64246411
}
64256412

@@ -6433,17 +6420,20 @@ export class Compiler extends DiagnosticEmitter {
64336420

64346421
// find out whether all elements are constant (array is static)
64356422
var length = expressions.length;
6436-
var values = new Array<ExpressionRef>(length);
6423+
var compiledValues = new Array<ExpressionRef>(length);
6424+
var constantValues = new Array<ExpressionRef>(length);
64376425
var nativeElementType = elementType.toNativeType();
64386426
var isStatic = true;
64396427
for (let i = 0; i < length; ++i) {
6440-
values[i] = expressions[i]
6428+
let expr = expressions[i]
64416429
? this.compileExpression(<Expression>expressions[i], elementType, ConversionKind.IMPLICIT, WrapMode.NONE)
64426430
: elementType.toNativeZero(module);
6431+
compiledValues[i] = expr;
64436432
if (isStatic) {
6444-
let expr = module.precomputeExpression(values[i]);
6433+
expr = module.precomputeExpression(compiledValues[i]);
64456434
if (getExpressionId(expr) == ExpressionId.Const) {
64466435
assert(getExpressionType(expr) == nativeElementType);
6436+
constantValues[i] = expr;
64476437
} else {
64486438
if (isConst) {
64496439
this.warning(
@@ -6457,7 +6447,7 @@ export class Compiler extends DiagnosticEmitter {
64576447
}
64586448

64596449
// make a static array if possible
6460-
if (isStatic) return this.ensureStaticArray(elementType, values);
6450+
if (isStatic) return this.ensureStaticArray(elementType, constantValues);
64616451

64626452
// otherwise obtain the array type
64636453
var arrayPrototype = assert(this.program.arrayPrototype);
@@ -6491,7 +6481,7 @@ export class Compiler extends DiagnosticEmitter {
64916481
stmts[index++] = this.makeCallDirect(setter, [
64926482
module.createGetLocal(tempLocal.index, nativeArrayType), // this
64936483
module.createI32(i),
6494-
values[i]
6484+
compiledValues[i]
64956485
]);
64966486
}
64976487
assert(index + 1 == stmts.length);

src/program.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@ export class Program extends DiagnosticEmitter {
328328
/** Module-level exports by exported name. */
329329
moduleLevelExports: Map<string,ModuleExport> = new Map();
330330

331+
/** ArrayBuffer instance reference. */
332+
arrayBufferInstance: Class | null = null;
331333
/** Array prototype reference. */
332334
arrayPrototype: ClassPrototype | null = null;
333335
/** String instance reference. */
@@ -351,6 +353,8 @@ export class Program extends DiagnosticEmitter {
351353
gcMarkInstance: Function | null = null;
352354
/** Size of a managed object header. */
353355
gcHeaderSize: u32 = 0;
356+
/** Offset of the GC hook. */
357+
gcHookOffset: u32 = 0;
354358

355359
/** Currently processing filespace. */
356360
currentFilespace: Filespace;
@@ -603,6 +607,13 @@ export class Program extends DiagnosticEmitter {
603607
}
604608
}
605609

610+
// register 'ArrayBuffer'
611+
if (this.elementsLookup.has("ArrayBuffer")) {
612+
let element = assert(this.elementsLookup.get("ArrayBuffer"));
613+
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
614+
this.arrayBufferInstance = resolver.resolveClass(<ClassPrototype>element, null);
615+
}
616+
606617
// register 'Array'
607618
if (this.elementsLookup.has("Array")) {
608619
let element = assert(this.elementsLookup.get("Array"));
@@ -708,7 +719,9 @@ export class Program extends DiagnosticEmitter {
708719
this.gcAllocateInstance = gcAllocateInstance;
709720
this.gcLinkInstance = gcLinkInstance;
710721
this.gcMarkInstance = gcMarkInstance;
711-
this.gcHeaderSize = (2 * options.usizeType.byteSize + 4 + 7) & ~7; // TODO: hardcoded atm
722+
let gcHookOffset = 2 * options.usizeType.byteSize; // .next + .prev
723+
this.gcHookOffset = gcHookOffset;
724+
this.gcHeaderSize = (gcHookOffset + 4 + 7) & ~7; // + .hook index + alignment
712725
this.hasGC = true;
713726
}
714727
}
@@ -2911,6 +2924,14 @@ export class Class extends Element {
29112924
return null;
29122925
}
29132926

2927+
offsetof(fieldName: string): u32 {
2928+
var members = assert(this.members);
2929+
assert(members.has(fieldName));
2930+
var field = <Element>members.get(fieldName);
2931+
assert(field.kind == ElementKind.FIELD);
2932+
return (<Field>field).memoryOffset;
2933+
}
2934+
29142935
toString(): string {
29152936
return this.simpleName;
29162937
}

0 commit comments

Comments
 (0)