wasm-pack: wasm-opt: Exported global cannot be mutable

🐛 Bug description

I am using wasm-pack and wasm-bingen to build a javascript package with rust.

Since today when I am running wasm-pack build I am getting following error:

[INFO]: Checking for the Wasm target...
[INFO]: Compiling to Wasm...
    Finished release [optimized + debuginfo] target(s) in 13.59s
[INFO]: Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[wasm-validator error in module] unexpected true: Exported global cannot be mutable, on 
global$0

# --- omitted .wast output ---

Fatal: error in validating input
Error: failed to execute `wasm-opt`: exited with exit code: 1
  full command: "~/.cache/.wasm-pack/wasm-opt-4d7a65327e9363b7/wasm-opt" "$REPO/rav1e_js/pkg/rav1e_js_bg.wasm" "-o" "$REPO/rav1e_js/pkg/rav1e_js_bg.wasm-opt.wasm" "-O"
To disable `wasm-opt`, add `wasm-opt = false` to your package metadata in your `Cargo.toml`.

It was still working on master yesterday (see CI), but doesn’t anymore today with the same commit (see CI). (The important step is “Build”).

🤔 Expected Behavior

Build package and optimize it with wasm-opt as it did before.

(Running wasm-pack build --dev works, but I’d like to have the optimization).

👟 Steps to reproduce

Trying a few things I figured out that I can make wasm-opt pass when I am removing all exported (non-static) methods on the exported structs (which is abviously not desireable).

On urhengulas/rav1e@wasm-opt-fix I removed all the methods and it builds and optimizes fine:

➜  rav1e git:(wasm-opt-fix) ✗ cd rav1e_js && wasm-pack build
[INFO]: Checking for the Wasm target...
[INFO]: Compiling to Wasm...
    Finished release [optimized + debuginfo] target(s) in 0.05s
[INFO]: License key is set in Cargo.toml but no LICENSE file(s) were found; Please add the LICENSE file(s) to your project directory
[INFO]: Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: :-) Done in 0.88s
[INFO]: :-) Your wasm pkg is ready to publish at /home/urhengulas/Documents/github.com/xiph/rav1e/rav1e_js/pkg.

But, as soon I am adding a method (e.g. fn Frame.debug(&self) -> Self in rav1e_js/src/frame.rs) back, I am getting the error from the bug description.

🌍 Your environment

Include the relevant details of your environment.

➜  rustc --version
rustc 1.45.0 (5c1f21c3b 2020-07-13)
➜  wasm-pack --version
wasm-pack 0.9.1
➜  ~/.cache/.wasm-pack/wasm-opt-4d7a65327e9363b7/wasm-opt --version
wasm-opt version_90

Thx

Thanks to everyone taking the time and having a look at this issue!

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 57
  • Comments: 32 (8 by maintainers)

Commits related to this issue

Most upvoted comments

@Urhengulas It’s possible to specify wasm-opt arguments, so you should be able to do this:

[package.metadata.wasm-pack.profile.release]
wasm-opt = ["-Oz", "--enable-mutable-globals"]

Change -Oz to whatever optimization level you need.

Ran into the same issue while doing the wasm game of life tutorial: https://rustwasm.github.io/docs/book/game-of-life/implementing.html

I don’t have much context here on what exactly the problem is but I will say that whatever changed broke pretty much every standard Hello World project in the Rust-Wasm ecosystem and that doesn’t feel great. Wherever this change was introduced needs to have some sort of integration test with some common tutorials so that releases don’t break documentation across the entire ecosystem.

If a breaking change must happen (which I’d argue it probably doesn’t) then there should be advance notice, some warning messages in terminal output before a breaking change (similar to how Rust itself deprecates things), and work put in to update documentation across the ecosystem that does not break with default settings.

edit: i think we might want to revisit whether wasm-pack should even run wasm-opt by default instead of having it be opt in until it has stabilized - these breaking changes are not fun to work with.

https://github.com/rustwasm/wasm-bindgen/pull/2391 has been merged & released in wasm-bindgen 0.2.70. I believe this fixes the issue, and this ticket can now be closed.

The problem seems to be with the non-MVP webassembly feature mutable-global. Adding --enable-mutable-globals to the wasm-opt command seems to fix it (see https://github.com/WebAssembly/binaryen/issues/3006#issuecomment-667125012).

My workaround is now:

  1. disable wasm-opt in your Cargo.toml
[package.metadata.wasm-pack.profile.release]
wasm-opt = false
  1. … run wasm-pack
$ wasm-pack build
  1. … and optimize manually
$ /path/to/wasm-opt pkg/repo_bg.wasm -o pkg/repo_bg.wasm -O --enable-mutable-globals

(My /path/to/wasm-opt is ~/.cache/.wasm-pack/wasm-opt-4d7a65327e9363b7/wasm-opt.)


Edit: Originally I recommended to use wasm-opt [...] -O -all, but this creates new errors.

This issue may be fixed by https://github.com/rustwasm/wasm-bindgen/pull/2391 , which prevents wasm-bindgen from exporting mutable globals for compatibility reasons.

Thanks for the input @EverlastingBugstopper, @vtavernier.


I’ve created a bit more atomic example:

// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct Boat {
    length: u32,
}

#[wasm_bindgen]
impl Boat {
    // works
    pub fn new(length: u32) -> Self {
        Self { length }
    }

    // doesn't work
    pub fn float(&self) -> String {
        String::from("floating... ⛵")
    }

    // works
    pub fn volume(&self) -> u32 {
        self.length * 2
    }
}

// doesn't work
#[wasm_bindgen]
pub fn string() -> String {
    String::from("wasm-opt")
}

So it seems returning a String fails wasm-opt.

It is also failing, when returning some structs, but for others not. I will investigate what the failing ones have in common.

Fixed my issue by doing this: wasm-bindgen = "=0.2.60"

Other deps I use that depend on wasm-bindgen also must be downgraded, but they don’t need the = in their versions.

Fixed my issue by doing this: wasm-bindgen = “=0.2.60”

~wasm-bindgen = "=0.2.65" is sufficient as per @vtavernier’s comment above. I am worried the compiler can emit exported mutable globals by default, so this might actually be an upstream rustc issue, not sure.~

It works for compiling to WebAssembly, but causes an issue for wasm2js.

saw this same issue the other day in some e2e tests

Thanks for that, @lukaslueg. I actually didn’t realized that exported mutable globals are now supported everywhere, so @0xd4d’s answer is actually the correct one and there’s no need to downgrade wasm-bindgen.

target-features is used by the toolchain to specific the feature set that the output is expecting. If llvm’s output includes the use of given feature that feature should appear in the target-features section. If it doesn’t, that is a bug IIUC.

If you want to transpile those features away in order to support older runtimes that seems fine too.

In this case it seems clear that this is the problem: llvm is producing code that depends a feature but that information not being correctly transmitted to wasm-opt.

For me the bug was introduced by wasm-bindgen 0.2.66 (0.2.65 is fine).

For me this issue showed up in CI. The last build that was correct before the failed one today was on July 21st. The nightly compiler from that version already exhibited the bug so it seems the cause is some dependency somewhere triggering this behavior which is not supported by wasm-opt, but I haven’t been able to trace which one yet.