wasmerio / wasmer

🚀 The leading Wasm Runtime supporting WASIX and WASI
https://wasmer.io
MIT License
19.09k stars 819 forks source link

llvm: Incorrect result for extmul instructions #4568

Open candymate opened 7 months ago

candymate commented 7 months ago

Describe the bug

Wasmer with llvm compiler gives wrong execution result for extmul instructions. The PoC code is as the following:

use wasmer::sys::{EngineBuilder, Features};
use wasmer::{imports, Cranelift, CraneliftOptLevel, Instance, Memory, MemoryType, Module, Store, CompilerConfig};
use wasmer_compiler_llvm::{LLVM, LLVMOptLevel};
use anyhow::Error;

fn main() -> Result<(), Error> {
    let diff = 0; // CHANGE HERE!!! (0 and 1)

    let mut features = Features::new();
    features.multi_value(true);
    features.simd(true);
    features.threads(true);

    let engine = if diff == 0 {
        let mut compiler = Cranelift::default();
        compiler.canonicalize_nans(true);
        compiler.opt_level(CraneliftOptLevel::None);

        let engine = EngineBuilder::new(compiler).set_features(Some(features));
        engine
    } else {
        let mut compiler = LLVM::default();
        compiler.canonicalize_nans(true);
        compiler.opt_level(LLVMOptLevel::None);

        let engine = EngineBuilder::new(compiler).set_features(Some(features));
        engine
    };

    let module_wat = r#"
    (module
        (type (;0;) (func (result v128)))
        (import "mem" "mem" (memory (;0;) 1))
        (func (;0;) (type 0)
          v128.const i32x4 0x40404040 0x40404040 0x40404040 0x40404040
          v128.const i32x4 0x00010203 0x807f7e7d 0xcccdcecf 0xfffefdfc
          i16x8.extmul_low_i8x16_s)
        (export "main" (func 0)))
    "#;

    let mut store = Store::new(engine);
    let module = Module::new(&store, &module_wat)?;

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

    let imports = imports!{
        "mem" => {
            "mem" => memory.clone()
        }
    };

    let instance = Instance::new(&mut store, &module, &imports)?;
    let main = instance.exports.get_function("main")
        .expect("`main` was not an exported function");
    let result = main.call(&mut store, vec![].as_slice());

    println!("{:?}", result);

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

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

[dependencies]
wasmer = { version = "4.2.8", features = ["cranelift", "singlepass", "compiler"] }
wasmer-compiler-llvm = "4.2.8"
anyhow = "1.0"

Steps to reproduce

You can change the value of the variable diff on line 7. (with 0 and 1) It will choose the compiler between Cranelift and llvm.

cargo run --release

Expected behavior

The output should be the one below as Cranelift gives. (0xe0001fc0_1f801f40_00000040_008000c0) This value is checked to be correct with the WebAssembly interpreter.

Ok([V128(297747715032074996854313701444155343040)])

Actual behavior

Execution with the llvm compiler gives the following result: (0xff80ff00_f340f3c0_1fc01f40_004000c0)

Ok([V128(339622925012778316421041060864476577984)])

Additional context

xdoardo commented 1 month ago

Planned!