Skip to content

Add a low memory limit hint #1090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/asc.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ exports.main = function main(argv, options, callback) {
assemblyscript.setSourceMap(compilerOptions, args.sourceMap != null);
assemblyscript.setNoUnsafe(compilerOptions, args.noUnsafe);
assemblyscript.setPedantic(compilerOptions, args.pedantic);
assemblyscript.setLowMemoryLimit(compilerOptions, args.lowMemoryLimit >>> 0);

// Add or override aliases if specified
if (args.use) {
Expand Down
6 changes: 6 additions & 0 deletions cli/asc.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@
"type": "S",
"alias": "u"
},
"lowMemoryLimit": {
"category": "Features",
"description": "Enforces very low (<64k) memory constraints.",
"default": 0,
"type": "i"
},

"memoryBase": {
"category": "Linking",
Expand Down
1 change: 1 addition & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export namespace CommonNames {
export const ASC_TABLE_BASE = "ASC_TABLE_BASE";
export const ASC_OPTIMIZE_LEVEL = "ASC_OPTIMIZE_LEVEL";
export const ASC_SHRINK_LEVEL = "ASC_SHRINK_LEVEL";
export const ASC_LOW_MEMORY_LIMIT = "ASC_LOW_MEMORY_LIMIT";
export const ASC_WASI = "ASC_WASI";
export const ASC_FEATURE_SIGN_EXTENSION = "ASC_FEATURE_SIGN_EXTENSION";
export const ASC_FEATURE_MUTABLE_GLOBALS = "ASC_FEATURE_MUTABLE_GLOBALS";
Expand Down
16 changes: 14 additions & 2 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ export class Options {
noUnsafe: bool = false;
/** If true, enables pedantic diagnostics. */
pedantic: bool = false;
/** Indicates a very low (<64k) memory limit. */
lowMemoryLimit: i32 = 0;

/** Hinted optimize level. Not applied by the compiler itself. */
optimizeLevelHint: i32 = 0;
Expand Down Expand Up @@ -361,7 +363,7 @@ export class Compiler extends DiagnosticEmitter {
this.memoryOffset = i64_new(options.memoryBase);
module.setLowMemoryUnused(false);
} else {
if (options.optimizeLevelHint >= 2) {
if (options.optimizeLevelHint >= 2 && !options.lowMemoryLimit) {
this.memoryOffset = i64_new(1024);
module.setLowMemoryUnused(true);
} else {
Expand Down Expand Up @@ -502,6 +504,16 @@ export class Compiler extends DiagnosticEmitter {
// update the heap base pointer
var memoryOffset = this.memoryOffset;
memoryOffset = i64_align(memoryOffset, options.usizeType.byteSize);
var lowMemoryLimit32 = this.options.lowMemoryLimit;
if (lowMemoryLimit32) {
let lowMemoryLimit = i64_new(lowMemoryLimit32 & ~15);
if (i64_gt(memoryOffset, lowMemoryLimit)) {
this.error(
DiagnosticCode.Low_memory_limit_exceeded_by_static_data_0_1,
null, i64_to_string(memoryOffset), i64_to_string(lowMemoryLimit)
);
}
}
this.memoryOffset = memoryOffset;
module.removeGlobal(BuiltinNames.heap_base);
if (this.runtimeFeatures & RuntimeFeatures.HEAP) {
Expand All @@ -526,7 +538,7 @@ export class Compiler extends DiagnosticEmitter {
var isSharedMemory = options.hasFeature(Feature.THREADS) && options.sharedMemory > 0;
module.setMemory(
this.options.memoryBase /* is specified */ || this.memorySegments.length
? i64_low(i64_shr_u(i64_align(memoryOffset, 0x10000), i64_new(16, 0)))
? i64_low(i64_shr_u(i64_align(memoryOffset, 0x10000), i64_new(16)))
: 0,
isSharedMemory ? options.sharedMemory : Module.UNLIMITED_MEMORY,
this.memorySegments,
Expand Down
2 changes: 2 additions & 0 deletions src/diagnosticMessages.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum DiagnosticCode {
User_defined_0 = 102,
Feature_0_is_not_enabled = 103,
Function_0_is_possibly_called_virtually_which_is_not_yet_supported = 104,
Low_memory_limit_exceeded_by_static_data_0_1 = 105,
Conversion_from_type_0_to_1_requires_an_explicit_cast = 200,
Conversion_from_type_0_to_1_will_require_an_explicit_cast_when_switching_between_32_64_bit = 201,
Type_0_cannot_be_changed_to_type_1 = 202,
Expand Down Expand Up @@ -165,6 +166,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 102: return "User-defined: {0}";
case 103: return "Feature '{0}' is not enabled.";
case 104: return "Function '{0}' is possibly called virtually, which is not yet supported.";
case 105: return "Low memory limit exceeded by static data: {0} > {1}";
case 200: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
case 201: return "Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.";
case 202: return "Type '{0}' cannot be changed to type '{1}'.";
Expand Down
1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"User-defined: {0}": 102,
"Feature '{0}' is not enabled.": 103,
"Function '{0}' is possibly called virtually, which is not yet supported.": 104,
"Low memory limit exceeded by static data: {0} > {1}": 105,

"Conversion from type '{0}' to '{1}' requires an explicit cast.": 200,
"Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.": 201,
Expand Down
1 change: 1 addition & 0 deletions src/glue/js/i64.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ declare function i64_not(value: i64): i64;

declare function i64_eq(left: i64, right: i64): bool;
declare function i64_ne(left: i64, right: i64): bool;
declare function i64_gt(left: i64, right: i64): bool;

declare function i64_align(value: i64, alignment: i32): i64;

Expand Down
4 changes: 4 additions & 0 deletions src/glue/js/i64.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ global.i64_ne = function(left, right) {
return left.ne(right);
};

global.i64_gt = function(left, right) {
return left.gt(right);
};

global.i64_align = function(value, alignment) {
assert(alignment && (alignment & (alignment - 1)) == 0);
var mask = Long.fromInt(alignment - 1);
Expand Down
6 changes: 6 additions & 0 deletions src/glue/wasm/i64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ function i64_ne(left: i64, right: i64): bool {
return left != right;
}

// @ts-ignore: decorator
@global
function i64_gt(left: i64, right: i64): bool {
return left > right;
}

// @ts-ignore: decorator
@global
function i64_align(value: i64, alignment: i64): i64 {
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ export function setNoUnsafe(options: Options, noUnsafe: bool): void {
options.noUnsafe = noUnsafe;
}

/** Sets the `lowMemoryLimit` option. */
export function setLowMemoryLimit(options: Options, lowMemoryLimit: i32): void {
options.lowMemoryLimit = lowMemoryLimit;
}

/** Sign extension operations. */
export const FEATURE_SIGN_EXTENSION = Feature.SIGN_EXTENSION;
/** Mutable global imports and exports. */
Expand Down
2 changes: 2 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,8 @@ export class Program extends DiagnosticEmitter {
i64_new(options.optimizeLevelHint, 0));
this.registerConstantInteger(CommonNames.ASC_SHRINK_LEVEL, Type.i32,
i64_new(options.shrinkLevelHint, 0));
this.registerConstantInteger(CommonNames.ASC_LOW_MEMORY_LIMIT, Type.i32,
i64_new(options.lowMemoryLimit, 0));

// register feature hints
this.registerConstantInteger(CommonNames.ASC_FEATURE_SIGN_EXTENSION, Type.bool,
Expand Down
2 changes: 2 additions & 0 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ declare const ASC_TABLE_BASE: i32;
declare const ASC_OPTIMIZE_LEVEL: i32;
/** Provided shrinkLevel option. */
declare const ASC_SHRINK_LEVEL: i32;
/** Provided lowMemoryLimit option. */
declare const ASC_LOW_MEMORY_LIMIT: i32;
/** Whether the sign extension feature is enabled. */
declare const ASC_FEATURE_SIGN_EXTENSION: bool;
/** Whether the mutable globals feature is enabled. */
Expand Down
15 changes: 13 additions & 2 deletions std/assembly/rt/tlsf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,10 @@ function addMemory(root: Root, start: usize, end: usize): bool {

/** Grows memory to fit at least another block of the specified size. */
function growMemory(root: Root, size: usize): void {
if (ASC_LOW_MEMORY_LIMIT) {
unreachable();
return;
}
// Here, both rounding performed in searchBlock ...
const halfMaxSize = BLOCK_MAXSIZE >> 1;
if (size < halfMaxSize) { // don't round last fl
Expand Down Expand Up @@ -462,7 +466,7 @@ function prepareSize(size: usize): usize {
export function maybeInitialize(): Root {
var root = ROOT;
if (!root) {
const rootOffset = (__heap_base + AL_MASK) & ~AL_MASK;
let rootOffset = (__heap_base + AL_MASK) & ~AL_MASK;
let pagesBefore = memory.size();
let pagesNeeded = <i32>((((rootOffset + ROOT_SIZE) + 0xffff) & ~0xffff) >>> 16);
if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable();
Expand All @@ -475,7 +479,14 @@ export function maybeInitialize(): Root {
SETHEAD(root, fl, sl, null);
}
}
addMemory(root, (rootOffset + ROOT_SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16);
let memStart = (rootOffset + ROOT_SIZE + AL_MASK) & ~AL_MASK;
if (ASC_LOW_MEMORY_LIMIT) {
const memEnd = <usize>ASC_LOW_MEMORY_LIMIT & ~AL_MASK;
if (memStart <= memEnd) addMemory(root, memStart, memEnd);
else unreachable(); // low memory limit already exceeded
} else {
addMemory(root, memStart, memory.size() << 16);
}
ROOT = root;
}
return root;
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/do.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@
if
i32.const 0
i32.const 1072
i32.const 490
i32.const 501
i32.const 13
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -883,7 +883,7 @@
if
i32.const 0
i32.const 1072
i32.const 502
i32.const 513
i32.const 19
call $~lib/builtins/abort
unreachable
Expand All @@ -899,7 +899,7 @@
if
i32.const 0
i32.const 1072
i32.const 510
i32.const 521
i32.const 13
call $~lib/builtins/abort
unreachable
Expand Down
19 changes: 12 additions & 7 deletions tests/compiler/do.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
(table $0 1 funcref)
(global $do/ran (mut i32) (i32.const 0))
(global $~lib/rt/tlsf/ROOT (mut i32) (i32.const 0))
(global $~lib/ASC_LOW_MEMORY_LIMIT i32 (i32.const 0))
(global $~lib/rt/tlsf/collectLock (mut i32) (i32.const 0))
(global $~lib/gc/gc.auto (mut i32) (i32.const 1))
(global $~started (mut i32) (i32.const 0))
Expand Down Expand Up @@ -1164,7 +1165,9 @@
global.get $~lib/heap/__heap_base
i32.const 15
i32.add
i32.const -16
i32.const 15
i32.const -1
i32.xor
i32.and
local.set $1
memory.size
Expand Down Expand Up @@ -1273,7 +1276,6 @@
br $for-loop|0
end
end
local.get $0
local.get $1
i32.const 1572
i32.add
Expand All @@ -1283,6 +1285,9 @@
i32.const -1
i32.xor
i32.and
local.set $5
local.get $0
local.get $5
memory.size
i32.const 16
i32.shl
Expand All @@ -1302,7 +1307,7 @@
if
i32.const 112
i32.const 64
i32.const 457
i32.const 461
i32.const 29
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -1693,7 +1698,7 @@
if
i32.const 0
i32.const 64
i32.const 490
i32.const 501
i32.const 13
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -1734,7 +1739,7 @@
if
i32.const 0
i32.const 64
i32.const 502
i32.const 513
i32.const 19
call $~lib/builtins/abort
unreachable
Expand All @@ -1753,7 +1758,7 @@
if
i32.const 0
i32.const 64
i32.const 507
i32.const 518
i32.const 17
call $~lib/builtins/abort
unreachable
Expand All @@ -1770,7 +1775,7 @@
if
i32.const 0
i32.const 64
i32.const 510
i32.const 521
i32.const 13
call $~lib/builtins/abort
unreachable
Expand Down
10 changes: 5 additions & 5 deletions tests/compiler/extends-baseaggregate.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@
if
i32.const 1184
i32.const 1136
i32.const 457
i32.const 461
i32.const 29
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -908,7 +908,7 @@
if
i32.const 0
i32.const 1136
i32.const 490
i32.const 501
i32.const 13
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -1000,7 +1000,7 @@
if
i32.const 0
i32.const 1136
i32.const 502
i32.const 513
i32.const 19
call $~lib/builtins/abort
unreachable
Expand All @@ -1016,7 +1016,7 @@
if
i32.const 0
i32.const 1136
i32.const 510
i32.const 521
i32.const 13
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -1131,7 +1131,7 @@
if
i32.const 0
i32.const 1136
i32.const 570
i32.const 581
i32.const 2
call $~lib/builtins/abort
unreachable
Expand Down
Loading