-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Failed to build a Wasm component from the Wasm core module with "non-component" imports #334
Comments
@bitwalker That's a blocker for me. Please check it out ASAP. |
I'm not sure I understand why non-component imports are being treated as component imports here, though I haven't dug into it just yet. The entire premise of not making much/all of the Miden SDK a component, was so that the code from the SDK would be considered part of the component being defined (i.e. the one importing the SDK as a utility), or part of the host at the very least. It sounds like If so, one option would be to provide to
We can't rely on #[inline(never)]
#[no_mangle]
pub extern "C-unwind" fn foo(...) {
core::hint::black_box(unreachable!())
} This ensures that callers treat calls to Even with those tweaks, I think we are still forced to assume that nothing is guaranteed with regards to inlining. The major problem with having no guarantees of course, is that even if we do know when it happens (and it isn't happening on some code path that is rarely called, and thus not encountered until an inopportune time), there isn't anything we can actually do to prevent it. I really don't want that kind of uncertainty. So the way I see it is we have few possible options:
We should probably have a call to discuss this tomorrow, ping me via IM if you have a particular time that works best, but I'll assume roughly sometime around our usual compiler standup time. We can try and hash through the gritty details together. |
@greenhat Sorry, I just realized I forgot to check, but are you building the Rust crate with I suppose the problem might be that we need WIT in the loop, and AFAIK there is no support for expressing component definitions via If I can find time tonight, I'll try to do some experimenting with all of this. |
Yes, that's my understanding. All unresolved imports are treated as component imports.
I'll keep digging into
I agree. It's fragile and ugly. I've given up on imports too early. I'll keep digging.
I thought about the third option, and it's my least favorite. The second option is feasible, but would require an effort to maintain a fork of
Sure, the usual time works for me. |
No, I'm using |
@bitwalker Be aware, that the relevant parts of |
I did some experimenting, and it seems that, at least at the moment, the following code does not inline the call to #[no_mangle]
pub fn run(num: i32) -> i32 {
foo(core::ptr::null(), num as usize);
1
}
#[inline(never)]
#[no_mangle]
pub extern "C-unwind" fn foo(arg1: *const (), len: usize) {
core::hint::black_box(());
} The resulting code seems to be compiled as if run:
push rax
call qword ptr [rip + foo@GOTPCREL]
mov eax, 1
pop rcx
ret
foo:
ret However, there is a caveat, which I'll get to in a moment. Approaches that just don't work correctly at all:
So the bottom line appears to be that LLVM will still optimize based on the definition, even if it is not inlined. Inlining would result in some pretty aggressive optimizations, but as we can see above, even without inlining, some attributes of the function can still result in radical assumptions about calls to it. In particular:
So IMO, it is just simply not an option to define stubs in Rust that we then replace. The program will be optimized around the stub implementation, regardless of what we do, and this will result in unsoundness if we then swap out that implementation with a new one that has different behavior. We might be able to come up with a stub that is sufficiently pessimized that it works, but slight changes to how Rust or LLVM compiles the code could cause it to break at any time. As discussed on our call, coming at this from the angle of finding a way to represent these functions in a component interface seems like our best bet for now, these findings just solidify that as our path forward IMO. I'll follow up if I come up with any other options though. |
@bitwalker That's a comprehensive answer! I agree, stubs are a dead end. I dug into the Wasm CM repo and found a discussion which addresses our case of a Wasm component with unsatisfied imports in the core module - WebAssembly/component-model#275 |
TLDR; Using core Wasm module imports for our intrinsics and Miden SDK functions is not playing well with the Wasm component model.
What
When building a Wasm component from a Wasm core module compiled from the
basic-wallet
with Rust SDK example, the build fails with the following error:Why
The reason for this error is that the Wasm core module contains imports that
wit-component
(used bycargo-component
to build the Wasm component) fails to recognize as component imports, i.e. imports that are defined in the WIT file. In this case, it'smiden::account::add_asset
tx kernel function from the Miden SDK. Effectively, all our intrinsics and Miden SDK functions are triggering this error.This happens at https://github.com/bytecodealliance/wasm-tools/blob/51ade657f6d6874b1bdfb87a47299dbe7e61d5c2/crates/wit-component/src/validation.rs#L411-L491, where the module name of the core module import is expected to be found in the interface imports in the WIT file.
How to reproduce
In #329 branch run:
RUST_LOG=debug cargo test rust_sdk_basic
To check out the Wasm core module, use
wasm-tools print PATH_TO_CORE_WASM
with the path reported in logs atcomponentizing WebAssembly module ...
How to fix
The most promising approach IMO would be to not use Wasm core module imports for our intrinsics and Miden SDK functions. The reason why
wit-component
treats them as errors is fair, and I think it'd be difficult if at all possible to alter that behavior in the upstream.So instead of defining our Miden SDK functions and intrinsics as
extern
compiler/sdk/base-sys/src/bindings/tx/externs.rs
Lines 5 to 13 in a876d8b
we could define them as functions with
unreachable!()
bodies and recognize them by function names and signatures in the frontend:Although,
#[inline(never)]
is only a hint to the compiler, with a side effect in the body we should be safe from inlining.The text was updated successfully, but these errors were encountered: