[llvm] [NVPTX] Fix fptosi/fptoui to i1. (PR #200718)
Justin Lebar via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 10:57:27 PDT 2026
https://github.com/jlebar updated https://github.com/llvm/llvm-project/pull/200718
>From 7d12bd57813f42d5d2144344c0988e07b9d5c5ad Mon Sep 17 00:00:00 2001
From: Justin Lebar <justin.lebar at gmail.com>
Date: Sun, 31 May 2026 18:07:01 -0700
Subject: [PATCH] [NVPTX] Fix fptosi/fptoui to i1.
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.
Because the conversion truncates toward zero, the only results that are
not poison are:
- 0 and -1 for the signed case, and
- 0 and 1 for the unsigned case.
So the i1 result is fully determined by a single fp compare:
- `fptosi x to i1` == `x <= -1.0`
- `fptoui x to i1` == `x >= 1.0`
with any value being acceptable for the poison (out-of-range) inputs.
---
llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp | 13 +++++++
llvm/lib/Target/NVPTX/NVPTXInstrInfo.td | 11 ++----
llvm/test/CodeGen/NVPTX/convert-fp.ll | 39 ++++++++++++++++++---
3 files changed, 51 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp b/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp
index d1d01089aa49e..21b61cecfd6c4 100644
--- a/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp
@@ -994,6 +994,7 @@ NVPTXTargetLowering::NVPTXTargetLowering(const NVPTXTargetMachine &TM,
MVT::bf16, Custom);
}
+ setOperationAction({ISD::FP_TO_SINT, ISD::FP_TO_UINT}, MVT::i1, Custom);
setOperationAction(ISD::FROUND, MVT::f16, Promote);
setOperationAction(ISD::FROUND, MVT::v2f16, Expand);
setOperationAction(ISD::FROUND, MVT::v2bf16, Expand);
@@ -3481,6 +3482,18 @@ NVPTXTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
return LowerINT_TO_FP(Op, DAG);
case ISD::FP_TO_SINT:
case ISD::FP_TO_UINT:
+ // fptosi/fptoui to i1 truncate toward zero, so the only defined results
+ // are {0,-1} (signed) and {0,1} (unsigned); every other input results in
+ // poison. Thus we can simply lower to `x <= -1.0` or `x >= 1.0`.
+ if (Op.getValueType() == MVT::i1) {
+ SDLoc DL(Op);
+ SDValue X = Op.getOperand(0);
+ bool IsSigned = Op.getOpcode() == ISD::FP_TO_SINT;
+ return DAG.getSetCC(
+ DL, MVT::i1, X,
+ DAG.getConstantFP(IsSigned ? -1.0 : 1.0, DL, X.getValueType()),
+ IsSigned ? ISD::SETOLE : ISD::SETOGE);
+ }
return LowerFP_TO_INT(Op, DAG);
case ISD::FP_ROUND:
return LowerFP_ROUND(Op, DAG);
diff --git a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
index 982d664e694f8..ff3e5144c6fbe 100644
--- a/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
+++ b/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
@@ -2105,26 +2105,25 @@ def : Pat<(f64 (uint_to_fp i32:$a)), (CVT_f64_u32 $a, CvtRN)>;
def : Pat<(f64 (uint_to_fp i64:$a)), (CVT_f64_u64 $a, CvtRN)>;
+// fp_to_sint/uint to i1 is custom-lowered to an fp compare
+// (NVPTXTargetLowering::LowerOperation), so no i1 patterns appear below.
+
// f16 -> sint
-def : Pat<(i1 (fp_to_sint f16:$a)), (SETP_i16ri $a, 0, CmpEQ)>;
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<(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<(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<(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 +2133,6 @@ 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<(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 +2143,16 @@ 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<(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<(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<(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.ll b/llvm/test/CodeGen/NVPTX/convert-fp.ll
index 59b33b1bce7a7..f03287a811339 100644
--- a/llvm/test/CodeGen/NVPTX/convert-fp.ll
+++ b/llvm/test/CodeGen/NVPTX/convert-fp.ll
@@ -1,7 +1,7 @@
-; RUN: llc < %s -mtriple=nvptx -mcpu=sm_20 | FileCheck %s
-; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_20 | FileCheck %s
-; RUN: %if ptxas-ptr32 %{ llc < %s -mtriple=nvptx -mcpu=sm_20 | %ptxas-verify %}
-; RUN: %if ptxas %{ llc < %s -mtriple=nvptx64 -mcpu=sm_20 | %ptxas-verify %}
+; RUN: llc < %s -mtriple=nvptx -mcpu=sm_53 | FileCheck %s
+; RUN: llc < %s -mtriple=nvptx64 -mcpu=sm_53 | FileCheck %s
+; RUN: %if ptxas-ptr32 %{ llc < %s -mtriple=nvptx -mcpu=sm_53 | %ptxas-verify %}
+; RUN: %if ptxas %{ llc < %s -mtriple=nvptx64 -mcpu=sm_53 | %ptxas-verify %}
define i16 @cvt_u16_f32(float %x) {
; CHECK: cvt.rzi.u16.f32 %rs{{[0-9]+}}, %r{{[0-9]+}};
@@ -163,3 +163,34 @@ define i64 @cvt_s64_f64(double %x) {
%a = fptosi double %x to i64
ret i64 %a
}
+
+
+; fptoui/fptosi to i1 truncate toward zero, so the result is just
+; (x >= 1.0) / (x <= -1.0) — a single fp compare.
+; CHECK-LABEL: cvt_u1_f32
+; CHECK: setp.ge.f32 %p{{[0-9]+}}, %r{{[0-9]+}}, 0f3F800000;
+; CHECK: ret;
+define i1 @cvt_u1_f32(float %x) { %a = fptoui float %x to i1 ret i1 %a }
+
+; CHECK-LABEL: cvt_s1_f32
+; CHECK: setp.le.f32 %p{{[0-9]+}}, %r{{[0-9]+}}, 0fBF800000;
+; CHECK: ret;
+define i1 @cvt_s1_f32(float %x) { %a = fptosi float %x to i1 ret i1 %a }
+
+; CHECK-LABEL: cvt_u1_f64(
+; CHECK: setp.ge.f64 %p{{[0-9]+}}, %rd{{[0-9]+}}, 0d3FF0000000000000;
+define i1 @cvt_u1_f64(double %x) { %a = fptoui double %x to i1 ret i1 %a }
+
+; CHECK-LABEL: cvt_s1_f64(
+; CHECK: setp.le.f64 %p{{[0-9]+}}, %rd{{[0-9]+}}, 0dBFF0000000000000;
+define i1 @cvt_s1_f64(double %x) { %a = fptosi double %x to i1 ret i1 %a }
+
+; CHECK-LABEL: cvt_u1_f16(
+; CHECK: mov.b16 %rs{{[0-9]+}}, 0x3C00;
+; CHECK: setp.ge.f16 %p{{[0-9]+}}, %rs{{[0-9]+}}, %rs{{[0-9]+}};
+define i1 @cvt_u1_f16(half %x) { %a = fptoui half %x to i1 ret i1 %a }
+
+; CHECK-LABEL: cvt_s1_f16(
+; CHECK: mov.b16 %rs{{[0-9]+}}, 0xBC00;
+; CHECK: setp.le.f16 %p{{[0-9]+}}, %rs{{[0-9]+}}, %rs{{[0-9]+}};
+define i1 @cvt_s1_f16(half %x) { %a = fptosi half %x to i1 ret i1 %a }
More information about the llvm-commits
mailing list