Skip to content

Commit 16d8fea

Browse files
committed
feat: enhance lowMemoryLimit to support larger than 64k memory limitation
1 parent be1d94a commit 16d8fea

File tree

189 files changed

+17302
-6984
lines changed

Some content is hidden

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

189 files changed

+17302
-6984
lines changed

cli/options.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@
260260
},
261261
"lowMemoryLimit": {
262262
"category": "Features",
263-
"description": "Enforces very low (<64k) memory constraints.",
263+
"description": "Enforces memory constraints.",
264264
"default": 0,
265265
"type": "i"
266266
},

src/compiler.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export class Options {
276276
noUnsafe: bool = false;
277277
/** If true, enables pedantic diagnostics. */
278278
pedantic: bool = false;
279-
/** Indicates a very low (<64k) memory limit. */
279+
/** Indicates a memory limit. */
280280
lowMemoryLimit: u32 = 0;
281281
/** If true, exports the runtime helpers. */
282282
exportRuntime: bool = false;
@@ -815,7 +815,7 @@ export class Compiler extends DiagnosticEmitter {
815815
// check that we didn't exceed lowMemoryLimit already
816816
let lowMemoryLimit32 = options.lowMemoryLimit;
817817
if (lowMemoryLimit32) {
818-
let lowMemoryLimit = i64_new(lowMemoryLimit32 & ~15);
818+
let lowMemoryLimit = i64_new(lowMemoryLimit32);
819819
if (i64_gt(memoryOffset, lowMemoryLimit)) {
820820
this.error(
821821
DiagnosticCode.Low_memory_limit_exceeded_by_static_data_0_1,

std/assembly/rt/tlsf.ts

+21-10
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ import { E_ALLOCATION_TOO_LARGE } from "../util/error";
196196
);
197197
}
198198

199+
// @ts-ignore: decorator
200+
@inline function sizeRoundToPage(size: usize): i32 {
201+
return <i32>((size + 0xffff) >>> 16);
202+
}
203+
199204
/** Inserts a previously used block back into the free list. */
200205
function insertBlock(root: Root, block: Block): void {
201206
if (DEBUG) assert(block); // cannot be null
@@ -427,10 +432,6 @@ function addMemory(root: Root, start: usize, endU64: u64): bool {
427432

428433
/** Grows memory to fit at least another block of the specified size. */
429434
function growMemory(root: Root, size: usize): void {
430-
if (ASC_LOW_MEMORY_LIMIT) {
431-
unreachable();
432-
return;
433-
}
434435
// Here, both rounding performed in searchBlock ...
435436
if (size >= SB_SIZE) {
436437
size = roundSize(size);
@@ -439,13 +440,23 @@ function growMemory(root: Root, size: usize): void {
439440
// to merge with the tail block, that's one time, otherwise it's two times.
440441
let pagesBefore = memory.size();
441442
size += BLOCK_OVERHEAD << usize((<usize>pagesBefore << 16) - BLOCK_OVERHEAD != changetype<usize>(GETTAIL(root)));
442-
let pagesNeeded = <i32>(((size + 0xffff) & ~0xffff) >>> 16);
443+
if (ASC_LOW_MEMORY_LIMIT) {
444+
if ((<usize>pagesBefore << 16) + size > <usize>ASC_LOW_MEMORY_LIMIT) unreachable();
445+
}
446+
let pagesNeeded = sizeRoundToPage(size);
443447
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
448+
if (ASC_LOW_MEMORY_LIMIT) {
449+
pagesWanted = min(pagesWanted, sizeRoundToPage(ASC_LOW_MEMORY_LIMIT) - pagesBefore);
450+
}
444451
if (memory.grow(pagesWanted) < 0) {
445452
if (memory.grow(pagesNeeded) < 0) unreachable();
446453
}
447454
let pagesAfter = memory.size();
448-
addMemory(root, <usize>pagesBefore << 16, <u64>pagesAfter << 16);
455+
if (ASC_LOW_MEMORY_LIMIT) {
456+
addMemory(root, <usize>pagesBefore << 16, min(<u64>pagesAfter << 16, <u64>ASC_LOW_MEMORY_LIMIT & ~AL_MASK));
457+
} else {
458+
addMemory(root, <usize>pagesBefore << 16, <u64>pagesAfter << 16);
459+
}
449460
}
450461

451462
/** Computes the size (excl. header) of a block. */
@@ -467,7 +478,7 @@ function initialize(): void {
467478
if (isDefined(ASC_RTRACE)) oninit(__heap_base);
468479
let rootOffset = (__heap_base + AL_MASK) & ~AL_MASK;
469480
let pagesBefore = memory.size();
470-
let pagesNeeded = <i32>((((rootOffset + ROOT_SIZE) + 0xffff) & ~0xffff) >>> 16);
481+
let pagesNeeded = sizeRoundToPage(rootOffset + ROOT_SIZE);
471482
if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable();
472483
let root = changetype<Root>(rootOffset);
473484
root.flMap = 0;
@@ -480,9 +491,9 @@ function initialize(): void {
480491
}
481492
let memStart = rootOffset + ROOT_SIZE;
482493
if (ASC_LOW_MEMORY_LIMIT) {
483-
const memEnd = <u64>ASC_LOW_MEMORY_LIMIT & ~AL_MASK;
484-
if (memStart <= memEnd) addMemory(root, memStart, memEnd);
485-
else unreachable(); // low memory limit already exceeded
494+
const limitedEnd: u64 = min(<u64>memory.size() << 16, <u64>ASC_LOW_MEMORY_LIMIT & ~AL_MASK);
495+
if (<u64>memStart > limitedEnd) unreachable(); // low memory limit already exceeded
496+
addMemory(root, memStart, limitedEnd);
486497
} else {
487498
addMemory(root, memStart, <u64>memory.size() << 16);
488499
}

tests/compiler.js

+13-16
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,13 @@ async function runTest(basename) {
380380
if (config.skipInstantiate) {
381381
instantiateDebug.end(SKIPPED);
382382
} else {
383-
384-
if (!await testInstantiate(debugBuffer, glue, stderr)) {
383+
if (!await testInstantiate(debugBuffer, glue, stderr, !!config.expectedFailed)) {
385384
instantiateDebug.end(FAILURE);
386385
return prepareResult(FAILURE, "instantiate error (debug)");
387386
}
388387
instantiateDebug.end(SUCCESS);
389388
const instantiateRelease = section("instantiate release");
390-
if (!await testInstantiate(releaseBuffer, glue, stderr)) {
389+
if (!await testInstantiate(releaseBuffer, glue, stderr, !!config.expectedFailed)) {
391390
instantiateRelease.end(FAILURE);
392391
return prepareResult(FAILURE, "instantiate error (release)");
393392
}
@@ -423,7 +422,7 @@ async function runTest(basename) {
423422

424423
const rtracedBuffer = stdout.toBuffer();
425424
const instantiateRtrace = section("instantiate rtrace");
426-
if (!await testInstantiate(rtracedBuffer, glue, stderr)) {
425+
if (!await testInstantiate(rtracedBuffer, glue, stderr, !!config.expectedFailed)) {
427426
instantiateRtrace.end(FAILURE);
428427
return prepareResult(FAILURE, "rtrace error");
429428
}
@@ -434,7 +433,7 @@ async function runTest(basename) {
434433
}
435434

436435
// Tests if instantiation of a module succeeds
437-
async function testInstantiate(binaryBuffer, glue, stderr) {
436+
async function testInstantiate(binaryBuffer, glue, stderr, expectedFailed) {
438437
let failed = false;
439438
try {
440439
const memory = new WebAssembly.Memory({ initial: 10 });
@@ -539,23 +538,21 @@ async function testInstantiate(binaryBuffer, glue, stderr) {
539538
failed = true;
540539
console.log(` memory leak detected: ${leakCount} leaking`);
541540
}
542-
if (!failed) {
543-
if (rtrace.active) {
544-
console.log(" " +
545-
rtrace.allocCount + " allocs, " +
546-
rtrace.freeCount + " frees, " +
547-
rtrace.resizeCount + " resizes, " +
548-
rtrace.moveCount + " moves"
549-
);
550-
}
551-
return true;
541+
if (rtrace.active) {
542+
console.log(" " +
543+
rtrace.allocCount + " allocs, " +
544+
rtrace.freeCount + " frees, " +
545+
rtrace.resizeCount + " resizes, " +
546+
rtrace.moveCount + " moves"
547+
);
552548
}
553549
} catch (err) {
550+
failed = true;
554551
stderr.write("---\n");
555552
stderr.write(err.stack);
556553
stderr.write("\n---\n");
557554
}
558-
return false;
555+
return failed == expectedFailed;
559556
}
560557

561558
// Evaluates the overall test result

0 commit comments

Comments
 (0)