- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.9k
Description
If you compile the following Rust code to WASM:
pub unsafe fn cast(x: f64) -> u8 {
    x.to_int_unchecked()
}it compiles to the following with Rust 1.45:
example::cast:
        block           
        local.get       0
        f64.const       0x1p32
        f64.lt  
        local.get       0
        f64.const       0x0p0
        f64.ge  
        i32.and 
        i32.eqz
        br_if           0
        local.get       0
        i32.trunc_f64_u
        return
        end_block
        i32.const       0
        end_functionThe same happens in Rust <1.45 if you use as _. In both cases the following LLVM IR is emitted:
define hidden i8 @_ZN7example4cast17he66904755097d668E(double %x) unnamed_addr #0 !dbg !5 {
  %0 = fptoui double %x to i8, !dbg !9
  ret i8 %0, !dbg !22
}
fptoui is explicitly defined to be UB if an out of range value is provided.
If the value cannot fit in ty2, the result is a poison value.
However it seems like the WASM backend in LLVM ignores that and emits additional bounds checks.
Additionally this gets even worse in Rust 1.45 if you use as _ instead:
example::cast:
        local.get       0
        f64.const       0x1.fep7
        f64.gt  
        local.set       1
        block           
        block           
        local.get       0
        f64.const       0x0p0
        local.get       0
        f64.const       0x0p0
        f64.gt  
        f64.select
        local.tee       0
        f64.const       0x1p32
        f64.lt  
        local.get       0
        f64.const       0x0p0
        f64.ge  
        i32.and 
        i32.eqz
        br_if           0
        local.get       0
        i32.trunc_f64_u
        local.set       2
        br              1
        end_block
        i32.const       0
        local.set       2
        end_block
        i32.const       -1
        local.get       2
        local.get       1
        i32.select
        end_functionThis means that in the end in the WASM VM will do 3 range checks (the one emitted by rust, the one emitted by the llvm backend, and the one it needs to do to possibly trap).
Additionally Rust's saturating checks don't play well with WASM's nontrapping-fptoint target-feature as there's still redundant checks:
example::cast:
        i64.const       9223372036854775807
        local.get       0
        f32.const       -0x1p63
        local.get       0
        f32.const       -0x1p63
        f32.gt  
        f32.select
        i64.trunc_sat_f32_s
        local.get       0
        f32.const       0x1.fffffep62
        f32.gt  
        i64.select
        i64.const       0
        local.get       0
        local.get       0
        f32.eq  
        i64.select
        end_function