bytecodealliance / wasmtime

A fast and secure runtime for WebAssembly
https://wasmtime.dev/
Apache License 2.0
14.81k stars 1.24k forks source link

Cranelift: JIT assertion failure when using `ArgumentPurpose::StructArgument` on macOS (A64) #8852

Open NotAFlyingGoose opened 1 week ago

NotAFlyingGoose commented 1 week ago

.clif Test Case

function %foo(i64 sarg(16)) -> i32 apple_aarch64 {
block0(v0: i64):
    v1 = load.i32 notrap aligned v0
    return v1
}

function %main() -> i32 apple_aarch64 {
    ss0 = explicit_slot 4, align = 16
    sig0 = (i64 sarg(16)) -> i32 apple_aarch64
    fn0 = colocated u0:0 sig0

block0:
    v0 = iconst.i32 42
    stack_store v0, ss0  ; v0 = 42
    v1 = stack_addr.i64 ss0
    v2 = call fn0(v1)
    return v2
}

Steps to Reproduce

I've created a MRE here

The basic issue is caused when declaring a function with AbiParam::special(ptr_ty, ArgumentPurpose::StructArgument(aggr_pointee_size)) as a parameter.

This is not an issue with cranelift_object. This assertion failure specifically happens in cranelift_jit

Expected Results

I expected the above MRE to compile and print the number 42 to the screen. This happens when using x86, cranelift_object, or not using ArgumentPurpose::StructArgument. When using A64, cranelift_jit, and ArgumentPurpose::StructArgument, cranelift panics

Actual Results

When calling module.finalize_definitions(), the MRE reaches an assertion failure

thread 'main' panicked at /Users/goose/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cranelift-jit-0.109.0/src/compiled_blob.rs:90:21:
assertion failed: (diff >> 26 == -1) || (diff >> 26 == 0)
stack backtrace:
   0: rust_begin_unwind
             at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/std/src/panicking.rs:652:5
   1: core::panicking::panic_fmt
             at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/core/src/panicking.rs:72:14
   2: core::panicking::panic
             at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/core/src/panicking.rs:146:5
   3: cranelift_jit::compiled_blob::CompiledBlob::perform_relocations
             at /Users/goose/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cranelift-jit-0.109.0/src/compiled_blob.rs:90:21
   4: cranelift_jit::backend::JITModule::finalize_definitions
             at /Users/goose/.cargo/registry/src/index.crates.io-6f17d22bba15001f/cranelift-jit-0.109.0/src/backend.rs:475:13
   5: cl_aggr_args_mac::main
             at ./src/main.rs:41:5
   6: core::ops::function::FnOnce::call_once
             at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Versions and Environment

Cranelift version or commit: I just updated the MRE to 0.109 this morning and the error is still present

Operating system: macOS 14.3.1

Architecture: Apple M2 (Arm64) It's important to note that this ONLY occurs on Arm64, which makes sense as that's where the assertion failure is. The MRE should work as expected on x86.

Extra Info

The exact line in the MRE to look at is main.rs:152

bjorn3 commented 1 week ago

You may be interested in the topic at https://bytecodealliance.zulipchat.com/#narrow/stream/217117-cranelift/topic/.E2.9C.94.20How.20to.20properly.20use.20ArgumentPurpose.3A.3AStructArgument.3F

tl;dr: StructArgument should not be used on arm64. Also

Just like LLVM, Cranelift only handles the lower half of the calling convention. The upper half which lowers C types is required to be implemented by the frontend. You can find the rust implementations of this for various architectures at https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/abi/call This code is shared by the LLVM, GCC and Cranelift backends of rustc.

bjorn3 commented 1 week ago

Also if you control the calling convention on both sides, I strongly suggest avoiding StructArgument entirely and just passing pointers if you need to pass a struct as argument. StructArgument primarily exists for calling with native C functions that take a struct as argument on architectures where this is done by passing the struct at a fixed stack offset rather than by passing a pointer.

Edit: Seems like you actually want compatibility with native C functions. In that case you do actually need to reimplement the target specific lowerings as found at https://github.com/rust-lang/rust/blob/master/compiler/rustc_target/src/abi/call yourself.

NotAFlyingGoose commented 1 week ago

Thank you! This is very helpful!

In any case, if StructArgument isn't meant to be used on a64 then there should probably be a better error message than an assertion failure. Either that or something else should be done. The code does end up compiling when using cranelift_object, so the panic in cranelift_jit should probably be fixed.

bjorn3 commented 1 week ago

I just noticed that the assertion failure is unrelated to StructArgument. It is the assertion at https://github.com/bytecodealliance/wasmtime/blob/81efaa738419c6e2078e4ad6d14df1e1461d415f/cranelift/jit/src/compiled_blob.rs#L90 I'm not sure when this assertion fires though.