From 96a4a9056229917218e7e1f3466155fd29c491fc Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2020 10:34:25 -0700 Subject: [PATCH] Clear out asm assignments in minimal runtime metadce (#10725) Before this, if X was never used (via metadce), we would still have X = asm['X'] After this that doesn't exist. Closure can do this (but doesn't always), so this mostly helps non-closure builds. --- tests/code_size/hello_webgl2_wasm2js.json | 4 +-- tests/code_size/hello_webgl_wasm2js.json | 4 +-- tests/code_size/hello_world_wasm2js.json | 4 +-- tests/code_size/random_printf_wasm2js.json | 8 +++--- ...al-runtime-applyDCEGraphRemovals-output.js | 22 +++++++++++++++ .../minimal-runtime-applyDCEGraphRemovals.js | 20 +++++++++++++ tests/test_other.py | 2 ++ tools/acorn-optimizer.js | 28 +++++++++++++++++-- 8 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 tests/optimizer/minimal-runtime-applyDCEGraphRemovals-output.js create mode 100644 tests/optimizer/minimal-runtime-applyDCEGraphRemovals.js diff --git a/tests/code_size/hello_webgl2_wasm2js.json b/tests/code_size/hello_webgl2_wasm2js.json index 5e710aa7f..74fcba714 100644 --- a/tests/code_size/hello_webgl2_wasm2js.json +++ b/tests/code_size/hello_webgl2_wasm2js.json @@ -2,9 +2,9 @@ "a.html": 588, "a.html.gz": 386, "a.js": 24518, - "a.js.gz": 9119, + "a.js.gz": 9124, "a.mem": 3168, "a.mem.gz": 2711, "total": 28274, - "total_gz": 12216 + "total_gz": 12221 } diff --git a/tests/code_size/hello_webgl_wasm2js.json b/tests/code_size/hello_webgl_wasm2js.json index d81ff1a6c..c3f375970 100644 --- a/tests/code_size/hello_webgl_wasm2js.json +++ b/tests/code_size/hello_webgl_wasm2js.json @@ -2,9 +2,9 @@ "a.html": 588, "a.html.gz": 386, "a.js": 24013, - "a.js.gz": 8955, + "a.js.gz": 8962, "a.mem": 3168, "a.mem.gz": 2711, "total": 27769, - "total_gz": 12052 + "total_gz": 12059 } diff --git a/tests/code_size/hello_world_wasm2js.json b/tests/code_size/hello_world_wasm2js.json index 3561ffcd9..bd68c0c5d 100644 --- a/tests/code_size/hello_world_wasm2js.json +++ b/tests/code_size/hello_world_wasm2js.json @@ -2,9 +2,9 @@ "a.html": 697, "a.html.gz": 437, "a.js": 2026, - "a.js.gz": 911, + "a.js.gz": 913, "a.mem": 6, "a.mem.gz": 32, "total": 2729, - "total_gz": 1380 + "total_gz": 1382 } diff --git a/tests/code_size/random_printf_wasm2js.json b/tests/code_size/random_printf_wasm2js.json index b2091f41f..dd8c27740 100644 --- a/tests/code_size/random_printf_wasm2js.json +++ b/tests/code_size/random_printf_wasm2js.json @@ -1,6 +1,6 @@ { - "a.html": 20054, - "a.html.gz": 8394, - "total": 20054, - "total_gz": 8394 + "a.html": 20051, + "a.html.gz": 8371, + "total": 20051, + "total_gz": 8371 } diff --git a/tests/optimizer/minimal-runtime-applyDCEGraphRemovals-output.js b/tests/optimizer/minimal-runtime-applyDCEGraphRemovals-output.js new file mode 100644 index 000000000..7026cbc5e --- /dev/null +++ b/tests/optimizer/minimal-runtime-applyDCEGraphRemovals-output.js @@ -0,0 +1,22 @@ +var name; + +var asmLibraryArg = { + "save1": 1, + "save2": 2 +}; + +WebAssembly.instantiate(Module["wasm"], imports).then(function(output) { + asm = output.instance.exports; + expD1 = asm["expD1"]; + expD2 = asm["expD2"]; + expD3 = asm["expD3"]; + + initRuntime(asm); + ready(); +}); + +expD1; + +Module["expD2"]; + +asm["expD3"]; diff --git a/tests/optimizer/minimal-runtime-applyDCEGraphRemovals.js b/tests/optimizer/minimal-runtime-applyDCEGraphRemovals.js new file mode 100644 index 000000000..92ff2b08e --- /dev/null +++ b/tests/optimizer/minimal-runtime-applyDCEGraphRemovals.js @@ -0,0 +1,20 @@ +var name; +var asmLibraryArg = { 'save1': 1, 'number': 33, 'name': name, 'func': function() {}, 'save2': 2 }; + +// exports gotten directly in the minimal runtime style +WebAssembly.instantiate(Module["wasm"], imports).then(function(output) { + asm = output.instance.exports; + expD1 = asm['expD1']; + expD2 = asm['expD2']; + expD3 = asm['expD3']; + expD4 = asm['expD4']; + initRuntime(asm); + ready(); +}); + +// add uses for some of them, leave *4 as non-roots +expD1; +Module['expD2']; +asm['expD3']; + +// EXTRA_INFO: { "unused": ["emcc$import$number", "emcc$import$name", "emcc$import$func", "emcc$export$expD4", "emcc$export$expI4"] } diff --git a/tests/test_other.py b/tests/test_other.py index 50abd9a16..d6481e666 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -2171,6 +2171,8 @@ int f() { ['emitDCEGraph', 'noPrint']), (path_from_root('tests', 'optimizer', 'emitDCEGraph5.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph5-output.js')).read(), ['emitDCEGraph', 'noPrint']), + (path_from_root('tests', 'optimizer', 'minimal-runtime-applyDCEGraphRemovals.js'), open(path_from_root('tests', 'optimizer', 'minimal-runtime-applyDCEGraphRemovals-output.js')).read(), + ['applyDCEGraphRemovals']), (path_from_root('tests', 'optimizer', 'applyDCEGraphRemovals.js'), open(path_from_root('tests', 'optimizer', 'applyDCEGraphRemovals-output.js')).read(), ['applyDCEGraphRemovals']), (path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges.js'), open(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges-output.js')).read(), diff --git a/tools/acorn-optimizer.js b/tools/acorn-optimizer.js index 3cd318e25..7cdd1b59e 100644 --- a/tools/acorn-optimizer.js +++ b/tools/acorn-optimizer.js @@ -112,12 +112,19 @@ function nullify(node) { node.raw = 'null'; } -function undefinedify(node) { +// This converts the node into something that terser will ignore in a var +// declaration, that is, it is a way to get rid of initial values. +function convertToNothingInVarInit(node) { node.type = 'Literal'; node.value = undefined; node.raw = 'undefined'; } +function convertToNull(node) { + node.type = 'Identifier'; + node.name = 'null'; +} + function setLiteralValue(item, value) { item.value = value; item.raw = "'" + value + "'"; @@ -851,7 +858,24 @@ function applyDCEGraphRemovals(ast) { var value = node.right; if ((full in unused) && (isAsmUse(value) || !hasSideEffects(value))) { - undefinedify(node); + // This will be in a var init, and we just remove that value. + convertToNothingInVarInit(node); + } + } + } else if (node.type === 'ExpressionStatement') { + var expr = node.expression; + // In the minimal runtime code pattern we have just + // x = asm['x'] + // and never in a var. + if (expr.operator === '=' && + expr.left.type === 'Identifier' && + isAsmUse(expr.right)) { + var name = expr.left.name; + if (name === getAsmOrModuleUseName(expr.right)) { + var full = 'emcc$export$' + name; + if (full in unused) { + emptyOut(node); + } } } }