Rust-GPU / Rust-GPU/rust-cuda

WarpMatchValue for f32/f64 uses truncating casts instead of to_bits()

Open Beginner friendly
#406 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
5.4k
Forks
249
PR merge metrics
No merged PRs in 30d

Description

f32 and f64 types are mistakenly implemented using the generic impl_match! macro which uses the as keyword to cast values to unsigned integers, rather than properly preserving their bit representation for PTX comparison.

This presents the following issues:

  1. Code that uses warp_match_any on f32 or f64 intends to check if threads in a warp possess the exact same floating-point value. However, the as u32 cast on an f32 in Rust executes a truncating numeric conversion. For example, 1.1f32 and 1.9f32 are both cast to 1u32.
  2. Because different fractional values (and all negative floats, which inherently cast to 0) produce identical integer masks under this truncation, the underlying PTX intrinsic will incorrectly report that these threads share matching values. Algorithms relying on this for SIMT execution branch convergence or duplicate filtering will proceed with corrupted state without emitting any compilation errors or runtime warnings.

Reproduction Case

Using warp_match_any on distinct f32 or f64 floating-point numbers will erroneously return that the threads have matching values natively:

use cuda_std::warp::warp_match_any;

#[cuda_std::kernel]
pub unsafe fn match_float_kernel() {
    use cuda_std::thread;
    let tid = thread::thread_idx_x();
    
    // Thread 0 has 1.1, Thread 1 has 1.9, Thread 2 has -5.5
    let val: f32 = if tid == 0 { 1.1 } else if tid == 1 { 1.9 } else { -5.5 };
    
    // PTX generates match.any on the integer cast of val.
    // 1.1 as u32 == 1, 1.9 as u32 == 1, and -5.5 as u32 == 0! 
    // This incorrectly evaluates Threads 0 and 1 as a perfect match.
    let matching_mask = unsafe { warp_match_any(0xFFFFFFFF, val) };
}

Issue Details

In crates/cuda_std/src/warp.rs, the type instantiations for WarpMatchValue generically apply value as [<u $width>] inside the impl_match! macro. While this successfully behaves as a bitwise cast for integers like i32 and i64, it is invalid for floats which must explicitly use to_bits():

// Current macro definition in warp.rs
macro_rules! impl_match {
    ($($type:ty, $width:literal),* $(,)?) => {
        $(
            paste::paste! {
                impl WarpMatchValue for $type {
                    unsafe fn match_any(mask: u32, value: Self) -> u32 {
                        // <--- BUG: `f32 as u32` evaluates as truncating numeric conversion (`1.9 -> 1`)
                        unsafe { [<match_any_ $width>](mask, value as [<u $width>]) } 
                    }
                    // ...
                }
            }
        )*
    }
}

// Current instantiations using the macro
impl_match! {
    i32, 32,
    i64, 64,
    u32, 32,
    u64, 64,
    f32, 32, // <--- BUG: Floats should not be dynamically handled via integer `as` casting
    f64, 64, // <--- BUG: Floats should not be dynamically handled via integer `as` casting
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in crates/cuda_std/src/warp.rs by reading the WarpMatchValue implementations and the impl_match! macro, then compare the f32 and f64 instantiations with the integer cases. Done means floating-point warp matching preserves each value's bit representation rather than performing numeric truncation, while integer behavior remains unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.