Skip to content

Commit c54dd64

Browse files
authored
fix: Backport static memory fixes made in #1559 (#1586)
1 parent 17f908f commit c54dd64

File tree

222 files changed

+5454
-5418
lines changed

Some content is hidden

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

222 files changed

+5454
-5418
lines changed

src/compiler.ts

+16-19
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ import {
194194
writeF64,
195195
uniqueMap,
196196
isPowerOf2,
197-
v128_zero
197+
v128_zero,
198+
readI32
198199
} from "./util";
199200

200201
/** Compiler options. */
@@ -1844,26 +1845,23 @@ export class Compiler extends DiagnosticEmitter {
18441845
var memoryOffset = i64_align(this.memoryOffset, alignment);
18451846
var segment = new MemorySegment(buffer, memoryOffset);
18461847
this.memorySegments.push(segment);
1847-
this.memoryOffset = i64_add(memoryOffset, i64_new(buffer.length, 0));
1848+
this.memoryOffset = i64_add(memoryOffset, i64_new(buffer.length));
18481849
return segment;
18491850
}
18501851

18511852
/** Adds a static memory segment representing a runtime object. */
18521853
addRuntimeMemorySegment(buffer: Uint8Array): MemorySegment {
1853-
// Runtime objects imply a full BLOCK and OBJECT header, see rt/common.ts
1854-
// ((memoryOffset + sizeof_usize + AL_MASK) & ~AL_MASK) - sizeof_usize
1855-
var usizeSize = this.options.usizeType.byteSize;
1856-
var memoryOffset = i64_sub(i64_align(i64_add(this.memoryOffset, i64_new(usizeSize)), 16), i64_new(usizeSize));
1854+
var memoryOffset = this.program.computeBlockStart64(this.memoryOffset);
18571855
var segment = new MemorySegment(buffer, memoryOffset);
18581856
this.memorySegments.push(segment);
1859-
this.memoryOffset = i64_add(memoryOffset, i64_new(buffer.length, 0));
1857+
this.memoryOffset = i64_add(memoryOffset, i64_new(buffer.length));
18601858
return segment;
18611859
}
18621860

18631861
/** Ensures that a string exists in static memory and returns a pointer to it. Deduplicates. */
18641862
ensureStaticString(stringValue: string): ExpressionRef {
18651863
var program = this.program;
1866-
var rtHeaderSize = program.runtimeHeaderSize;
1864+
var totalOverhead = program.totalOverhead;
18671865
var stringInstance = assert(program.stringInstance);
18681866
var stringSegment: MemorySegment;
18691867
var segments = this.stringSegments;
@@ -1873,12 +1871,12 @@ export class Compiler extends DiagnosticEmitter {
18731871
let len = stringValue.length;
18741872
let buf = stringInstance.createBuffer(len << 1);
18751873
for (let i = 0; i < len; ++i) {
1876-
writeI16(stringValue.charCodeAt(i), buf, rtHeaderSize + (i << 1));
1874+
writeI16(stringValue.charCodeAt(i), buf, totalOverhead + (i << 1));
18771875
}
18781876
stringSegment = this.addRuntimeMemorySegment(buf);
18791877
segments.set(stringValue, stringSegment);
18801878
}
1881-
var ptr = i64_add(stringSegment.offset, i64_new(rtHeaderSize));
1879+
var ptr = i64_add(stringSegment.offset, i64_new(totalOverhead));
18821880
this.currentType = stringInstance.type;
18831881
if (this.options.isWasm64) {
18841882
return this.module.i64(i64_low(ptr), i64_high(ptr));
@@ -1971,19 +1969,18 @@ export class Compiler extends DiagnosticEmitter {
19711969
var arrayBufferInstance = program.arrayBufferInstance;
19721970
var buf = arrayBufferInstance.createBuffer(values.length * elementType.byteSize);
19731971
this.program.OBJECTInstance.writeField("rtId", id, buf, 0); // use specified rtId
1974-
assert(this.writeStaticBuffer(buf, program.runtimeHeaderSize, elementType, values) == buf.length);
1972+
this.writeStaticBuffer(buf, program.totalOverhead, elementType, values);
19751973
return this.addRuntimeMemorySegment(buf);
19761974
}
19771975

19781976
/** Adds an array header to static memory and returns the created segment. */
19791977
private addStaticArrayHeader(elementType: Type, bufferSegment: MemorySegment): MemorySegment {
19801978
var program = this.program;
1981-
var runtimeHeaderSize = program.runtimeHeaderSize;
19821979
var arrayPrototype = assert(program.arrayPrototype);
19831980
var arrayInstance = assert(this.resolver.resolveClass(arrayPrototype, [ elementType ]));
1984-
var bufferLength = bufferSegment.buffer.length - runtimeHeaderSize;
1981+
var bufferLength = readI32(bufferSegment.buffer, program.OBJECTInstance.offsetof("rtSize"));
19851982
var arrayLength = i32(bufferLength / elementType.byteSize);
1986-
var bufferAddress = i64_add(bufferSegment.offset, i64_new(runtimeHeaderSize));
1983+
var bufferAddress = i64_add(bufferSegment.offset, i64_new(program.totalOverhead));
19871984

19881985
var buf = arrayInstance.createBuffer();
19891986
assert(arrayInstance.writeField("buffer", bufferAddress, buf));
@@ -2016,7 +2013,7 @@ export class Compiler extends DiagnosticEmitter {
20162013
assert(rtInstance.writeField("_env", 0, buf));
20172014
instance.memorySegment = memorySegment = this.addRuntimeMemorySegment(buf);
20182015
}
2019-
return i64_add(memorySegment.offset, i64_new(program.runtimeHeaderSize));
2016+
return i64_add(memorySegment.offset, i64_new(program.totalOverhead));
20202017
}
20212018

20222019
// === Statements ===============================================================================
@@ -8699,15 +8696,15 @@ export class Compiler extends DiagnosticEmitter {
86998696
flow.freeTempLocal(tempThis);
87008697
flow.freeTempLocal(tempDataStart);
87018698

8702-
let runtimeHeaderSize = program.runtimeHeaderSize;
8699+
let totalOverhead = program.totalOverhead;
87038700
let bufferSegment = this.addStaticBuffer(elementType, values);
8704-
let bufferAddress = i64_add(bufferSegment.offset, i64_new(runtimeHeaderSize));
8701+
let bufferAddress = i64_add(bufferSegment.offset, i64_new(totalOverhead));
87058702

87068703
// make both the buffer and array header static if assigned to a global. this can't be done
87078704
// if inside of a function because each invocation must create a new array reference then.
87088705
if (constraints & Constraints.PREFER_STATIC) {
87098706
let arraySegment = this.addStaticArrayHeader(elementType, bufferSegment);
8710-
let arrayAddress = i64_add(arraySegment.offset, i64_new(runtimeHeaderSize));
8707+
let arrayAddress = i64_add(arraySegment.offset, i64_new(totalOverhead));
87118708
this.currentType = arrayType;
87128709
return program.options.isWasm64
87138710
? this.module.i64(i64_low(arrayAddress), i64_high(arrayAddress))
@@ -8872,7 +8869,7 @@ export class Compiler extends DiagnosticEmitter {
88728869
flow.freeTempLocal(tempThis);
88738870

88748871
let bufferSegment = this.addStaticBuffer(elementType, values, arrayInstance.id);
8875-
let bufferAddress = i64_add(bufferSegment.offset, i64_new(program.runtimeHeaderSize));
8872+
let bufferAddress = i64_add(bufferSegment.offset, i64_new(program.totalOverhead));
88768873

88778874
// return the static buffer directly if assigned to a global
88788875
if (constraints & Constraints.PREFER_STATIC) {

src/program.ts

+63-24
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ import {
148148
BuiltinNames
149149
} from "./builtins";
150150

151+
// Memory manager constants
152+
const AL_SIZE = 16;
153+
const AL_MASK = AL_SIZE - 1;
154+
151155
/** Represents a yet unresolved `import`. */
152156
class QueuedImport {
153157
constructor(
@@ -769,23 +773,58 @@ export class Program extends DiagnosticEmitter {
769773
return null;
770774
}
771775

772-
/** Gets the size of a runtime header. */
773-
get runtimeHeaderSize(): i32 {
774-
var cached = this._runtimeHeaderSize;
775-
if (!cached) {
776-
// see: rt/common.ts
777-
var blockOverhead = this.BLOCKInstance.nextMemoryOffset;
778-
var totalOverhead = this.OBJECTInstance.nextMemoryOffset;
779-
const AL_SIZE = 16;
780-
const AL_MASK = AL_SIZE - 1;
781-
var objectOverhead = (totalOverhead - blockOverhead + AL_MASK) & ~AL_MASK;
782-
var headerSize = blockOverhead + objectOverhead;
783-
assert(headerSize == 20);
784-
this._runtimeHeaderSize = cached = headerSize;
776+
/** Gets the overhead of a memory manager block. */
777+
get blockOverhead(): i32 {
778+
// BLOCK | data...
779+
// ^ 16b alignment
780+
return this.BLOCKInstance.nextMemoryOffset;
781+
}
782+
783+
/** Gets the overhead of a managed object, excl. block overhead, incl. alignment. */
784+
get objectOverhead(): i32 {
785+
// OBJECT+align | data...
786+
// └ 0 ┘ ^ 16b alignment
787+
return (this.OBJECTInstance.nextMemoryOffset - this.blockOverhead + AL_MASK) & ~AL_MASK;
788+
}
789+
790+
/** Gets the total overhead of a managed object, incl. block overhead. */
791+
get totalOverhead(): i32 {
792+
// BLOCK | OBJECT+align | data...
793+
// └ = TOTAL ┘ ^ 16b alignment
794+
return this.blockOverhead + this.objectOverhead;
795+
}
796+
797+
/** Computes the next properly aligned offset of a memory manager block, given the current bump offset. */
798+
computeBlockStart(currentOffset: i32): i32 {
799+
var blockOverhead = this.blockOverhead;
800+
return ((currentOffset + blockOverhead + AL_MASK) & ~AL_MASK) - blockOverhead;
801+
}
802+
803+
/** Computes the next properly aligned offset of a memory manager block, given the current bump offset. */
804+
computeBlockStart64(currentOffset: i64): i64 {
805+
var blockOverhead = i64_new(this.blockOverhead);
806+
return i64_sub(i64_align(i64_add(currentOffset, blockOverhead), AL_SIZE), blockOverhead);
807+
}
808+
809+
/** Computes the size of a memory manager block, excl. block overhead. */
810+
computeBlockSize(payloadSize: i32, isManaged: bool): i32 {
811+
// see: std/rt/tlsf.ts, computeSize; becomes mmInfo
812+
if (isManaged) payloadSize += this.objectOverhead;
813+
// we know that payload must be aligned, and that block sizes must be chosen
814+
// so that blocks are adjacent with the next payload aligned. hence, block
815+
// size is payloadSize rounded up to where the next block would start:
816+
var blockSize = this.computeBlockStart(payloadSize);
817+
// make sure that block size is valid according to TLSF requirements
818+
var blockOverhead = this.blockOverhead;
819+
var blockMinsize = ((3 * this.options.usizeType.byteSize + blockOverhead + AL_MASK) & ~AL_MASK) - blockOverhead;
820+
if (blockSize < blockMinsize) blockSize = blockMinsize;
821+
const blockMaxsize = 1 << 30; // 1 << (FL_BITS + SB_BITS - 1), exclusive
822+
const tagsMask = 3;
823+
if (blockSize >= blockMaxsize || (blockSize & tagsMask) != 0) {
824+
throw new Error("invalid block size");
785825
}
786-
return cached;
826+
return blockSize;
787827
}
788-
private _runtimeHeaderSize: u32 = 0;
789828

790829
/** Creates a native variable declaration. */
791830
makeNativeVariableDeclaration(
@@ -4299,22 +4338,22 @@ export class Class extends TypedElement {
42994338

43004339
/** Creates a buffer suitable to hold a runtime instance of this class. */
43014340
createBuffer(overhead: i32 = 0): Uint8Array {
4302-
var size = this.nextMemoryOffset + overhead;
4303-
var buffer = new Uint8Array(this.program.runtimeHeaderSize + size);
4304-
assert(!this.program.options.isWasm64); // TODO: WASM64, mmInfo is usize
4305-
// see: std/assembly/rt/common.ts
4306-
assert(size < (1 << 28)); // 1 bit BUFFERED + 3 bits color
4307-
var OBJECT = this.program.OBJECTInstance;
4308-
OBJECT.writeField("mmInfo", size, buffer, 0);
4341+
var program = this.program;
4342+
var payloadSize = this.nextMemoryOffset + overhead;
4343+
var blockSize = program.computeBlockSize(payloadSize, true); // excl. overhead
4344+
var totalSize = program.blockOverhead + blockSize;
4345+
var buffer = new Uint8Array(totalSize);
4346+
var OBJECT = program.OBJECTInstance;
4347+
OBJECT.writeField("mmInfo", blockSize, buffer, 0);
43094348
OBJECT.writeField("gcInfo", 1, buffer, 0); // RC = 1
43104349
OBJECT.writeField("gcInfo2", 0, buffer, 0);
43114350
OBJECT.writeField("rtId", this.id, buffer, 0);
4312-
OBJECT.writeField("rtSize", size, buffer, 0);
4351+
OBJECT.writeField("rtSize", payloadSize, buffer, 0);
43134352
return buffer;
43144353
}
43154354

43164355
/** Writes a field value to a buffer and returns the number of bytes written. */
4317-
writeField<T>(name: string, value: T, buffer: Uint8Array, baseOffset: i32 = this.program.runtimeHeaderSize): i32 {
4356+
writeField<T>(name: string, value: T, buffer: Uint8Array, baseOffset: i32 = this.program.totalOverhead): i32 {
43184357
var element = this.lookupInSelf(name);
43194358
if (element !== null && element.kind == ElementKind.FIELD) {
43204359
let fieldInstance = <Field>element;

tests/compiler/abi.optimized.wat

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
(module
22
(type $none_=>_i32 (func (result i32)))
33
(memory $0 1)
4-
(data (i32.const 1036) "\0c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0c\00\00\00a\00b\00i\00.\00t\00s")
4+
(data (i32.const 1036) "\1c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0c\00\00\00a\00b\00i\00.\00t\00s")
55
(export "memory" (memory $0))
66
(export "exported" (func $abi/exported))
77
(export "exportedExported" (func $abi/exported))

tests/compiler/abi.untouched.wat

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
55
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
66
(memory $0 1)
7-
(data (i32.const 12) "\0c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0c\00\00\00a\00b\00i\00.\00t\00s\00")
7+
(data (i32.const 12) "\1c\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0c\00\00\00a\00b\00i\00.\00t\00s\00")
88
(table $0 1 funcref)
99
(global $abi/condition (mut i32) (i32.const 0))
1010
(global $abi/y (mut i32) (i32.const 0))

tests/compiler/assert-nonnull.optimized.wat

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
55
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
66
(memory $0 1)
7-
(data (i32.const 1036) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00u\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00n\00u\00l\00l")
8-
(data (i32.const 1100) "\"\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\"\00\00\00a\00s\00s\00e\00r\00t\00-\00n\00o\00n\00n\00u\00l\00l\00.\00t\00s")
9-
(data (i32.const 1164) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e")
10-
(data (i32.const 1228) "\1a\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s")
11-
(data (i32.const 1276) "^\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00^\00\00\00E\00l\00e\00m\00e\00n\00t\00 \00t\00y\00p\00e\00 \00m\00u\00s\00t\00 \00b\00e\00 \00n\00u\00l\00l\00a\00b\00l\00e\00 \00i\00f\00 \00a\00r\00r\00a\00y\00 \00i\00s\00 \00h\00o\00l\00e\00y")
7+
(data (i32.const 1036) "<\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00u\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00n\00u\00l\00l")
8+
(data (i32.const 1100) "<\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\"\00\00\00a\00s\00s\00e\00r\00t\00-\00n\00o\00n\00n\00u\00l\00l\00.\00t\00s")
9+
(data (i32.const 1164) "<\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e")
10+
(data (i32.const 1228) ",\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s")
11+
(data (i32.const 1276) "|\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00^\00\00\00E\00l\00e\00m\00e\00n\00t\00 \00t\00y\00p\00e\00 \00m\00u\00s\00t\00 \00b\00e\00 \00n\00u\00l\00l\00a\00b\00l\00e\00 \00i\00f\00 \00a\00r\00r\00a\00y\00 \00i\00s\00 \00h\00o\00l\00e\00y")
1212
(table $0 1 funcref)
1313
(export "memory" (memory $0))
1414
(export "testVar" (func $assert-nonnull/testVar))

tests/compiler/assert-nonnull.untouched.wat

+5-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
77
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
88
(memory $0 1)
9-
(data (i32.const 12) "\1e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00u\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00n\00u\00l\00l\00")
10-
(data (i32.const 76) "\"\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\"\00\00\00a\00s\00s\00e\00r\00t\00-\00n\00o\00n\00n\00u\00l\00l\00.\00t\00s\00")
11-
(data (i32.const 140) "$\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00")
12-
(data (i32.const 204) "\1a\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s\00")
13-
(data (i32.const 252) "^\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00^\00\00\00E\00l\00e\00m\00e\00n\00t\00 \00t\00y\00p\00e\00 \00m\00u\00s\00t\00 \00b\00e\00 \00n\00u\00l\00l\00a\00b\00l\00e\00 \00i\00f\00 \00a\00r\00r\00a\00y\00 \00i\00s\00 \00h\00o\00l\00e\00y\00")
9+
(data (i32.const 12) "<\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1e\00\00\00u\00n\00e\00x\00p\00e\00c\00t\00e\00d\00 \00n\00u\00l\00l\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
10+
(data (i32.const 76) "<\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\"\00\00\00a\00s\00s\00e\00r\00t\00-\00n\00o\00n\00n\00u\00l\00l\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00")
11+
(data (i32.const 140) "<\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00$\00\00\00I\00n\00d\00e\00x\00 \00o\00u\00t\00 \00o\00f\00 \00r\00a\00n\00g\00e\00\00\00\00\00\00\00\00\00")
12+
(data (i32.const 204) ",\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s\00\00\00")
13+
(data (i32.const 252) "|\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00^\00\00\00E\00l\00e\00m\00e\00n\00t\00 \00t\00y\00p\00e\00 \00m\00u\00s\00t\00 \00b\00e\00 \00n\00u\00l\00l\00a\00b\00l\00e\00 \00i\00f\00 \00a\00r\00r\00a\00y\00 \00i\00s\00 \00h\00o\00l\00e\00y\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
1414
(table $0 1 funcref)
1515
(global $~argumentsLength (mut i32) (i32.const 0))
1616
(export "memory" (memory $0))

tests/compiler/bool.optimized.wat

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(module
22
(memory $0 1)
3-
(data (i32.const 1036) "\0e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0e\00\00\00b\00o\00o\00l\00.\00t\00s")
3+
(data (i32.const 1036) ",\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0e\00\00\00b\00o\00o\00l\00.\00t\00s")
44
(export "memory" (memory $0))
55
)

tests/compiler/bool.untouched.wat

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
44
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
55
(memory $0 1)
6-
(data (i32.const 12) "\0e\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0e\00\00\00b\00o\00o\00l\00.\00t\00s\00")
6+
(data (i32.const 12) ",\00\00\00\01\00\00\00\00\00\00\00\01\00\00\00\0e\00\00\00b\00o\00o\00l\00.\00t\00s\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")
77
(table $0 1 funcref)
88
(global $bool/i (mut i32) (i32.const 2))
99
(global $bool/I (mut i64) (i64.const 2))

0 commit comments

Comments
 (0)