trunk
trunk copied to clipboard
Build failure with Rust 1.82.0 and bundled wasm-opt
After upgrading from Rust 1.81 to 1.82 I discovered that a particular wasm/trunk project no longer builds. It always fails with:
[parse exception: invalid code after misc prefix: 17 (at 0:20646)]
Fatal: error parsing wasm (try --debug for more info)
2024-11-04T23:06:47.941140Z ERROR ❌ error
error from build pipeline
Caused by:
0: HTML build pipeline failed (1 errors), showing first
1: error from asset pipeline
2: running wasm-opt
3: wasm-opt call to executable '/home/___/.cache/trunk/wasm-opt-version_116/bin/wasm-opt' with args: '["--output=.../wasm_test_2024_b/target/wasm-opt/release/wasm_test_2024_bg.wasm", "-Oz", ".../wasm_test_2024_b/dist/.stage/wasm_test_2024-616af1a08af380ae_bg.wasm"]' returned a bad status: exit status: 1
2024-11-04T23:06:47.941182Z ERROR error from build pipeline
2024-11-04T23:06:47.941184Z INFO 1: HTML build pipeline failed (1 errors), showing first
2024-11-04T23:06:47.941186Z INFO 2: error from asset pipeline
2024-11-04T23:06:47.941187Z INFO 3: running wasm-opt
2024-11-04T23:06:47.941189Z INFO 4: wasm-opt call to executable '/home/___/.cache/trunk/wasm-opt-version_116/bin/wasm-opt' with args: '["--output=.../wasm_test_2024_b/target/wasm-opt/release/wasm_test_2024_bg.wasm", "-Oz", ".../wasm_test_2024_b/dist/.stage/wasm_test_2024-616af1a08af380ae_bg.wasm"]' returned a bad status: exit status: 1
I built a minimized project that demonstrates the issue here: https://github.com/eric-seppanen/wasm_test_2024
This seems to be a wasm-opt issue, but since wasm-opt is bundled with trunk by default, perhaps it would be a good idea to ship a newer version?
I investigated a few versions of wasm-opt and found that any version >= 117 works, but they all require an additional CLI flag --enable-bulk-memory.
I'm not sure how to make this happen when using trunk.
So perhaps this is related to #866?
It might make sense to update the default versions for the different tools: https://github.com/trunk-rs/trunk/blob/306fa0b72990262333725eabe8fdcc66d17bc14c/src/tools.rs#L98-L105
Also https://github.com/trunk-rs/trunk/pull/901 might help.
After some more experimentation, This problem seems specific to wasm-bindgen/wasm-bindgen-cli 0.2.95 (0.2.93 works; 0.2.94 was yanked but I think it's also broken). But the problem is only triggered by a build with rust 1.82.0.
I don't think it matters what Trunk's default wasm-bindgen version is-- the version of wasm-bindgen-cli has to match the version of the wasm-bindgen library crate. It looks like Trunk does this matching automatically, so the failure appears when my project uses wasm-bindgen >= 0.2.94.
I have the same problem
Finished `release` profile [optimized] target(s) in 6.89s
[parse exception: invalid code after misc prefix: 17 (at 0:564976)] Fatal: error parsing wasm (try --debug for more info) 2024-11-08T02:11:23.262311Z ERROR ❌ error error from build pipeline
rustc 1.82.0 (f6e511eec 2024-10-15) trunk 0.21.4
As you're using trunk 0.21.4, you should be able to use the new data-wasm-opt-param flag. If you find some reasonable settings that "just work", we could add them as a default, as we now also have the version available internally.
Or maybe we need to bump the default version of wasm-opt.
Also note that this will be fixed in the next release of wasm-bindgen, which won't emit table.fill instructions by default when it encounters the wasm metadata output by rustc 1.82.
As you're using trunk 0.21.4, you should be able to use the new
data-wasm-opt-paramflag. If you find some reasonable settings that "just work", we could add them as a default, as we now also have the version available internally.
I had this issue and resolved it for myself with the following:
trunk0.21.4wasm-opt119 via https://github.com/WebAssembly/binaryen/releases/tag/version_119data-wasm-opt-params="--enable-bulk-memory"on the failing component
I admit I do not know what bulk memory or table.fill instructions are, perhaps there are consequences to this that I am unaware of.
Thanks for the summary. That is super helpful. Now that we have internal APIs for the tools in place, would it make sense to auto-enable --enable-bulk-memory in case the wasm-opt version 119 or higher?
Note that rustc doesn't enable bulk memory by default, so to me the safest default would be to track whatever rustc is doing. Enabling bulk memory instructions by default could affect compatibility with existing runtimes.
The reason we're having problems with rust 1.82.0 is because of a bug in wasm-bindgen 0.2.94-0.2.95: wasm-bindgen notices that rustc has enabled new target features, and accidentally enables bulk memory on its output as a side-effect. This will no longer be the case in wasm-bindgen 0.2.96.
What should happen in the long term? A few possibilities I can think of:
- By default enable the most common set of target features when calling
wasm-opt. This is hard to do as this set is changing across rustc versions and there are runtime compatibility hazards in enabling the wrong thing. - Require trunk users to specify a set of
wasm-optflags that match their runtime capabilities and their rustc output. This gives users the power to solve their own problems, but many users won't know what settings to use, which makes the tools harder to use correctly. - Automatically detect what rustc is doing, and try to configure wasm-opt the same way. Ideally wasm-opt would be doing this itself[^1], but if that's not going to happen then this might be a kindness to users who just want things to work without needing a deep dive into webassembly proposals and rustc target-features. Perhaps this is possible by reading the "target-features" section of the wasm output?
Is wasm-opt necessary for trunk to work? Perhaps if wasm-opt is going to need such careful configuration it should be an optional step in the build process (for users that don't want the deep dive).
[^1]: wasm-opt has a deprecated flag --detect-features but I can't find any documentation on whether this functionality ever existed or still exists. Maybe more research is needed here?
Thanks for this thread! It's super helpful.
Is it possible to specify default wasm-opt version through the configuration file? From a quick look at the documentation and code it appears not, but maybe I missed something.
For others, this might be helpful: I managed to get things working by cloning the trunk, then patching trunk/src/tools.rs changing the default version to 119 and adding data-wasm-opt-params="--enable-bulk-memory" as proposed by @isosphere.
One thing I noted is data-wasm-opt-params="--enable-bulk-memory" seems to not work if you've set data-wasm-opt="s".
I guess the answer to all of this is: PRs welcome :wink:
@troelsfr the wasm-opt version is configurable already through Trunk.toml https://github.com/trunk-rs/trunk/blob/4c9d85f6f04a31a272c71db8df12e142c76311fb/Trunk.toml
I'm using:
[tools]
wasm_opt = "version_119"
and this on my html:
<link data-trunk rel="rust" data-bin="some-app-name" data-wasm-opt="z"
data-wasm-opt-params="--enable-bulk-memory" />
Sorry for not reading the docs careful enough! And thanks a lot for pointing me to the right place!
Also note that this will be fixed in the next release of wasm-bindgen, which won't emit
table.fillinstructions by default when it encounters the wasm metadata output by rustc 1.82.
It doesn't seem like this got fixed in the meantime, see our CI run in https://github.com/yewstack/yew/actions/runs/13441890205/job/37558138060. This hits the same issue with
trunk v0.21.7
wasm-bindgen v0.2.100
Is there a way to set this data-wasm-opt-params for all projects in a workspace?
Also note that this will be fixed in the next release of wasm-bindgen, which won't emit table.fill instructions by default when it encounters the wasm metadata output by rustc 1.82.
It doesn't seem like this got fixed in the meantime
That's surprising. I saw the problem go away when updating to wasm-bindgen 0.2.96, and since then we have also updated to 0.2.100 and the problem has not reappeared for us.
I'm not sure what to look for in that github actions link; can you point out what the failure is?
Example error for completeness (doesn't get marked as a build failure in the log for unrelated reasons - the relevant step is "Build examples"):
[parse exception: invalid code after misc prefix: 17 (at 0:104693)]
Fatal: error parsing wasm (try --debug for more info)
2025-02-20T18:18:00.800687Z ERROR ❌ error
error from build pipeline
Caused by:
0: HTML build pipeline failed (1 errors), showing first
1: error from asset pipeline
2: running wasm-opt
3: wasm-opt call to executable '/home/runner/.cache/trunk/wasm-opt-version_116/bin/wasm-opt' with args: '["--output=/home/runner/work/yew/yew/target/wasm-opt/release/webgl_bg.wasm", "-O", "/home/runner/work/yew/yew/dist/webgl/.stage/webgl-1ebcba5a5bedf01f_bg.wasm"]' returned a bad status: exit status: 1
2025-02-20T18:18:00.800710Z ERROR error from build pipeline
2025-02-20T18:18:00.800713Z INFO 1: HTML build pipeline failed (1 errors), showing first
2025-02-20T18:18:00.800716Z INFO 2: error from asset pipeline
2025-02-20T18:18:00.800717Z INFO 3: running wasm-opt
2025-02-20T18:18:00.800718Z INFO 4: wasm-opt call to executable '/home/runner/.cache/trunk/wasm-opt-version_116/bin/wasm-opt' with args: '["--output=/home/runner/work/yew/yew/target/wasm-opt/release/webgl_bg.wasm", "-O", "/home/runner/work/yew/yew/dist/webgl/.stage/webgl-1ebcba5a5bedf01f_bg.wasm"]' returned a bad status: exit status: 1
Maybe this got "revived" by 1.87.0-nightly?
Yeah, looks like LLVM20 turned on bulk memory instructions: https://github.com/rust-lang/rust/issues/137315
Should we consider always bundling the newest wasm_opt?
We can get the latest wasm_opt version using GitHub API by:
curl https://api.github.com/repos/WebAssembly/binaryen/releases/latest | jq -r ".tag_name"
or if we want to do it in Rust (at runtime?):
#[derive(Deserialize)]
struct GitHubRelease {
tag_name: String,
}
pub fn get_latest_wasm_opt_version() -> String {
let url = "https://api.github.com/repos/WebAssembly/binaryen/releases/latest";
let client = reqwest::blocking::Client::new();
// github api requires a user agent
// https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api?apiVersion=2022-11-28#user-agent-required
let req_builder = client.get(url).header("User-Agent", "trunk-wasm-opt-checker");
// Send the request
let res = req_builder
.send()
.expect("Failed to send request to GitHub API");
if !res.status().is_success() {
// Get more details about the error
let status = res.status();
let error_text = res
.text()
.unwrap_or_else(|_| "Could not read error response".to_string());
panic!(
"GitHub API request failed with status: {}. Details: {}",
status, error_text
);
}
let release: GitHubRelease = res.json().expect("Failed to parse GitHub API response");
release.tag_name
}
Does the latest version of wasm_opt fix this issue?
It doesn't seem to fix it for me, or I'm doing something wrong.
I'm using rust nightly 1.88, wasm-opt version_123 and wasm-bindgen 0.2.100.
I can only get it working by disabling the -nontrapping-fptoint feature when compiling and pass --enable-bulk-memory to wasm-opt, which goes against what is said here https://github.com/trunk-rs/trunk/pull/967.
Is what I'm doing the right way to do it?
This is my project:
build script:
RUSTFLAGS="-C target-feature=-nontrapping-fptoint" trunk build --release
index.html:
<link
data-trunk
rel="rust"
data-wasm-opt="4"
data-wasm-opt-params="--enable-bulk-memory"
/>
Cargo.toml:
[profile.release]
codegen-units = 1
lto = true
panic = "abort"
strip = true
.cargo/config.toml:
[unstable]
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]
trim-paths = true
If wasm-opt is going to be full of usability paper cuts, I wonder if trunk should just disable it by default.
It seems like a bad tradeoff to force people to debug a wasm code optimizer before they ever get their first project working.
I'm not using nightly but this works for me even after changing to wasm_opt version_123.
I'm using wasm-bindgen 0.2.95 in case that matters.
Original
Running into this same issue for a simple trunk and yew setup. None of the solutions given help, guess I just won't allow for release mode builds for now?
Update
After some experimenting, it looked like some package I had installed way back in 2020 had installed an ancient version of wasm-opt: 17115192 Apr 28 2020 /usr/local/bin/wasm-opt
$ which wasm-opt
/usr/local/bin/wasm-opt
$ wasm-opt --version
wasm-opt version 93 (version_93)
I wasn't able to determine who/what installed it, so I renamed the bin found here and it correctly found the most recent version from cargo install. This fixed it for me 100%!