fix: set CFLAGS to disable bulk-memory for wasm32 C deps#409
fix: set CFLAGS to disable bulk-memory for wasm32 C deps#409
Conversation
… bulk-memory LLVM 21+ enables `bulk-memory` and `bulk-memory-opt` by default for wasm32-unknown-unknown, causing C dependencies (e.g., the `ring` crate) to emit `memory.fill`/`memory.copy` instructions. NEAR's VM rejects these with `CompilationError(PrepareError(Deserialization))`. The `-mno-bulk-memory` flag is insufficient because LLVM 21 introduced a separate `bulk-memory-opt` feature that is not disabled by it. `-mcpu=mvp` restricts the C compiler to the MVP instruction set, which is future-proof against further feature splits. Fixes #408
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR updates cargo-near-build’s contract build environment to force C/C++ dependencies compiled during cargo near build to target the WebAssembly MVP instruction set, preventing NEAR-incompatible bulk-memory instructions from being emitted by newer LLVM toolchains.
Changes:
- Set
CFLAGS="-mcpu=mvp"andCXXFLAGS="-mcpu=mvp"for the wasm build invocation. - Add
env_keysconstants forCFLAGSandCXXFLAGS.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| cargo-near-build/src/near/build/mod.rs | Adds default C/C++ compiler flags to the cargo build environment to restrict emitted wasm instructions to MVP. |
| cargo-near-build/src/env_keys.rs | Introduces env var key constants for CFLAGS and CXXFLAGS. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| (env_keys::CFLAGS_ENV, "-mcpu=mvp"), | ||
| (env_keys::CXXFLAGS_ENV, "-mcpu=mvp"), |
There was a problem hiding this comment.
Setting CFLAGS/CXXFLAGS here will override any flags the user already has in their shell environment for the entire cargo build invocation (not just wasm-target C/C++ compilation). This can break builds that rely on additional CFLAGS or that compile any host-side C code during the build. Consider (a) appending -mcpu=mvp to any existing CFLAGS/CXXFLAGS values instead of replacing them, and/or (b) scoping the flags to the wasm32 target only (so host compilation isn’t affected) while still allowing --env to fully override when needed.
| (env_keys::CFLAGS_ENV, "-mcpu=mvp"), | |
| (env_keys::CXXFLAGS_ENV, "-mcpu=mvp"), | |
| ("CFLAGS_wasm32-unknown-unknown", "-mcpu=mvp"), | |
| ("CXXFLAGS_wasm32-unknown-unknown", "-mcpu=mvp"), |
|
This sounds like a risky and just a coincidental remedy rather a proper fix. This may break reproducible builds verification, so be careful with it. |
Use CFLAGS_wasm32_unknown_unknown instead of CFLAGS to avoid affecting host-side C compilation. The cc crate checks target-scoped env vars before the generic CFLAGS, so this only affects wasm32 cross-compilation.
|
That's a fair concern. On the reproducibility side though, I verified that for LLVM 18 (where builds already work), the output is bit-for-bit identical with and without the flag — so this shouldn't change anything for existing setups. For LLVM 21, builds fail entirely without it, so there's no existing reproducibility to break. The flags are also scoped to Happy to hear if you see a better approach though — the core problem is that newer LLVM versions emit bulk-memory instructions from C deps by default, and cargo-near doesn't have a way to prevent that today. |
Use -mno-bulk-memory -mno-bulk-memory-opt instead of -mcpu=mvp to only disable bulk-memory instructions while keeping other post-MVP features (sign-ext, mutable-globals) that NearVM supports since protocol 62.
Edition 2024 implies resolver 3 by default, so no need for an explicit resolver field. Aligns templates with the main project and new-project template which already use edition 2024.
This reverts commit 9918a75.
Summary
CFLAGS_wasm32_unknown_unknown="-mno-bulk-memory -mno-bulk-memory-opt"(and CXXFLAGS) in the build environment to prevent C/C++ dependencies from emitting bulk-memory wasm instructionsProblem
LLVM 21+ enables
bulk-memoryandbulk-memory-optby default forwasm32-unknown-unknown. This causes C dependencies (e.g., theringcrate) to emitmemory.fill/memory.copyinstructions, which NEAR's VM rejects withCompilationError(PrepareError(Deserialization)).Both flags are needed: LLVM 21 split bulk-memory into two separate features, and
-mno-bulk-memoryalone only disables the first —memory.fillstill gets emitted throughbulk-memory-opt. See llvm/llvm-project#109443 for related discussion.Other post-MVP features (sign-ext, mutable-globals) are left enabled as NearVM supports them since protocol 62.
Testing
Verified with the
register-contractfrom defuse-protocol/near-outlayer which depends onring(C crypto code):memory.fillerrorsFixes #408