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
f64 return value mismatch #183
Comments
The RefCell is not necessary, this results in a much smaller file: fn main() {
let binary = [
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x60, 0x00, 0x00, 0x60,
0x00, 0x01, 0x7C, 0x03, 0x03, 0x02, 0x00, 0x01, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, 0x01,
0x05, 0x03, 0x01, 0x00, 0x11, 0x06, 0x19, 0x03, 0x7F, 0x01, 0x41, 0x80, 0x80, 0xC0, 0x00,
0x0B, 0x7F, 0x00, 0x41, 0x90, 0x80, 0xC0, 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x90, 0x80, 0xC0,
0x00, 0x0B, 0x07, 0x4D, 0x05, 0x06, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x02, 0x00, 0x19,
0x5F, 0x5F, 0x69, 0x6E, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5F, 0x66, 0x75, 0x6E, 0x63,
0x74, 0x69, 0x6F, 0x6E, 0x5F, 0x74, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x0B, 0x5F, 0x5F,
0x68, 0x65, 0x61, 0x70, 0x5F, 0x62, 0x61, 0x73, 0x65, 0x03, 0x01, 0x0A, 0x5F, 0x5F, 0x64,
0x61, 0x74, 0x61, 0x5F, 0x65, 0x6E, 0x64, 0x03, 0x02, 0x09, 0x67, 0x61, 0x6D, 0x65, 0x5F,
0x74, 0x69, 0x6D, 0x65, 0x00, 0x01, 0x0A, 0x42, 0x02, 0x02, 0x00, 0x0B, 0x3D, 0x00, 0x02,
0x40, 0x41, 0x00, 0x29, 0x03, 0x80, 0x80, 0x40, 0x42, 0x01, 0x52, 0x0D, 0x00, 0x41, 0x00,
0x2B, 0x03, 0x88, 0x80, 0x40, 0x0F, 0x0B, 0x41, 0x00, 0x42, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0xD0, 0xB7, 0xC0, 0x00, 0x37, 0x03, 0x88, 0x80, 0x40, 0x41, 0x00, 0x42, 0x01, 0x37,
0x03, 0x80, 0x80, 0x40, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x6F, 0x40, 0x0B, 0x0B,
0x19, 0x01, 0x00, 0x41, 0x80, 0x80, 0xC0, 0x00, 0x0B, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, 0x04, 0x6E,
0x61, 0x6D, 0x65, 0x01, 0x1F, 0x02, 0x00, 0x11, 0x5F, 0x5F, 0x77, 0x61, 0x73, 0x6D, 0x5F,
0x63, 0x61, 0x6C, 0x6C, 0x5F, 0x63, 0x74, 0x6F, 0x72, 0x73, 0x01, 0x09, 0x67, 0x61, 0x6D,
0x65, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x02, 0x05, 0x02, 0x00, 0x00, 0x01, 0x00,
];
let import_object = wasmer_runtime::imports! {};
let instance = wasmer_runtime::instantiate(&binary, &import_object).unwrap();
if let Ok(func) = instance.func::<(), f64>("game_time") {
let ret_val = func.call().unwrap();
println!("{} {:#016x}", ret_val, ret_val.to_bits());
}
} WAST: (module
(type $t0 (func))
(type $t1 (func (result f64)))
(func $__wasm_call_ctors (type $t0))
(func $game_time (type $t1) (result f64)
block $B0
i32.const 0
i64.load offset=1048576
i64.const 1
i64.ne
br_if $B0
i32.const 0
f64.load offset=1048584
return
end
i32.const 0
i64.const 4643000109586448384
i64.store offset=1048584
i32.const 0
i64.const 1
i64.store offset=1048576
f64.const 0x1.f4p+7 (;=250;))
(table $__indirect_function_table 1 1 anyfunc)
(memory $memory 17)
(global $g0 (mut i32) (i32.const 1048576))
(global $__heap_base i32 (i32.const 1048592))
(global $__data_end i32 (i32.const 1048592))
(export "memory" (memory 0))
(export "__indirect_function_table" (table 0))
(export "__heap_base" (global 1))
(export "__data_end" (global 2))
(export "game_time" (func $game_time))
(data (i32.const 1048576) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00")) Source: use std::cell::RefCell;
struct Context {
val: f64,
}
fn build_context() -> Context {
Context {
val: 250.0,
}
}
thread_local! {
static CONTEXT: Context = build_context();
}
#[no_mangle]
pub extern "C" fn game_time() -> f64 {
CONTEXT.with(|ctx| {
ctx.val
})
} |
It seems like global variables don't work at all actually (if they are sufficiently uninlinable) as shown here: (module
(type $t0 (func))
(type $t1 (func (result f64)))
(func $__wasm_call_ctors (type $t0))
(func $mutate (type $t0)
i32.const 0
i64.const 4636737291354636288
i64.store offset=1048576)
(func $game_time (type $t1) (result f64)
i32.const 0
f64.load offset=1048576)
(table $__indirect_function_table 1 1 anyfunc)
(memory $memory 17)
(global $g0 (mut i32) (i32.const 1048576))
(global $__heap_base i32 (i32.const 1048584))
(global $__data_end i32 (i32.const 1048584))
(export "memory" (memory 0))
(export "__indirect_function_table" (table 0))
(export "__heap_base" (global 1))
(export "__data_end" (global 2))
(export "mutate" (func $mutate))
(export "game_time" (func $game_time))
(data (i32.const 1048576) "\00\00\00\00\00@o@")) Neither calling mutate nor skipping it produce the correct value, suggesting that it is indeed an ABI problem, because even if it would store / load from an incorrect address, at least |
Thanks for the super detailed examples, we'll take a look on it soon! |
Ah, I figured out the problem. It's the assumption that c structs with a single element have the same ABI as the single element itself, but that isn't always the case. So the S1<f64> WasmExternType impl destructures the S1 by derefencing from a pointer, storing the value in XMM0. However the JIT'ed code already has the f64 residing in the XMM0 register, which now gets overwritten by garbage: |
@CryZe Looks like you're using visual studio to debug Rust! I personally have not got this to work correctly. This is unrelated to the issue, but could you point me to resources on how you got visual studio configured for rust? How you're attaching the debugger? In addition to learning how to do that, I want to reproduce and visual the problem the same way you have. |
I did not really set up much. It works straight out of the box. You just need to make sure you use the msvc target and then you open the actual .exe file as a project (not as a file) in Visual Studio. After the "project" is opened you want to open the main.rs and put a breakpoint in there. Then you can start the project and it should hit the breakpoint. From there I added the disassembly and register view (which you can find under debugging -> views (or something, mine is german)). And then I right clicked the register view to show the SSE registers. |
I actually usually use the Visual Studio Debugger in VS Code where I also have direct access to all my source files and stuff. However for this, VS Code's debugger is missing the disassembly and register view, which was necessary here. So I switched to Visual Studio instead. So since this is such a temporary occurance I just manually stepped through the calls which auto opened the files, and didn't bother with properly setting up any solution or anything. But I guess you could theoretically add the folders to the solution and then save it as a proper solution? I really don't know though. |
I also was able to replicate this on Godbolt right now, where #[repr(C)] struct F(f64) and f64 behave differently when targeting windows (not linux though): https://rust.godbolt.org/z/pNibCT |
So I tweaked the macro so I can specify the repr per S* type and gave the S1 a repr(transparent) and now everything works. Would that be a sufficient solution that I could PR (or maybe you guys do the change real quick) or is there a better solution? |
The ABI of aggregates such as single element structs is not required to be the same as the single elements themselves. This is especially true for f64 vs. #[repr(c)] struct F(f64); on Windows. Therefore the macro has been tweaked so S1 uses repr(transparent) which is made for exactly for this use case. Closes wasmerio#183
That seems like a good solution to me! Make a pr! |
The ABI of aggregates such as single element structs is not required to be the same as the single elements themselves. This is especially true for f64 vs. #[repr(c)] struct F(f64); on Windows. Therefore the macro has been tweaked so S1 uses repr(transparent) which is made for exactly for this use case. Closes #183
feat(cache) `blake3` is no longer optional
Okay, so I've been able to test out wasmer on Windows now and for the most part it seems to work fine, except the occasional segfaults (I have yet to figure out when they happen) and the issue this thread is about: I have an exported function that returns 250.0 f64 value, but the host sees a garbage value instead:
The game_time function as WAST looks like this:
This is the original source of the wasm file:
It does not break if I return 250.0 as a constant instead. So the thread_local RefCell is important (maybe just a static or thread_local would suffice, not sure yet).
This may or may not be broken on Linux and macOS too.
The text was updated successfully, but these errors were encountered: