Skip to content

Commit 95dfa38

Browse files
authored
[MINIMAL_RUNTIME] Remove WASM_MODULE_EXPORTS replacement. NFC (#23405)
Followup to #23403 We now generate a new function called `assignWasmExports` which gets called instead of the `WASM_MODULE_EXPORTS` replacement. As is evident from the lack of code size changes closure is able to inline this function. The motivation for this change is 3-fold: 1. Remove differences between MINIMAL_RUNTIME and regular runtime. 2. Simplifies and logic by removing magic late stage replacement. 3. Sets the stage for regular runtime to use the same technique.
1 parent 7aa70ae commit 95dfa38

File tree

5 files changed

+56
-105
lines changed

5 files changed

+56
-105
lines changed

src/postamble_minimal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ WebAssembly.instantiate(Module['wasm'], imports).then((output) => {
183183
#if !DECLARE_ASM_MODULE_EXPORTS
184184
exportWasmSymbols(wasmExports);
185185
#else
186-
<<< WASM_MODULE_EXPORTS >>>
186+
assignWasmExports(wasmExports);
187187
#endif
188188
#if '$wasmTable' in addedLibraryItems
189189
wasmTable = wasmExports['__indirect_function_table'];

test/js_optimizer/minimal-runtime-2-emitDCEGraph.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,14 @@ var imports = {
6565
}
6666
};
6767
var _main, _unused, wasmExports;
68-
WebAssembly.instantiate(Module["wasm"], imports).then(((output) => {
69-
wasmExports = output.instance.exports;
68+
function assignWasmExports(wasmExports) {
7069
_main = wasmExports["b"];
7170
_unused = wasmExports["c"];
72-
initRuntime();
71+
}
72+
WebAssembly.instantiate(Module["wasm"], imports).then(((output) => {
73+
wasmExports = output.instance.exports;
74+
assignWasmExports(wasmExports);
75+
initRuntime(wasmExports);
7376
ready();
7477
}));
7578

test/js_optimizer/minimal-runtime-emitDCEGraph.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var wasmImports = {
3636
function run() {
3737
var ret = _main();
3838
}
39-
function initRuntime() {
39+
function initRuntime(wasmExports) {
4040
for (var i in __ATINIT__) __ATINIT__[i].func();
4141
}
4242
var env = wasmImports;
@@ -65,11 +65,15 @@ var imports = {
6565
}
6666
};
6767
var _main, _unused;
68-
WebAssembly.instantiate(Module["wasm"], imports).then(((output) => {
69-
var wasmExports = output.instance.exports;
68+
function assignWasmExports(wasmExports) {
7069
_main = wasmExports["b"];
7170
_unused = wasmExports["c"];
72-
initRuntime();
71+
}
72+
73+
WebAssembly.instantiate(Module["wasm"], imports).then(((output) => {
74+
var wasmExports = output.instance.exports;
75+
assignWasmExports(wasmExports);
76+
initRuntime(wasmExports);
7377
ready();
7478
}));
7579

tools/acorn-optimizer.mjs

Lines changed: 30 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -674,9 +674,7 @@ function emitDCEGraph(ast) {
674674
//
675675
// or, in the minimal runtime, it looks like
676676
//
677-
// WebAssembly.instantiate(Module["wasm"], imports).then((output) => {
678-
// var wasmExports = output.instance.exports; // may also not have "var", if
679-
// // declared outside and used elsewhere
677+
// function assignWasmExports(wasmExports)
680678
// ..
681679
// _malloc = wasmExports["malloc"];
682680
// ..
@@ -801,79 +799,45 @@ function emitDCEGraph(ast) {
801799
emptyOut(node);
802800
}
803801
} else if (node.type === 'FunctionDeclaration') {
804-
if (!specialScopes) {
805-
defuns.push(node);
806-
const name = node.id.name;
807-
nameToGraphName[name] = getGraphName(name, 'defun');
808-
emptyOut(node); // ignore this in the second pass; we scan defuns separately
809-
}
810-
} else if (node.type === 'ArrowFunctionExpression') {
811-
assert(specialScopes > 0);
812-
specialScopes--;
802+
const name = node.id.name;
813803
// Check if this is the minimal runtime exports function, which looks like
814-
// (output) => { var wasmExports = output.instance.exports;
804+
// function assignWasmExports(wasmExports)
815805
if (
806+
name == 'assignWasmExports' &&
816807
node.params.length === 1 &&
817808
node.params[0].type === 'Identifier' &&
818-
node.params[0].name === 'output' &&
819-
node.body.type === 'BlockStatement'
809+
node.params[0].name === 'wasmExports'
820810
) {
811+
// This looks very much like what we are looking for.
821812
const body = node.body.body;
822-
if (body.length >= 1) {
823-
const first = body[0];
824-
let target;
825-
let value; // "(var?) target = value"
826-
// Look either for var wasmExports = or just wasmExports =
827-
if (first.type === 'VariableDeclaration' && first.declarations.length === 1) {
828-
const decl = first.declarations[0];
829-
target = decl.id;
830-
value = decl.init;
831-
} else if (
832-
first.type === 'ExpressionStatement' &&
833-
first.expression.type === 'AssignmentExpression'
813+
assert(!foundMinimalRuntimeExports);
814+
foundMinimalRuntimeExports = true;
815+
for (let i = 0; i < body.length; i++) {
816+
const item = body[i];
817+
if (
818+
item.type === 'ExpressionStatement' &&
819+
item.expression.type === 'AssignmentExpression' &&
820+
item.expression.operator === '=' &&
821+
item.expression.left.type === 'Identifier' &&
822+
item.expression.right.type === 'MemberExpression' &&
823+
item.expression.right.object.type === 'Identifier' &&
824+
item.expression.right.object.name === 'wasmExports' &&
825+
item.expression.right.property.type === 'Literal'
834826
) {
835-
const assign = first.expression;
836-
if (assign.operator === '=') {
837-
target = assign.left;
838-
value = assign.right;
839-
}
840-
}
841-
if (target && target.type === 'Identifier' && target.name === 'wasmExports' && value) {
842-
if (
843-
value.type === 'MemberExpression' &&
844-
value.object.type === 'MemberExpression' &&
845-
value.object.object.type === 'Identifier' &&
846-
value.object.object.name === 'output' &&
847-
value.object.property.type === 'Identifier' &&
848-
value.object.property.name === 'instance' &&
849-
value.property.type === 'Identifier' &&
850-
value.property.name === 'exports'
851-
) {
852-
// This looks very much like what we are looking for.
853-
assert(!foundMinimalRuntimeExports);
854-
for (let i = 1; i < body.length; i++) {
855-
const item = body[i];
856-
if (
857-
item.type === 'ExpressionStatement' &&
858-
item.expression.type === 'AssignmentExpression' &&
859-
item.expression.operator === '=' &&
860-
item.expression.left.type === 'Identifier' &&
861-
item.expression.right.type === 'MemberExpression' &&
862-
item.expression.right.object.type === 'Identifier' &&
863-
item.expression.right.object.name === 'wasmExports' &&
864-
item.expression.right.property.type === 'Literal'
865-
) {
866-
const name = item.expression.left.name;
867-
const asmName = item.expression.right.property.value;
868-
saveAsmExport(name, asmName);
869-
emptyOut(item); // ignore all this in the second pass; this does not root
870-
}
871-
}
872-
foundMinimalRuntimeExports = true;
873-
}
827+
const name = item.expression.left.name;
828+
const asmName = item.expression.right.property.value;
829+
saveAsmExport(name, asmName);
830+
emptyOut(item); // ignore all this in the second pass; this does not root
874831
}
875832
}
833+
} else if (!specialScopes) {
834+
defuns.push(node);
835+
nameToGraphName[name] = getGraphName(name, 'defun');
836+
emptyOut(node); // ignore this in the second pass; we scan defuns separately
876837
}
838+
} else if (node.type === 'ArrowFunctionExpression') {
839+
assert(specialScopes > 0);
840+
specialScopes--;
877841
} else if (node.type === 'Property' && node.method) {
878842
assert(specialScopes > 0);
879843
specialScopes--;

tools/emscripten.py

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,6 @@
5353
]
5454

5555

56-
def compute_minimal_runtime_initializer_and_exports(post, exports, receiving):
57-
# Declare all exports out to global JS scope so that JS library functions can access them in a
58-
# way that minifies well with Closure
59-
# e.g. var a,b,c,d,e,f;
60-
61-
# `receiving` contains all of the assignments from wasm exports to
62-
# global JS variables: e.g. a = wasmExports['a']; b = wasmExports['b'];
63-
post = shared.do_replace(post, '<<< WASM_MODULE_EXPORTS >>>', receiving)
64-
65-
exports = [asmjs_mangle(x) for x in exports if x != building.WASM_CALL_CTORS]
66-
receiving = 'var ' + ',\n '.join(exports) + ';'
67-
return post, receiving
68-
69-
7056
def write_output_file(outfile, module):
7157
for chunk in module:
7258
outfile.write(chunk)
@@ -459,12 +445,6 @@ def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True, base_metadat
459445

460446
receiving = create_receiving(function_exports)
461447

462-
if settings.MINIMAL_RUNTIME:
463-
if settings.DECLARE_ASM_MODULE_EXPORTS:
464-
post, receiving = compute_minimal_runtime_initializer_and_exports(post, function_exports, receiving)
465-
else:
466-
receiving = ''
467-
468448
module = create_module(receiving, metadata, global_exports, forwarded_json['librarySymbols'])
469449

470450
metadata.library_definitions = forwarded_json['libraryDefinitions']
@@ -964,30 +944,30 @@ def create_receiving(function_exports):
964944
receiving = []
965945

966946
if settings.MINIMAL_RUNTIME:
967-
# In Wasm exports are assigned inside a function to variables
947+
# Exports are assigned inside a function to variables
968948
# existing in top level JS scope, i.e.
969949
# var _main;
970-
# WebAssembly.instantiate(Module['wasm'], imports).then((output) => {
971-
# var wasmExports = output.instance.exports;
950+
# function assignWasmExports(wasmExport) {
972951
# _main = wasmExports["_main"];
973952
generate_dyncall_assignment = settings.DYNCALLS and '$dynCall' in settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE
974-
exports_that_are_not_initializers = [x for x in function_exports if x != building.WASM_CALL_CTORS]
975-
976-
for s in exports_that_are_not_initializers:
953+
exports = [x for x in function_exports if x != building.WASM_CALL_CTORS]
954+
receiving.append('function assignWasmExports(wasmExports) {')
955+
for s in exports:
977956
mangled = asmjs_mangle(s)
978957
dynCallAssignment = ('dynCalls["' + s.replace('dynCall_', '') + '"] = ') if generate_dyncall_assignment and mangled.startswith('dynCall_') else ''
979958
should_export = settings.EXPORT_ALL or (settings.EXPORT_KEEPALIVE and mangled in settings.EXPORTED_FUNCTIONS)
980959
export_assignment = ''
981960
if settings.MODULARIZE and should_export:
982961
export_assignment = f"Module['{mangled}'] = "
983-
receiving += [f'{export_assignment}{dynCallAssignment}{mangled} = wasmExports["{s}"]']
962+
receiving.append(f" {export_assignment}{dynCallAssignment}{mangled} = wasmExports['{s}'];")
963+
receiving.append('}')
964+
sep = ',\n '
965+
mangled = [asmjs_mangle(s) for s in exports]
966+
receiving.append(f'var {sep.join(mangled)};')
984967
else:
985968
receiving += make_export_wrappers(function_exports)
986969

987-
if settings.MINIMAL_RUNTIME:
988-
return '\n '.join(receiving) + '\n'
989-
else:
990-
return '\n'.join(receiving) + '\n'
970+
return '\n'.join(receiving) + '\n'
991971

992972

993973
def create_module(receiving, metadata, global_exports, library_symbols):

0 commit comments

Comments
 (0)