Properly handle metadce of MINIMAL_RUNTIME exports (#10663)
We handled imports, but not exports. As a result, some things in the wasm binary were not cleaned up by metadce. I noticed this when comparing builds with minimal and normal runtime - the JS was much smaller in minimal, but the binaries were a little bigger due to stuff like unnecessary dynCalls, things like stackAlloc, etc. This adds a metadce test for minimal runtime. After this, minimal runtime has similar wasm sizes to the normal runtime.
This commit is contained in:
Родитель
9f06f0a187
Коммит
afba0e5191
|
@ -3,8 +3,8 @@
|
|||
"a.html.gz": 377,
|
||||
"a.js": 5352,
|
||||
"a.js.gz": 2540,
|
||||
"a.wasm": 8364,
|
||||
"a.wasm.gz": 4520,
|
||||
"total": 14293,
|
||||
"total_gz": 7437
|
||||
"a.wasm": 8350,
|
||||
"a.wasm.gz": 4509,
|
||||
"total": 14285,
|
||||
"total_gz": 7426
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"a.html.gz": 377,
|
||||
"a.js": 5133,
|
||||
"a.js.gz": 2426,
|
||||
"a.wasm": 11318,
|
||||
"a.wasm.gz": 7100,
|
||||
"total": 17014,
|
||||
"total_gz": 9903
|
||||
"a.wasm": 11262,
|
||||
"a.wasm.gz": 7065,
|
||||
"total": 16958,
|
||||
"total_gz": 9868
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"a.html": 588,
|
||||
"a.html.gz": 386,
|
||||
"a.js": 24667,
|
||||
"a.js.gz": 9175,
|
||||
"a.js": 24518,
|
||||
"a.js.gz": 9119,
|
||||
"a.mem": 3168,
|
||||
"a.mem.gz": 2711,
|
||||
"total": 28423,
|
||||
"total_gz": 12272
|
||||
"total": 28274,
|
||||
"total_gz": 12216
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"a.html.gz": 377,
|
||||
"a.js": 4844,
|
||||
"a.js.gz": 2365,
|
||||
"a.wasm": 8364,
|
||||
"a.wasm.gz": 4520,
|
||||
"total": 13785,
|
||||
"total_gz": 7262
|
||||
"a.wasm": 8350,
|
||||
"a.wasm.gz": 4509,
|
||||
"total": 13777,
|
||||
"total_gz": 7251
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"a.html.gz": 377,
|
||||
"a.js": 4624,
|
||||
"a.js.gz": 2254,
|
||||
"a.wasm": 11318,
|
||||
"a.wasm.gz": 7100,
|
||||
"total": 16505,
|
||||
"total_gz": 9731
|
||||
"a.wasm": 11262,
|
||||
"a.wasm.gz": 7065,
|
||||
"total": 16449,
|
||||
"total_gz": 9696
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"a.html": 588,
|
||||
"a.html.gz": 386,
|
||||
"a.js": 24162,
|
||||
"a.js.gz": 9012,
|
||||
"a.js": 24013,
|
||||
"a.js.gz": 8955,
|
||||
"a.mem": 3168,
|
||||
"a.mem.gz": 2711,
|
||||
"total": 27918,
|
||||
"total_gz": 12109
|
||||
"total": 27769,
|
||||
"total_gz": 12052
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"a.html.gz": 427,
|
||||
"a.js": 450,
|
||||
"a.js.gz": 321,
|
||||
"a.wasm": 172,
|
||||
"a.wasm.gz": 166,
|
||||
"total": 1287,
|
||||
"total_gz": 914
|
||||
"a.wasm": 96,
|
||||
"a.wasm.gz": 109,
|
||||
"total": 1211,
|
||||
"total_gz": 857
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"a.html": 697,
|
||||
"a.html.gz": 437,
|
||||
"a.js": 2179,
|
||||
"a.js.gz": 979,
|
||||
"a.js": 2026,
|
||||
"a.js.gz": 911,
|
||||
"a.mem": 6,
|
||||
"a.mem.gz": 32,
|
||||
"total": 2882,
|
||||
"total_gz": 1448
|
||||
"total": 2729,
|
||||
"total_gz": 1380
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"a.html": 2907,
|
||||
"a.html.gz": 1231,
|
||||
"total": 2913,
|
||||
"total": 2907,
|
||||
"total_gz": 1231
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"a.html": 13704,
|
||||
"a.html.gz": 7262,
|
||||
"total": 13744,
|
||||
"total_gz": 7262
|
||||
"a.html": 13468,
|
||||
"a.html.gz": 7113,
|
||||
"total": 13468,
|
||||
"total_gz": 7113
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"a.html": 14100,
|
||||
"a.html.gz": 7546,
|
||||
"total": 14100,
|
||||
"total_gz": 7546
|
||||
"a.html": 13845,
|
||||
"a.html.gz": 7353,
|
||||
"total": 13845,
|
||||
"total_gz": 7353
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"a.html": 20529,
|
||||
"a.html.gz": 8546,
|
||||
"total": 20529,
|
||||
"total_gz": 8546
|
||||
"a.html": 20054,
|
||||
"a.html.gz": 8394,
|
||||
"total": 20054,
|
||||
"total_gz": 8394
|
||||
}
|
||||
|
|
|
@ -36,8 +36,12 @@
|
|||
{
|
||||
"name": "emcc$export$_main",
|
||||
"export": "_main",
|
||||
"reaches": [],
|
||||
"root": true
|
||||
"reaches": []
|
||||
},
|
||||
{
|
||||
"name": "emcc$export$_unused",
|
||||
"export": "c",
|
||||
"reaches": []
|
||||
},
|
||||
{
|
||||
"name": "emcc$import$_emscripten_console_log",
|
||||
|
|
|
@ -64,12 +64,13 @@ var imports = {
|
|||
})
|
||||
}
|
||||
};
|
||||
var _main;
|
||||
var _main, _unused;
|
||||
WebAssembly.instantiate(Module["wasm"], imports).then((function(output) {
|
||||
var asm = output.instance.exports;
|
||||
_main = asm["b"];
|
||||
_unused = asm["c"];
|
||||
initRuntime();
|
||||
ready();
|
||||
}));
|
||||
|
||||
// EXTRA_INFO: { "exports": [["_main","_main"]]}
|
||||
// EXTRA_INFO: { "exports": [["_main","_main"]]}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
a
|
||||
b
|
|
@ -0,0 +1,2 @@
|
|||
$__wasm_call_ctors
|
||||
$add
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
memory
|
||||
table
|
|
@ -8257,6 +8257,7 @@ int main() {
|
|||
'O3': (['-O3'], [], [], 85), # noqa
|
||||
'Os': (['-Os'], [], [], 85), # noqa
|
||||
'Oz': (['-Oz'], [], [], 85), # noqa
|
||||
'Os_mr': (['-Os', '-s', 'MINIMAL_RUNTIME'], [], [], 85), # noqa
|
||||
})
|
||||
@no_fastcomp()
|
||||
def test_metadce_minimal(self, *args):
|
||||
|
|
|
@ -550,7 +550,7 @@ function emitDCEGraph(ast) {
|
|||
//
|
||||
// The imports that wasm receives look like this:
|
||||
//
|
||||
// Module.asmLibraryArg = { "abort": abort, "assert": assert, [..] };
|
||||
// var asmLibraryArg = { "abort": abort, "assert": assert, [..] };
|
||||
//
|
||||
// The exports are trickier, as they have a different form whether or not
|
||||
// async compilation is enabled. It can be either:
|
||||
|
@ -563,6 +563,14 @@ function emitDCEGraph(ast) {
|
|||
// return Module["asm"]["_malloc"].apply(null, arguments);
|
||||
// });
|
||||
//
|
||||
// or, in the minimal runtime, it looks like
|
||||
//
|
||||
// WebAssembly.instantiate(Module["wasm"], imports).then(function(output) {
|
||||
// var asm = output.instance.exports;
|
||||
// ..
|
||||
// _malloc = asm["malloc"];
|
||||
// ..
|
||||
// });
|
||||
var imports = [];
|
||||
var defuns = [];
|
||||
var dynCallNames = [];
|
||||
|
@ -570,6 +578,7 @@ function emitDCEGraph(ast) {
|
|||
var modulePropertyToGraphName = {};
|
||||
var exportNameToGraphName = {}; // identical to asm['..'] nameToGraphName
|
||||
var foundAsmLibraryArgAssign = false;
|
||||
var foundMinimalRuntimeExports = false;
|
||||
var graph = [];
|
||||
|
||||
function saveAsmExport(name, asmName) {
|
||||
|
@ -628,15 +637,68 @@ function emitDCEGraph(ast) {
|
|||
// name may be slightly different (extra "_" in wasm backend)
|
||||
saveAsmExport(name, asmName);
|
||||
emptyOut(node); // ignore this in the second pass; this does not root
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// A variable declaration that has no initial values can be ignored in
|
||||
// the second pass, these are just declarations, not roots - an actual
|
||||
// use must be found in order to root.
|
||||
if (!node.declarations.reduce(function(hasInit, decl) {
|
||||
return hasInit || !!decl.init
|
||||
}, false)) {
|
||||
emptyOut(node);
|
||||
}
|
||||
} else if (node.type === 'FunctionDeclaration') {
|
||||
defuns.push(node);
|
||||
var name = node.id.name;
|
||||
nameToGraphName[name] = getGraphName(name, 'defun');
|
||||
emptyOut(node); // ignore this in the second pass; we scan defuns separately
|
||||
} else if (node.type === 'FunctionExpression') {
|
||||
// Check if this is the minimal runtime exports function, which looks like
|
||||
// function(output) { var asm = output.instance.exports;
|
||||
if (node.params.length === 1 && node.params[0].type === 'Identifier' &&
|
||||
node.params[0].name === 'output' && node.body.type === 'BlockStatement') {
|
||||
var body = node.body.body;
|
||||
if (body.length >= 1) {
|
||||
var first = body[0];
|
||||
if (first.type === 'VariableDeclaration' && first.declarations.length === 1) {
|
||||
var decl = first.declarations[0];
|
||||
if (decl.id.type === 'Identifier' && decl.id.name === 'asm') {
|
||||
var value = decl.init;
|
||||
if (value.type === 'MemberExpression' &&
|
||||
value.object.type === 'MemberExpression' &&
|
||||
value.object.object.type === 'Identifier' &&
|
||||
value.object.object.name === 'output' &&
|
||||
value.object.property.type === 'Identifier' &&
|
||||
value.object.property.name === 'instance' &&
|
||||
value.property.type === 'Identifier' &&
|
||||
value.property.name === 'exports') {
|
||||
// This looks very much like what we are looking for.
|
||||
assert(!foundMinimalRuntimeExports);
|
||||
for (var i = 1; i < body.length; i++) {
|
||||
var item = body[i];
|
||||
if (item.type === 'ExpressionStatement' &&
|
||||
item.expression.type === 'AssignmentExpression' &&
|
||||
item.expression.operator === '=' &&
|
||||
item.expression.left.type === 'Identifier' &&
|
||||
item.expression.right.type === 'MemberExpression' &&
|
||||
item.expression.right.object.type === 'Identifier' &&
|
||||
item.expression.right.object.name === 'asm' &&
|
||||
item.expression.right.property.type === 'Literal') {
|
||||
var name = item.expression.left.name;
|
||||
var asmName = item.expression.right.property.value;
|
||||
saveAsmExport(name, asmName);
|
||||
emptyOut(item); // ignore all this in the second pass; this does not root
|
||||
}
|
||||
}
|
||||
foundMinimalRuntimeExports = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// must find the info we need
|
||||
|
|
Загрузка…
Ссылка в новой задаче