Stream: git-wasmtime

Topic: wasmtime / issue #11911 Cranelift: Inconsistent execution...


view this post on Zulip Wasmtime GitHub notifications bot (Oct 22 2025 at 14:04):

akldc opened issue #11911:

.clif Test Case

test optimize
set opt_level=none
set preserve_frame_pointers=true
target s390x
target aarch64
target riscv64
target x86_64

function %main() -> i8x16, i8x16, i8x16, i32, i8x16, i8x16 tail {

        const0 = 0x00000000000000000000000000000000
        const1 = 0x7ecbb59efd61c837c76c86aef296bb9f

        block0:
        v8 = vconst.i8x16 const0
        v9 = vconst.i8x16 const1
        v10 = bnot v9  ; v9 = const1
        v11 = bitcast.f64x2 little v10
        v12 = sqrt v11
        v13 = sqrt v12
        v14 = bitcast.i8x16 little v13
        v15 = iconst.i32 0x3693_344b
        v16 = bitcast.i8x16 little v13
        jump block1(v16, v14, v14, v15, v14, v14)  ; v15 = 0x3693_344b
block1(v2: i8x16, v3: i8x16, v4: i8x16, v5: i32, v6: i8x16, v7: i8x16):
        return v2, v3, v4, v5, v6, v7
}

; print: %main()

Description

The execution results of the above Cranelift code differ between x86_64 and aarch64.

[x86    ] %main() -> [0xfff80000000000003e17c443ecb03841, 0xfff80000000000003e17c443ecb03841, 0xfff80000000000003e17c443ecb03841, 915616843, 0xfff80000000000003e17c443ecb03841, 0xfff80000000000003e17c443ecb03841]
[aarch64] %main() -> [0x7ff80000000000003e17c443ecb03841, 0x7ff80000000000003e17c443ecb03841, 0x7ff80000000000003e17c443ecb03841, 915616843, 0x7ff80000000000003e17c443ecb03841, 0x7ff80000000000003e17c443ecb03841]

To verify if the architecture-specific discrepancy stems from Cranelift's handling, I lifted the original .clif code to its equivalent WebAssembly module.

(module
  (type (;0;) (func (result v128 v128 v128 i32 v128 v128)))
  (func (;0;) (type 0) (result v128 v128 v128 i32 v128 v128)
    (local v128)
    v128.const i32x4 0xf296bb9f 0xc76c86ae 0xfd61c837 0x7ecbb59e
    v128.not
    f64x2.sqrt
    f64x2.sqrt
    local.tee 0
    local.get 0
    local.get 0
    i32.const 915616843
    local.get 0
    local.get 0)
  (export "main" (func 0)))

The Wasm module shows similar architecture differences.
x86_64 result:

340240828546070184846820624011963152449
340240828546070184846820624011963152449
340240828546070184846820624011963152449
915616843
340240828546070184846820624011963152449
340240828546070184846820624011963152449

aarch64 result:

170099645085600953115133320296079046721
170099645085600953115133320296079046721
170099645085600953115133320296079046721
915616843
170099645085600953115133320296079046721
170099645085600953115133320296079046721

This is just like the ordinary Wasm programs we usually encounter, and the same code should produce consistent results across different architectures.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 22 2025 at 14:04):

akldc added the bug label to Issue #11911.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 22 2025 at 14:04):

akldc added the cranelift label to Issue #11911.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 22 2025 at 14:16):

bjorn3 commented on issue #11911:

Wasm doesn't specify the NaN payload that is used for float operations that produce a NaN for performance reasons as architectures disagree about when they preserve the input NaN vs when they create a new NaN with a payload specified by the architecture. You can set the enable_nan_canonicalization Cranelift flag if you absolutely need consistent NaN payloads across architectures and don't care about the perf hit.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 22 2025 at 14:47):

alexcrichton closed issue #11911:

.clif Test Case

test optimize
set opt_level=none
set preserve_frame_pointers=true
target s390x
target aarch64
target riscv64
target x86_64

function %main() -> i8x16, i8x16, i8x16, i32, i8x16, i8x16 tail {

        const0 = 0x00000000000000000000000000000000
        const1 = 0x7ecbb59efd61c837c76c86aef296bb9f

        block0:
        v8 = vconst.i8x16 const0
        v9 = vconst.i8x16 const1
        v10 = bnot v9  ; v9 = const1
        v11 = bitcast.f64x2 little v10
        v12 = sqrt v11
        v13 = sqrt v12
        v14 = bitcast.i8x16 little v13
        v15 = iconst.i32 0x3693_344b
        v16 = bitcast.i8x16 little v13
        jump block1(v16, v14, v14, v15, v14, v14)  ; v15 = 0x3693_344b
block1(v2: i8x16, v3: i8x16, v4: i8x16, v5: i32, v6: i8x16, v7: i8x16):
        return v2, v3, v4, v5, v6, v7
}

; print: %main()

Description

The execution results of the above Cranelift code differ between x86_64 and aarch64.

[x86    ] %main() -> [0xfff80000000000003e17c443ecb03841, 0xfff80000000000003e17c443ecb03841, 0xfff80000000000003e17c443ecb03841, 915616843, 0xfff80000000000003e17c443ecb03841, 0xfff80000000000003e17c443ecb03841]
[aarch64] %main() -> [0x7ff80000000000003e17c443ecb03841, 0x7ff80000000000003e17c443ecb03841, 0x7ff80000000000003e17c443ecb03841, 915616843, 0x7ff80000000000003e17c443ecb03841, 0x7ff80000000000003e17c443ecb03841]

To verify if the architecture-specific discrepancy stems from Cranelift's handling, I lifted the original .clif code to its equivalent WebAssembly module.

(module
  (type (;0;) (func (result v128 v128 v128 i32 v128 v128)))
  (func (;0;) (type 0) (result v128 v128 v128 i32 v128 v128)
    (local v128)
    v128.const i32x4 0xf296bb9f 0xc76c86ae 0xfd61c837 0x7ecbb59e
    v128.not
    f64x2.sqrt
    f64x2.sqrt
    local.tee 0
    local.get 0
    local.get 0
    i32.const 915616843
    local.get 0
    local.get 0)
  (export "main" (func 0)))

The Wasm module shows similar architecture differences.
x86_64 result:

340240828546070184846820624011963152449
340240828546070184846820624011963152449
340240828546070184846820624011963152449
915616843
340240828546070184846820624011963152449
340240828546070184846820624011963152449

aarch64 result:

170099645085600953115133320296079046721
170099645085600953115133320296079046721
170099645085600953115133320296079046721
915616843
170099645085600953115133320296079046721
170099645085600953115133320296079046721

This is just like the ordinary Wasm programs we usually encounter, and the same code should produce consistent results across different architectures.

view this post on Zulip Wasmtime GitHub notifications bot (Oct 22 2025 at 14:47):

alexcrichton commented on issue #11911:

Agreed yes, this is NaN issues which is expected for Cranelift to have diverging results on different platforms with. There's some brief information on the topic here and in addition to the Cranelift setting enable_nan_canonicalization you can also for wasm set the -Wnan-canonicalization flag which makes the wasm behave the same across architectures.


Last updated: Dec 06 2025 at 06:05 UTC