Stream: git-wasmtime

Topic: wasmtime / issue #8145 Cranelift: inconsistent NaN canoni...


view this post on Zulip Wasmtime GitHub notifications bot (Mar 15 2024 at 05:11):

candymate added the bug label to Issue #8145.

view this post on Zulip Wasmtime GitHub notifications bot (Mar 15 2024 at 05:11):

candymate added the fuzz-bug label to Issue #8145.

view this post on Zulip Wasmtime GitHub notifications bot (Mar 15 2024 at 05:11):

candymate opened issue #8145:

Test Case

// main.rs
use wasmtime::*;

fn main() -> Result<()> {
    let mut config = Config::default();
    config.strategy(Strategy::Cranelift);
    config.cranelift_opt_level(OptLevel::None);
    config.cranelift_nan_canonicalization(true); // doesn't matter: both options show the same result

    let engine = Engine::new(&config)?;
    let wat = r#"
    (module
        (type (;0;) (func (param i64) (result i32)))
        (import "mem" "mem" (memory (;0;) 1))
        (func (;0;) (type 0) (param i64) (result i32)
          local.get 0
          f64.reinterpret_i64
          f32.demote_f64                ;; <---- only this is needed to trigger the bug!
          i32.reinterpret_f32)
        (export "main" (func 0)))
    "#;
    let module = Module::new(&engine, wat)?;
    let mut store = Store::new(&engine, ());

    let memory_ty = MemoryType::new(1, None);
    let memory = Memory::new(&mut store, memory_ty.clone())?;

    let instance = Instance::new(&mut store, &module, &[memory.into()])?;
    let main = instance.get_func(&mut store, "main")
        .expect("`main` was not an exported function");
    let params = vec![
        Val::I64(0xfffefdfccccdcecfu64 as i64)
    ];
    let mut results = vec![Val::F32(0)];
    println!("Opt level None: {:?}", main.call(
        &mut store,
        &params,
        &mut results
    ));
    println!("{:?}", results);

    Ok(())
}
[package]
name = "wasmtime-wrapper"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# wasmtime = "18.0.3"
wasmtime = { path = "../wasmtime/crates/wasmtime" } # commit: 5b576da4235e3df1bc4385644644157f720e5f21 (current latest, Date: Thu Mar 14 21:34:04 2024 -0500)

Steps to reproduce

Compare the following executions:

cargo run --release
cargo run --release --target=aarch64-unknown-linux-gnu
cargo run --release --target=s390x-unknown-linux-gnu
-----
cargo run --release --target=riscv64gc-unknown-linux-gnu

QEMU run options I'm currently using are the following:

qemu-aarch64 -L /usr/aarch64-linux-gnu -E LD_LIBRARY_PATH=/usr/aarch64-linux-gnu/lib -E WASMTIME_TEST_NO_HOG_MEMORY=1 target/aarch64-unknown-linux-gnu/release/wasmtime-wrapper
qemu-s390x -L /usr/s390x-linux-gnu -E LD_LIBRARY_PATH=/usr/s390x-linux-gnu/lib -E WASMTIME_TEST_NO_HOG_MEMORY=1 target/s390x-unknown-linux-gnu/release/wasmtime-wrapper
qemu-riscv64 -cpu rv64,v=true,vlen=128,vext_spec=v1.0,zba=true,zbb=true,zbs=true,zbc=true,zbkb=true,zcb=true,zicond=true -L /usr/riscv64-linux-gnu -E LD_LIBRARY_PATH=/usr/riscv64-linux-gnu/lib -E WASMTIME_TEST_NO_HOG_MEMORY=1 target/riscv64gc-unknown-linux-gnu/release/wasmtime-wrapper

Expected Results

Results from x64, aarch64, and s390x should be the value 0x7fc00000 (or 0xffc00000) due to NaN canonicalization

Opt level None: Ok(())
[I32(2143289344)]

Actual Results

Execution results in a wrong value -528410, which is 0xfff7efe6

Opt level None: Ok(())
[I32(-528410)]

However, RISC-V64 shows the correct result 0x7fc00000, which is a canonicalized NaN value:

Opt level None: Ok(())
[I32(2143289344)]

Versions and Environment

Extra Info

view this post on Zulip Wasmtime GitHub notifications bot (Mar 15 2024 at 05:18):

candymate commented on issue #8145:

I'm not sure which side contains the problem. (RISC-V vs. all the other architectures). According to the WebAssembly specifications, both results look fine. However, I expected cranelift_nan_canonicalization flag to canonicalize the NaN value computed from f32.demote_f64.

Specifications:

view this post on Zulip Wasmtime GitHub notifications bot (Mar 15 2024 at 15:15):

alexcrichton commented on issue #8145:

Thanks, as always, for the report!

The NaN canonicalization pass in cranelift has an allow-listed set of opcodes which get a canonicalization sequence inserted and demotion is not one of those. I believe the correct answer pops out on risc-v because risc-v doesn't need nan canonicalization as it natively does it, but that explains why the other platforms are in error here.

Looks like we need to add another opcode to that list...

view this post on Zulip Wasmtime GitHub notifications bot (Mar 15 2024 at 16:20):

alexcrichton closed issue #8145:

Test Case

// main.rs
use wasmtime::*;

fn main() -> Result<()> {
    let mut config = Config::default();
    config.strategy(Strategy::Cranelift);
    config.cranelift_opt_level(OptLevel::None);
    config.cranelift_nan_canonicalization(true); // doesn't matter: both options show the same result

    let engine = Engine::new(&config)?;
    let wat = r#"
    (module
        (type (;0;) (func (param i64) (result i32)))
        (import "mem" "mem" (memory (;0;) 1))
        (func (;0;) (type 0) (param i64) (result i32)
          local.get 0
          f64.reinterpret_i64
          f32.demote_f64                ;; <---- only this is needed to trigger the bug!
          i32.reinterpret_f32)
        (export "main" (func 0)))
    "#;
    let module = Module::new(&engine, wat)?;
    let mut store = Store::new(&engine, ());

    let memory_ty = MemoryType::new(1, None);
    let memory = Memory::new(&mut store, memory_ty.clone())?;

    let instance = Instance::new(&mut store, &module, &[memory.into()])?;
    let main = instance.get_func(&mut store, "main")
        .expect("`main` was not an exported function");
    let params = vec![
        Val::I64(0xfffefdfccccdcecfu64 as i64)
    ];
    let mut results = vec![Val::F32(0)];
    println!("Opt level None: {:?}", main.call(
        &mut store,
        &params,
        &mut results
    ));
    println!("{:?}", results);

    Ok(())
}
[package]
name = "wasmtime-wrapper"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
# wasmtime = "18.0.3"
wasmtime = { path = "../wasmtime/crates/wasmtime" } # commit: 5b576da4235e3df1bc4385644644157f720e5f21 (current latest, Date: Thu Mar 14 21:34:04 2024 -0500)

Steps to reproduce

Compare the following executions:

cargo run --release
cargo run --release --target=aarch64-unknown-linux-gnu
cargo run --release --target=s390x-unknown-linux-gnu
-----
cargo run --release --target=riscv64gc-unknown-linux-gnu

QEMU run options I'm currently using are the following:

qemu-aarch64 -L /usr/aarch64-linux-gnu -E LD_LIBRARY_PATH=/usr/aarch64-linux-gnu/lib -E WASMTIME_TEST_NO_HOG_MEMORY=1 target/aarch64-unknown-linux-gnu/release/wasmtime-wrapper
qemu-s390x -L /usr/s390x-linux-gnu -E LD_LIBRARY_PATH=/usr/s390x-linux-gnu/lib -E WASMTIME_TEST_NO_HOG_MEMORY=1 target/s390x-unknown-linux-gnu/release/wasmtime-wrapper
qemu-riscv64 -cpu rv64,v=true,vlen=128,vext_spec=v1.0,zba=true,zbb=true,zbs=true,zbc=true,zbkb=true,zcb=true,zicond=true -L /usr/riscv64-linux-gnu -E LD_LIBRARY_PATH=/usr/riscv64-linux-gnu/lib -E WASMTIME_TEST_NO_HOG_MEMORY=1 target/riscv64gc-unknown-linux-gnu/release/wasmtime-wrapper

Expected Results

Results from x64, aarch64, and s390x should be the value 0x7fc00000 (or 0xffc00000) due to NaN canonicalization

Opt level None: Ok(())
[I32(2143289344)]

Actual Results

Execution results in a wrong value -528410, which is 0xfff7efe6

Opt level None: Ok(())
[I32(-528410)]

However, RISC-V64 shows the correct result 0x7fc00000, which is a canonicalized NaN value:

Opt level None: Ok(())
[I32(2143289344)]

Versions and Environment

Extra Info


Last updated: Oct 23 2024 at 20:03 UTC