[llvm] [NVPTX] Fix fptosi/fptoui to i1. (PR #200718)

via llvm-commits llvm-commits at lists.llvm.org
Sun May 31 22:33:34 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-nvptx

Author: Justin Lebar (jlebar)

<details>
<summary>Changes</summary>

The langref says:

> The ‘fptosi’ instruction converts its floating-point operand into the
> nearest (rounding towards zero) signed integer value. If the value
> cannot fit in ty2, the result is a poison value.

Previously `fptosi to i1` and `fptoui to i1` were lowered as `x == 0.0`,
which is clearly incorrect.

A valid lowering for `fptosi to i1` is `and (cvt.rzi.s32.fNN x), 1`, i.e.

 - convert float to s32 with "round-to-nearest-integer in the direction
   of zero" mode, then
 - take the low-order bit.

Same for `fptoui to i1` except we convert to u32.

This works because the only results we care about are
 - rounding to 0 and -1 (for the signed case) and,
 - rounding to 0 and 1 (for the unsigned case)

and these give us the i1 values of 0 and 1 as expected.  (Other rounded
values are poison so we can return any value.)


---
Full diff: https://github.com/llvm/llvm-project/pull/200718.diff


3 Files Affected:

- (modified) llvm/lib/Target/NVPTX/NVPTXInstrInfo.td (+8-8) 
- (added) llvm/test/CodeGen/NVPTX/convert-fp-i1-narrow.ll (+14) 
- (modified) llvm/test/CodeGen/NVPTX/convert-fp.ll (+26) 


``````````diff
diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
index 982d664e694f8..8ffd1e00fbf0d 100644
--- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
+++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
@@ -2106,25 +2106,25 @@ def : Pat<(f64 (uint_to_fp i64:$a)), (CVT_f64_u64 $a, CvtRN)>;
 
 
 // f16 -> sint
-def : Pat<(i1  (fp_to_sint f16:$a)), (SETP_i16ri $a, 0, CmpEQ)>;
+def : Pat<(i1  (fp_to_sint f16:$a)), (SETP_i32ri (AND_b32ri (CVT_s32_f16 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_sint f16:$a)), (CVT_s16_f16 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_sint f16:$a)), (CVT_s32_f16 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_sint f16:$a)), (CVT_s64_f16 $a, CvtRZI)>;
 
 // f16 -> uint
-def : Pat<(i1  (fp_to_uint f16:$a)), (SETP_i16ri $a, 0, CmpEQ)>;
+def : Pat<(i1  (fp_to_uint f16:$a)), (SETP_i32ri (AND_b32ri (CVT_u32_f16 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_uint f16:$a)), (CVT_u16_f16 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_uint f16:$a)), (CVT_u32_f16 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_uint f16:$a)), (CVT_u64_f16 $a, CvtRZI)>;
 
 // bf16 -> sint
-def : Pat<(i1  (fp_to_sint bf16:$a)), (SETP_i16ri $a, 0, CmpEQ)>;
+def : Pat<(i1  (fp_to_sint bf16:$a)), (SETP_i32ri (AND_b32ri (CVT_s32_bf16 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_sint bf16:$a)), (CVT_s16_bf16 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_sint bf16:$a)), (CVT_s32_bf16 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_sint bf16:$a)), (CVT_s64_bf16 $a, CvtRZI)>;
 
 // bf16 -> uint
-def : Pat<(i1 (fp_to_uint bf16:$a)),  (SETP_i16ri $a, 0, CmpEQ)>;
+def : Pat<(i1 (fp_to_uint bf16:$a)),  (SETP_i32ri (AND_b32ri (CVT_u32_bf16 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_uint bf16:$a)), (CVT_u16_bf16 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_uint bf16:$a)), (CVT_u32_bf16 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_uint bf16:$a)), (CVT_u64_bf16 $a, CvtRZI)>;
@@ -2134,7 +2134,7 @@ let Predicates = [doF32FTZ] in {
   def : Pat<(i32 (fp_to_sint f32:$a)), (CVT_s32_f32 $a, CvtRZI_FTZ)>;
   def : Pat<(i64 (fp_to_sint f32:$a)), (CVT_s64_f32 $a, CvtRZI_FTZ)>;
 }
-def : Pat<(i1  (fp_to_sint f32:$a)), (SETP_i32ri $a, 0, CmpEQ)>;
+def : Pat<(i1  (fp_to_sint f32:$a)), (SETP_i32ri (AND_b32ri (CVT_s32_f32 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_sint f32:$a)), (CVT_s16_f32 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_sint f32:$a)), (CVT_s32_f32 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_sint f32:$a)), (CVT_s64_f32 $a, CvtRZI)>;
@@ -2145,19 +2145,19 @@ let Predicates = [doF32FTZ] in {
   def : Pat<(i32 (fp_to_uint f32:$a)), (CVT_u32_f32 $a, CvtRZI_FTZ)>;
   def : Pat<(i64 (fp_to_uint f32:$a)), (CVT_u64_f32 $a, CvtRZI_FTZ)>;
 }
-def : Pat<(i1  (fp_to_uint f32:$a)), (SETP_i32ri $a, 0, CmpEQ)>;
+def : Pat<(i1  (fp_to_uint f32:$a)), (SETP_i32ri (AND_b32ri (CVT_u32_f32 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_uint f32:$a)), (CVT_u16_f32 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_uint f32:$a)), (CVT_u32_f32 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_uint f32:$a)), (CVT_u64_f32 $a, CvtRZI)>;
 
 // f64 -> sint
-def : Pat<(i1  (fp_to_sint f64:$a)), (SETP_i64ri $a, 0, CmpEQ)>;
+def : Pat<(i1  (fp_to_sint f64:$a)), (SETP_i32ri (AND_b32ri (CVT_s32_f64 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_sint f64:$a)), (CVT_s16_f64 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_sint f64:$a)), (CVT_s32_f64 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_sint f64:$a)), (CVT_s64_f64 $a, CvtRZI)>;
 
 // f64 -> uint
-def : Pat<(i1  (fp_to_uint f64:$a)), (SETP_i64ri $a, 0, CmpEQ)>;
+def : Pat<(i1  (fp_to_uint f64:$a)), (SETP_i32ri (AND_b32ri (CVT_u32_f64 $a, CvtRZI), 1), 0, CmpNE)>;
 def : Pat<(i16 (fp_to_uint f64:$a)), (CVT_u16_f64 $a, CvtRZI)>;
 def : Pat<(i32 (fp_to_uint f64:$a)), (CVT_u32_f64 $a, CvtRZI)>;
 def : Pat<(i64 (fp_to_uint f64:$a)), (CVT_u64_f64 $a, CvtRZI)>;
diff --git a/llvm/test/CodeGen/NVPTX/convert-fp-i1-narrow.ll b/llvm/test/CodeGen/NVPTX/convert-fp-i1-narrow.ll
new file mode 100644
index 0000000000000..7670757469e04
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/convert-fp-i1-narrow.ll
@@ -0,0 +1,14 @@
+; fptoui/fptosi from half to i1 (sm_53+) must convert-to-int then test the low
+; bit, like the f32/f64 cases. (bf16 widens to f32 below sm_80, so it exercises
+; the f32 path, not a distinct bf16 pattern.)
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_53 | FileCheck %s
+
+; CHECK-LABEL: cvt_u1_f16(
+; CHECK: cvt.rzi.u32.f16 %r{{[0-9]+}}, %rs{{[0-9]+}};
+; CHECK: and.b32 %r{{[0-9]+}}, %r{{[0-9]+}}, 1;
+define i1 @cvt_u1_f16(half %x) { %a = fptoui half %x to i1  ret i1 %a }
+
+; CHECK-LABEL: cvt_s1_f16(
+; CHECK: cvt.rzi.s32.f16 %r{{[0-9]+}}, %rs{{[0-9]+}};
+; CHECK: and.b32 %r{{[0-9]+}}, %r{{[0-9]+}}, 1;
+define i1 @cvt_s1_f16(half %x) { %a = fptosi half %x to i1  ret i1 %a }
diff --git a/llvm/test/CodeGen/NVPTX/convert-fp.ll b/llvm/test/CodeGen/NVPTX/convert-fp.ll
index 59b33b1bce7a7..7bcd7993643d3 100644
--- a/llvm/test/CodeGen/NVPTX/convert-fp.ll
+++ b/llvm/test/CodeGen/NVPTX/convert-fp.ll
@@ -163,3 +163,29 @@ define i64 @cvt_s64_f64(double %x) {
   %a = fptosi double %x to i64
   ret i64 %a
 }
+
+
+define i1 @cvt_u1_f32(float %x) {
+; CHECK-LABEL: cvt_u1_f32
+; CHECK: cvt.rzi.u32.f32 %r{{[0-9]+}}, %r{{[0-9]+}};
+; CHECK: and.b32 %r{{[0-9]+}}, %r{{[0-9]+}}, 1;
+; CHECK: ret;
+  %a = fptoui float %x to i1
+  ret i1 %a
+}
+define i1 @cvt_s1_f32(float %x) {
+; CHECK-LABEL: cvt_s1_f32
+; CHECK: cvt.rzi.s32.f32 %r{{[0-9]+}}, %r{{[0-9]+}};
+; CHECK: and.b32 %r{{[0-9]+}}, %r{{[0-9]+}}, 1;
+; CHECK: ret;
+  %a = fptosi float %x to i1
+  ret i1 %a
+}
+; CHECK-LABEL: cvt_u1_f64(
+; CHECK: cvt.rzi.u32.f64 %r{{[0-9]+}}, %rd{{[0-9]+}};
+; CHECK: and.b32 %r{{[0-9]+}}, %r{{[0-9]+}}, 1;
+define i1 @cvt_u1_f64(double %x) { %a = fptoui double %x to i1  ret i1 %a }
+; CHECK-LABEL: cvt_s1_f64(
+; CHECK: cvt.rzi.s32.f64 %r{{[0-9]+}}, %rd{{[0-9]+}};
+; CHECK: and.b32 %r{{[0-9]+}}, %r{{[0-9]+}}, 1;
+define i1 @cvt_s1_f64(double %x) { %a = fptosi double %x to i1  ret i1 %a }

``````````

</details>


https://github.com/llvm/llvm-project/pull/200718


More information about the llvm-commits mailing list