[llvm] 6245690 - [PowerPC] improve performance on the isNan and !isNan function in case of -ffp-model=strict (#204170)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 23 06:32:25 PDT 2026
Author: zhijian lin
Date: 2026-07-23T09:32:21-04:00
New Revision: 624569002f06a27543ac964b2b338d677deb4e37
URL: https://github.com/llvm/llvm-project/commit/624569002f06a27543ac964b2b338d677deb4e37
DIFF: https://github.com/llvm/llvm-project/commit/624569002f06a27543ac964b2b338d677deb4e37.diff
LOG: [PowerPC] improve performance on the isNan and !isNan function in case of -ffp-model=strict (#204170)
For the IR representation:
isnan(x) → %0 = tail call noundef i1 @llvm.is.fpclass.f64(double %x, i32
3)
!isnan(x) → %0 = tail call noundef i1 @llvm.is.fpclass.f64(double %x,
i32 1020)
Under `-ffp-model=strict`, the generic
TargetLowering::expandIS_FPCLASS() is used to lower these when
Subtarget.hasP9Vector() && Subtarget.useCRBits() is false. However,
PowerPC has more optimal assembly sequences for isnan(x) and !isnan(x)
on POWER7/8 and generic PPC targets.
We implement a custom lowering for isnan(x) and !isnan(x) under
`-ffp-model=strict`, using `fcmpu `for POWER7/8 and generic PPC targets,
and` xscmpudp` for targets where VSX is available.
Added:
llvm/test/CodeGen/PowerPC/fp-classify-nan.ll
Modified:
llvm/lib/Target/PowerPC/PPCISelLowering.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 91125abb89504..8a03946a2ca10 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -817,6 +817,9 @@ PPCTargetLowering::PPCTargetLowering(const PPCTargetMachine &TM,
setOperationAction(ISD::FCANONICALIZE, MVT::f32, Legal);
}
+ setOperationAction(ISD::IS_FPCLASS, MVT::f32, Custom);
+ setOperationAction(ISD::IS_FPCLASS, MVT::f64, Custom);
+
if (Subtarget.hasAltivec()) {
for (MVT VT : { MVT::v16i8, MVT::v8i16, MVT::v4i32 }) {
setOperationAction(ISD::AVGCEILS, VT, Legal);
@@ -1258,11 +1261,8 @@ PPCTargetLowering::PPCTargetLowering(const PPCTargetMachine &TM,
if (Subtarget.hasP9Vector()) {
setOperationAction(ISD::INSERT_VECTOR_ELT, MVT::v4i32, Custom);
setOperationAction(ISD::INSERT_VECTOR_ELT, MVT::v4f32, Custom);
-
// Test data class instructions store results in CR bits.
if (Subtarget.useCRBits()) {
- setOperationAction(ISD::IS_FPCLASS, MVT::f32, Custom);
- setOperationAction(ISD::IS_FPCLASS, MVT::f64, Custom);
setOperationAction(ISD::IS_FPCLASS, MVT::f128, Custom);
setOperationAction(ISD::IS_FPCLASS, MVT::ppcf128, Custom);
}
@@ -11962,18 +11962,78 @@ static SDValue getDataClassTest(SDValue Op, FPClassTest Mask, const SDLoc &Dl,
SDValue PPCTargetLowering::LowerIS_FPCLASS(SDValue Op,
SelectionDAG &DAG) const {
- assert(Subtarget.hasP9Vector() && "Test data class requires Power9");
SDValue LHS = Op.getOperand(0);
uint64_t RHSC = Op.getConstantOperandVal(1);
SDLoc Dl(Op);
FPClassTest Category = static_cast<FPClassTest>(RHSC);
- if (LHS.getValueType() == MVT::ppcf128) {
+ EVT VT = LHS.getValueType();
+
+ assert((VT == MVT::f32 || VT == MVT::f64 ||
+ ((VT == MVT::f128 || VT == MVT::ppcf128) && Subtarget.hasVSX() &&
+ Subtarget.useCRBits())) &&
+ "invalid customize type for IS_FPCLASS.");
+ // Handle ppcf128 by extracting the higher part
+ if (VT == MVT::ppcf128) {
// The higher part determines the value class.
LHS = DAG.getNode(ISD::EXTRACT_ELEMENT, Dl, MVT::f64, LHS,
DAG.getConstant(1, Dl, MVT::i32));
+ VT = MVT::f64;
+ }
+
+ // If we have P9Vector and useCRBits, use the data class test instructions
+ if (Subtarget.hasP9Vector() && Subtarget.useCRBits()) {
+ return getDataClassTest(LHS, Category, Dl, DAG, Subtarget);
+ }
+
+ // For non-P9Vector targets, we can only check for NaN using fcmpu/xscmpudp
+ // These instructions set CR bits based on comparison with itself:
+ // - If value is NaN, the comparison is unordered (FU bit set)
+ // - If value is not NaN, the comparison is equal (EQ bit set)
+
+ if ((Category & ~fcNan) && (Category != ~fcNan)) {
+ // If not checking for NaN or non-NaN, we can't handle this without P9Vector
+ return SDValue();
}
- return getDataClassTest(LHS, Category, Dl, DAG, Subtarget);
+ // Determine which comparison instruction to use based on vector support
+ unsigned CmpOp;
+
+ if (Subtarget.hasVSX()) {
+ // Use xscmpudp for VSX targets (both f32 and f64)
+ // For f32, extend to f64 first
+ if (VT == MVT::f32) {
+ LHS = DAG.getNode(ISD::FP_EXTEND, Dl, MVT::f64, LHS);
+ } else if (VT != MVT::f64) {
+ return SDValue();
+ }
+ CmpOp = PPC::XSCMPUDP;
+ } else {
+ // Use fcmpu for non-VSX targets
+ // FCMPUS and FCMPUD both map to the same fcmpu instruction,
+ // just with
diff erent register classes (f4rc vs f8rc)
+ if (VT == MVT::f64) {
+ CmpOp = PPC::FCMPUD;
+ } else if (VT == MVT::f32) {
+ CmpOp = PPC::FCMPUS;
+ } else {
+ return SDValue();
+ }
+ }
+
+ // Create the comparison: fcmpu/xscmpudp CR, LHS, LHS
+ // The CR field output will be allocated by the register allocator
+ SDValue Cmp = SDValue(DAG.getMachineNode(CmpOp, Dl, MVT::i32, LHS, LHS), 0);
+
+ // Extract the unordered bit (FU) from the CR field
+ // For NaN detection: FU bit is set if operands are unordered (i.e., NaN)
+ SDValue NanCheck = SDValue(
+ DAG.getMachineNode(
+ TargetOpcode::EXTRACT_SUBREG, Dl, MVT::i1, Cmp,
+ DAG.getTargetConstant(Category == ~fcNan ? PPC::sub_un : PPC::sub_eq,
+ Dl, MVT::i32)),
+ 0);
+
+ return DAG.getNOT(Dl, NanCheck, MVT::i1);
}
// Adjust the length value for a load/store with length to account for the
diff --git a/llvm/test/CodeGen/PowerPC/fp-classify-nan.ll b/llvm/test/CodeGen/PowerPC/fp-classify-nan.ll
new file mode 100644
index 0000000000000..3a866984f2ba7
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/fp-classify-nan.ll
@@ -0,0 +1,126 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -mcpu=pwr8 < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=P8
+; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -mcpu=pwr9 < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=P9
+; RUN: llc -mtriple=powerpc64le-unknown-linux-gnu -mcpu=pwr8 -mattr=-vsx < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=P8-NO-VSX
+
+; RUN: llc -mtriple=powerpc-ibm-aix7.2.0.0 -mcpu=pwr8 < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=P8
+; RUN: llc -mtriple=powerpc-ibm-aix7.2.0.0 -mcpu=pwr9 < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=P9
+; RUN: llc -mtriple=powerpc-ibm-aix7.2.0.0 -mcpu=pwr8 -mattr=-vsx < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=P8-NO-VSX
+;
+define zeroext i1 @test_is_nan_f64(double %x) #0 {
+; P8-LABEL: test_is_nan_f64:
+; P8: # %bb.0:
+; P8-NEXT: xscmpudp cr0, f1, f1
+; P8-NEXT: li r3, 1
+; P8-NEXT: iseleq r3, 0, r3
+; P8-NEXT: blr
+;
+; P9-LABEL: test_is_nan_f64:
+; P9: # %bb.0:
+; P9-NEXT: xststdcdp cr0, f1, 64
+; P9-NEXT: li r3, 0
+; P9-NEXT: li r4, 1
+; P9-NEXT: iseleq r3, r4, r3
+; P9-NEXT: blr
+;
+; P8-NO-VSX-LABEL: test_is_nan_f64:
+; P8-NO-VSX: # %bb.0:
+; P8-NO-VSX-NEXT: fcmpu cr0, f1, f1
+; P8-NO-VSX-NEXT: li r3, 1
+; P8-NO-VSX-NEXT: iseleq r3, 0, r3
+; P8-NO-VSX-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 3)
+ ret i1 %result
+}
+
+define zeroext i1 @test_is_not_nan_f64(double %x) #0 {
+; P8-LABEL: test_is_not_nan_f64:
+; P8: # %bb.0:
+; P8-NEXT: xscmpudp cr0, f1, f1
+; P8-NEXT: li r3, 1
+; P8-NEXT: isel r3, 0, r3, un
+; P8-NEXT: blr
+;
+; P9-LABEL: test_is_not_nan_f64:
+; P9: # %bb.0:
+; P9-NEXT: xststdcdp cr0, f1, 64
+; P9-NEXT: li r3, 1
+; P9-NEXT: iseleq r3, 0, r3
+; P9-NEXT: blr
+;
+; P8-NO-VSX-LABEL: test_is_not_nan_f64:
+; P8-NO-VSX: # %bb.0:
+; P8-NO-VSX-NEXT: fcmpu cr0, f1, f1
+; P8-NO-VSX-NEXT: li r3, 1
+; P8-NO-VSX-NEXT: isel r3, 0, r3, un
+; P8-NO-VSX-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 1020)
+ ret i1 %result
+}
+
+define zeroext i1 @test_is_nan_f32(float %x) #0 {
+; P8-LABEL: test_is_nan_f32:
+; P8: # %bb.0:
+; P8-NEXT: xscmpudp cr0, f1, f1
+; P8-NEXT: li r3, 1
+; P8-NEXT: iseleq r3, 0, r3
+; P8-NEXT: blr
+;
+; P9-LABEL: test_is_nan_f32:
+; P9: # %bb.0:
+; P9-NEXT: xststdcsp cr0, f1, 64
+; P9-NEXT: li r3, 0
+; P9-NEXT: li r4, 1
+; P9-NEXT: iseleq r3, r4, r3
+; P9-NEXT: blr
+;
+; P8-NO-VSX-LABEL: test_is_nan_f32:
+; P8-NO-VSX: # %bb.0:
+; P8-NO-VSX-NEXT: fcmpu cr0, f1, f1
+; P8-NO-VSX-NEXT: li r3, 1
+; P8-NO-VSX-NEXT: iseleq r3, 0, r3
+; P8-NO-VSX-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 3)
+ ret i1 %result
+}
+
+define zeroext i1 @test_is_not_nan_f32(float %x) #0 {
+; P8-LABEL: test_is_not_nan_f32:
+; P8: # %bb.0:
+; P8-NEXT: xscmpudp cr0, f1, f1
+; P8-NEXT: li r3, 1
+; P8-NEXT: isel r3, 0, r3, un
+; P8-NEXT: blr
+;
+; P9-LABEL: test_is_not_nan_f32:
+; P9: # %bb.0:
+; P9-NEXT: xststdcsp cr0, f1, 64
+; P9-NEXT: li r3, 1
+; P9-NEXT: iseleq r3, 0, r3
+; P9-NEXT: blr
+;
+; P8-NO-VSX-LABEL: test_is_not_nan_f32:
+; P8-NO-VSX: # %bb.0:
+; P8-NO-VSX-NEXT: fcmpu cr0, f1, f1
+; P8-NO-VSX-NEXT: li r3, 1
+; P8-NO-VSX-NEXT: isel r3, 0, r3, un
+; P8-NO-VSX-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 1020)
+ ret i1 %result
+}
+
+declare i1 @llvm.is.fpclass.f64(double, i32)
+declare i1 @llvm.is.fpclass.f32(float, i32)
+declare float @llvm.fabs.f32(float)
+declare double @llvm.fabs.f64(double)
+declare fp128 @llvm.fabs.f128(fp128)
+declare <4 x float> @llvm.fabs.v4f32(<4 x float>)
+declare <2 x double> @llvm.fabs.v2f64(<2 x double>)
+
+attributes #0 = {strictfp}
More information about the llvm-commits
mailing list