Skip to content

Update binaryen #2461

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 24 commits into from
Aug 26, 2022
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
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
},
"engineStrict": true,
"dependencies": {
"binaryen": "109.0.0-nightly.20220813",
"binaryen": "109.0.0-nightly.20220826",
"long": "^5.2.0"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ export namespace CommonNames {
export const newArray = "__newArray";
export const BLOCK = "~lib/rt/common/BLOCK";
export const OBJECT = "~lib/rt/common/OBJECT";
// memory & table
export const DefaultMemory = "0";
export const DefaultTable = "0";
}

// shared
Expand Down
225 changes: 139 additions & 86 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,16 +355,26 @@ export const enum RuntimeFeatures {
setArgumentsLength = 1 << 6
}

/** Imported default names of compiler-generated elements. */
export namespace ImportNames {
/** Name of the default namespace */
export const DefaultNamespace = "env";
/** Name of the memory instance, if imported. */
export const Memory = "memory";
/** Name of the table instance, if imported. */
export const Table = "table";
}

/** Exported names of compiler-generated elements. */
export namespace ExportNames {
/** Name of the memory instance, if exported. */
export const Memory = "memory";
/** Name of the table instance, if exported. */
export const Table = "table";
Comment on lines +370 to +373
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What consideration were there to make these uppercase? I think in all other places we are just using lowercase names.

Copy link
Member Author

@MaxGraey MaxGraey Aug 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strict preference. But I think we should normalize everything to CamelCase-style all enums / namespaced constants similar to UnaryOp.Eqz32 and others

/** Name of the argumentsLength varargs helper global. */
export const argumentsLength = "__argumentsLength";
/** Name of the alternative argumentsLength setter function. */
export const setArgumentsLength = "__setArgumentsLength";
/** Name of the memory instance, if exported. */
export const memory = "memory";
/** Name of the table instance, if exported. */
export const table = "table";
}

/** Functions to export if `--exportRuntime` is set. */
Expand Down Expand Up @@ -639,25 +649,84 @@ export class Compiler extends DiagnosticEmitter {
}
}

this.memoryOffset = memoryOffset;
// setup default memory & table
this.initDefaultMemory(memoryOffset);
this.initDefaultTable();

// check that we didn't exceed lowMemoryLimit already
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)
// expose the arguments length helper if there are varargs exports
if (this.runtimeFeatures & RuntimeFeatures.setArgumentsLength) {
module.addFunction(BuiltinNames.setArgumentsLength, TypeRef.I32, TypeRef.None, null,
module.global_set(this.ensureArgumentsLength(), module.local_get(0, TypeRef.I32))
);
module.addFunctionExport(BuiltinNames.setArgumentsLength, ExportNames.setArgumentsLength);
}

// NOTE: no more element compiles from here. may go to the start function!

// compile the start function if not empty or if explicitly requested
var startIsEmpty = !startFunctionBody.length;
var exportStart = options.exportStart;
if (!startIsEmpty || exportStart != null) {
let signature = startFunctionInstance.signature;
if (!startIsEmpty && exportStart != null) {
module.addGlobal(BuiltinNames.started, TypeRef.I32, true, module.i32(0));
startFunctionBody.unshift(
module.global_set(BuiltinNames.started, module.i32(1))
);
startFunctionBody.unshift(
module.if(
module.global_get(BuiltinNames.started, TypeRef.I32),
module.return()
)
);
}
let funcRef = module.addFunction(
startFunctionInstance.internalName,
signature.paramRefs,
signature.resultRefs,
typesToRefs(startFunctionInstance.additionalLocals),
module.flatten(startFunctionBody)
);
startFunctionInstance.finalize(module, funcRef);
if (exportStart == null) module.setStart(funcRef);
else {
if (!isIdentifier(exportStart) || module.hasExport(exportStart)) {
this.error(
DiagnosticCode.Start_function_name_0_is_invalid_or_conflicts_with_another_export,
this.program.nativeRange, exportStart
);
} else {
module.addFunctionExport(startFunctionInstance.internalName, exportStart);
}
}
}

// set up memory
// Run custom passes
if (hasShadowStack) {
this.shadowStack.walkModule();
}
if (program.lookup("ASC_RTRACE") != null) {
new RtraceMemory(this).walkModule();
}

return module;
}

private initDefaultMemory(memoryOffset: i64): void {
this.memoryOffset = memoryOffset;

var options = this.options;
var module = this.module;
var memorySegments = this.memorySegments;

var initialPages: u32 = 0;
if (this.options.memoryBase /* is specified */ || this.memorySegments.length) {
var maximumPages = Module.UNLIMITED_MEMORY;
var isSharedMemory = false;

if (options.memoryBase /* is specified */ || memorySegments.length) {
initialPages = u32(i64_low(i64_shr_u(i64_align(memoryOffset, 0x10000), i64_new(16))));
}

if (options.initialMemory) {
if (options.initialMemory < initialPages) {
this.error(
Expand All @@ -669,7 +738,7 @@ export class Compiler extends DiagnosticEmitter {
initialPages = options.initialMemory;
}
}
var maximumPages = Module.UNLIMITED_MEMORY;

if (options.maximumMemory) {
if (options.maximumMemory < initialPages) {
this.error(
Expand All @@ -681,7 +750,7 @@ export class Compiler extends DiagnosticEmitter {
maximumPages = options.maximumMemory;
}
}
var isSharedMemory = false;

if (options.sharedMemory) {
isSharedMemory = true;
if (!options.maximumMemory) {
Expand All @@ -699,21 +768,52 @@ export class Compiler extends DiagnosticEmitter {
isSharedMemory = false;
}
}

// check that we didn't exceed lowMemoryLimit already
var lowMemoryLimit32 = 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)
);
}
}

// Just stubbed memory. Will update later in finalizeMemory
module.setMemory(
initialPages,
maximumPages,
this.memorySegments,
memorySegments,
options.target,
options.exportMemory ? ExportNames.memory : null,
options.exportMemory ? ExportNames.Memory : null,
CommonNames.DefaultMemory,
isSharedMemory
);

// import memory if requested (default memory is named '0' by Binaryen)
if (options.importMemory) module.addMemoryImport("0", "env", "memory", isSharedMemory);
if (options.importMemory) {
module.addMemoryImport(
CommonNames.DefaultMemory,
ImportNames.DefaultNamespace,
ImportNames.Memory,
isSharedMemory
);
}
}

private initDefaultTable(): void {
var options = this.options;
var module = this.module;

// import and/or export table if requested (default table is named '0' by Binaryen)
if (options.importTable) {
module.addTableImport("0", "env", "table");
module.addTableImport(
CommonNames.DefaultTable,
ImportNames.DefaultNamespace,
ImportNames.Table
);
if (options.pedantic && options.willOptimize) {
this.pedantic(
DiagnosticCode.Importing_the_table_disables_some_indirect_call_optimizations,
Expand All @@ -722,7 +822,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
if (options.exportTable) {
module.addTableExport("0", ExportNames.table);
module.addTableExport(CommonNames.DefaultTable, ExportNames.Table);
if (options.pedantic && options.willOptimize) {
this.pedantic(
DiagnosticCode.Exporting_the_table_disables_some_indirect_call_optimizations,
Expand All @@ -732,80 +832,33 @@ export class Compiler extends DiagnosticEmitter {
}

// set up function table (first elem is blank)
var tableBase = this.options.tableBase;
var tableBase = options.tableBase;
if (!tableBase) tableBase = 1; // leave first elem blank
var functionTable = this.functionTable;
var functionTableNames = new Array<string>(functionTable.length);
for (let i = 0, k = functionTable.length; i < k; ++i) {
functionTableNames[i] = functionTable[i].internalName;
}

var tableSize = tableBase + functionTable.length;
module.addFunctionTable(
"0",
tableSize,
var initialTableSize = <Index>tableBase + functionTable.length;
var maximumTableSize = Module.UNLIMITED_TABLE;

if (!(options.importTable || options.exportTable)) {
// use fixed size for non-imported and non-exported tables
options.importTable || options.exportTable ? Module.UNLIMITED_TABLE : tableSize,
maximumTableSize = initialTableSize;
if (options.willOptimize) {
// Hint for directize pass which indicate table's content will not change
// and can be better optimized
module.setPassArgument("directize-initial-contents-immutable", "true");
}
}
module.addFunctionTable(
CommonNames.DefaultTable,
initialTableSize,
maximumTableSize,
functionTableNames,
module.i32(tableBase)
);

// expose the arguments length helper if there are varargs exports
if (this.runtimeFeatures & RuntimeFeatures.setArgumentsLength) {
module.addFunction(BuiltinNames.setArgumentsLength, TypeRef.I32, TypeRef.None, null,
module.global_set(this.ensureArgumentsLength(), module.local_get(0, TypeRef.I32))
);
module.addFunctionExport(BuiltinNames.setArgumentsLength, ExportNames.setArgumentsLength);
}

// NOTE: no more element compiles from here. may go to the start function!

// compile the start function if not empty or if explicitly requested
var startIsEmpty = !startFunctionBody.length;
var exportStart = options.exportStart;
if (!startIsEmpty || exportStart != null) {
let signature = startFunctionInstance.signature;
if (!startIsEmpty && exportStart != null) {
module.addGlobal(BuiltinNames.started, TypeRef.I32, true, module.i32(0));
startFunctionBody.unshift(
module.global_set(BuiltinNames.started, module.i32(1))
);
startFunctionBody.unshift(
module.if(
module.global_get(BuiltinNames.started, TypeRef.I32),
module.return()
)
);
}
let funcRef = module.addFunction(
startFunctionInstance.internalName,
signature.paramRefs,
signature.resultRefs,
typesToRefs(startFunctionInstance.additionalLocals),
module.flatten(startFunctionBody)
);
startFunctionInstance.finalize(module, funcRef);
if (exportStart == null) module.setStart(funcRef);
else {
if (!isIdentifier(exportStart) || module.hasExport(exportStart)) {
this.error(
DiagnosticCode.Start_function_name_0_is_invalid_or_conflicts_with_another_export,
this.program.nativeRange, exportStart
);
} else {
module.addFunctionExport(startFunctionInstance.internalName, exportStart);
}
}
}

// Run custom passes
if (hasShadowStack) {
this.shadowStack.walkModule();
}
if (program.lookup("ASC_RTRACE") != null) {
new RtraceMemory(this).walkModule();
}

return module;
}

// === Exports ==================================================================================
Expand Down
Loading