Skip to content

Cranelift backend miscompiles f128 casts to i8/i16/u8/u16 #1680

Description

@valentynkit

I tried this code:

#![feature(f128)]
fn main() {
    // 300.0 as u8 saturates to 255.
    assert_eq!(std::hint::black_box(300.0f128) as u8, 255);
}

I expected to see this happen: the assert passes, like it does under LLVM.

Instead, this happened: under -Zcodegen-backend=cranelift the cast returns 44 (300 & 0xff), wrapped to the low byte instead of saturated. i8, i16, and u16 are wrong the same way. Wider targets (i32 and up) are correct.

$ rustc +nightly -Zcodegen-backend=cranelift repro.rs && ./repro
assertion `left == right` failed
  left: 44
 right: 255

When cg_clif is built with debug assertions (the verifier is on, as on rust-lang/rust CI) it's a hard error instead of a silent miscompile, and using two narrow widths together makes the shared libcall get declared with two return types and panic. Both are in the backtrace below.

Cause

In the f128 -> int branch of codegen_f16_f128.rs, narrow targets go through ret_ty = I32 and call __fixunstfsi, which returns a 32-bit int (the si suffix), but the libcall's return AbiParam is declared as to_ty:

let ret_ty = if to_ty.bits() < 32 { types::I32 } else { to_ty };
let name = format!("__fix{sign}tf{size}i", /* size from ret_ty */);
let ret = fx.lib_call(&name, vec![AbiParam::new(from_ty)],
                      vec![AbiParam::new(to_ty)], &[from])[0]; // to_ty, should be ret_ty

So ret comes back typed i8/i16 while the saturating clamp below it builds i32 constants, giving the type-mismatched select the verifier rejects (and the wrong value when the verifier is off). Declaring it with ret_ty is enough. f16 -> int is unaffected (it goes via f32), and wide f128 targets are fine (ret_ty == to_ty, no clamp).

Happy to send the one line fix.

Meta

Found by the CodegenCranelift CI job on rust-lang/rust#159954 (adding f128 conversion methods).

rustc --version --verbose:

rustc 1.97.0-nightly (d3cd04068 2026-05-16)
host: aarch64-apple-darwin

Also reproduces on current CI (aarch64-unknown-linux-gnu, 2026-07-26).

Backtrace

# cg_clif with debug assertions (verifier on):
error: VerifierErrors([VerifierError { location: inst22, context: Some("v19 = select.i32 v18, v16, v14  ; v16 = 255"), message: "arg 2 (v14) has type i8, expected i32" }])

# two narrow widths together (f128 as u8 and f128 as u16):
thread 'rustc' panicked at compiler/rustc_codegen_cranelift/src/abi/mod.rs:190:
called `Result::unwrap()` on an `Err` value: IncompatibleSignature("__fixunstfsi",
  Signature { params: [F128], returns: [I8]  ... },
  Signature { params: [F128], returns: [I16] ... })

@rustbot label +A-cranelift +I-miscompile +A-floating-point

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions