Skip to content

Commit df9c9b4

Browse files
committed
Add a rollup plugin for tree shaking Emscripten output.
Rollup has built in tree shaking which can inform the plugin which exports are no longer used. If those exports correspond to wasm exports they can potentially be removed from wasm file too. Overview: While the modules are parsed, we collect info about the exports before any tree shaking occurs to build a map from ESM export name to Wasm export name and info about the local function name within the JS e.g. _foo. During bundle generation rollup provides a list of exports that are removed. We then try to match the removed exports to Wasm exports. However, they all cannot simply be removed though since they may be used internally in the file still. A new list of the used wasm exports is generated and passed to wasm-metadce to remove any unused exports. Note: there are a lot more things we could do there, but this seemed like a good stopping point. It also shows that even for specific output, tree shaking is hard to do soundly for dynamically loaded wasm.
1 parent e26084f commit df9c9b4

File tree

10 files changed

+379
-0
lines changed

10 files changed

+379
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ tools/coverage/
2525
htmlcov/
2626
coverage.xml
2727

28+
/tools/rollup-plugin-emscripten/node_modules
29+
2830
.DS_Store
2931

3032
# Test output

test/rollup_plugin/index.mjs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import init, { _used_externally } from './library.mjs';
2+
3+
await init();
4+
5+
console.log(_used_externally());
6+
console.log('done');

test/rollup_plugin/library.c

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <emscripten.h>
2+
3+
// Should NOT be removed (used by index.mjs).
4+
EMSCRIPTEN_KEEPALIVE int used_externally() {
5+
return 42;
6+
}
7+
8+
// Should NOT be removed (used by library.js).
9+
EMSCRIPTEN_KEEPALIVE int used_internally() {
10+
return 99;
11+
}
12+
13+
// Should be removed.
14+
EMSCRIPTEN_KEEPALIVE int unused() {
15+
return 0xDEAD;
16+
}

test/rollup_plugin/library.js

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
addToLibrary({
3+
$placeHolder__deps: ['used_internally'],
4+
$placeHolder: function() {
5+
_used_internally();
6+
}
7+
});
8+
9+
extraLibraryFuncs.push('$placeHolder');

test/rollup_plugin/package.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "test-rollup-plugin",
3+
"type": "module"
4+
}

test/rollup_plugin/rollup.config.mjs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import emscriptenPlugin from 'rollup-plugin-emscripten';
2+
3+
export default {
4+
input: 'index.mjs',
5+
output: {
6+
dir: 'dist',
7+
format: 'es'
8+
},
9+
plugins: [
10+
emscriptenPlugin({
11+
'input': 'library.mjs'
12+
})
13+
]
14+
};

test/test_other.py

+27
Original file line numberDiff line numberDiff line change
@@ -15804,6 +15804,33 @@ def test_rollup(self):
1580415804
self.run_process(shared.get_npm_cmd('rollup') + ['--config'])
1580515805
self.assertContained('hello, world!', self.run_js('bundle.mjs'))
1580615806

15807+
@parameterized({
15808+
'': ('',),
15809+
'O1': ('-O1',),
15810+
'O2': ('-O2',),
15811+
'O3': ('-O3',),
15812+
})
15813+
def test_rollup_plugin(self, opt):
15814+
def get_exports(path):
15815+
with webassembly.Module(path) as module:
15816+
return [export.name for export in module.get_exports()]
15817+
15818+
copytree(test_file('rollup_plugin'), '.')
15819+
self.run_process([EMCC, 'library.c', opt, '--no-entry', '-sMODULARIZE=instance', '-sENVIRONMENT=node', '--js-library', 'library.js', '-o', 'library.mjs'])
15820+
self.run_process(['npm', 'install', path_from_root('tools/rollup-plugin-emscripten')])
15821+
self.run_process(shared.get_npm_cmd('rollup') + ['--config'])
15822+
exports_pre_rollup = get_exports('library.wasm')
15823+
self.assertContained('42\ndone\n', self.run_js('dist/index.js'))
15824+
exports = get_exports('dist/library.wasm')
15825+
self.assertTrue(len(exports) < len(exports_pre_rollup))
15826+
if opt != '-O3':
15827+
# In O3 wasm export names are minified.
15828+
self.assertTrue('used_externally' in exports)
15829+
self.assertTrue('used_internally' in exports)
15830+
self.assertTrue('unused' not in exports)
15831+
15832+
15833+
1580715834
def test_rlimit(self):
1580815835
self.do_other_test('test_rlimit.c', emcc_args=['-O1'])
1580915836

tools/rollup-plugin-emscripten/package-lock.json

+46
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "rollup-plugin-emscripten",
3+
"version": "0.0.1",
4+
"description": "Rollup plugin for Emscripten output with tree shaking.",
5+
"main": "src/index.mjs",
6+
"dependencies": {
7+
"tmp": "^0.2.3",
8+
"which": "^5.0.0"
9+
}
10+
}

0 commit comments

Comments
 (0)