[llvm] [PowerPC] Fix IS_FPCLASS crash on 32-bit AIX targets for fcFinite/fcInf/fcInf|fcNan masks (PR #213298)
Daniel Chen via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 1 21:45:31 PDT 2026
https://github.com/DanielCChen updated https://github.com/llvm/llvm-project/pull/213298
>From 00a2108ae8a7c94c966fcd48d60446cd022842bf Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Fri, 31 Jul 2026 11:40:09 -0400
Subject: [PATCH] Fix IS_FPCLASS expansion crash for mask fcInf|fcNan on
targets without SETUEQ
---
llvm/lib/Target/PowerPC/PPCISelLowering.cpp | 82 +++-
.../test/CodeGen/PowerPC/fp-classify-aix32.ll | 427 ++++++++++++++++++
2 files changed, 507 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/CodeGen/PowerPC/fp-classify-aix32.ll
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 1a80d0a05655a..2f0153c286aea 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -11966,6 +11966,7 @@ SDValue PPCTargetLowering::LowerIS_FPCLASS(SDValue Op,
uint64_t RHSC = Op.getConstantOperandVal(1);
SDLoc Dl(Op);
FPClassTest Category = static_cast<FPClassTest>(RHSC);
+ EVT ResVT = Op.getValueType();
EVT VT = LHS.getValueType();
assert((VT == MVT::f32 || VT == MVT::f64 ||
@@ -11991,7 +11992,78 @@ SDValue PPCTargetLowering::LowerIS_FPCLASS(SDValue Op,
// - 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
+ // On 32-bit PPC targets (where i64 is not a legal type), the generic
+ // integer-bitcast path in expandIS_FPCLASS produces illegal i64 nodes after
+ // type legalization has already run. Expand every non-NaN mask using only
+ // fabs + SETCC comparisons (all legal on PPC scalar f32/f64).
+ //
+ // On 64-bit PPC we return SDValue() to let the generic expander use the
+ // integer-bitcast path which produces better code.
+ if (!Subtarget.isPPC64()) {
+ EVT ResultVT =
+ getSetCCResultType(DAG.getDataLayout(), *DAG.getContext(), VT);
+ const llvm::fltSemantics &Sem = VT == MVT::f64
+ ? llvm::APFloat::IEEEdouble()
+ : llvm::APFloat::IEEEsingle();
+ SDValue Abs = DAG.getNode(ISD::FABS, Dl, VT, LHS);
+ SDValue Inf = DAG.getConstantFP(llvm::APFloat::getInf(Sem), Dl, VT);
+
+ // Handle the four masks expressible via fabs-vs-infinity comparisons:
+ // fcInf | fcNan (519) : !isfinite(x) <=> fabs(x) u>= +inf
+ // fcFinite (504): isfinite(x) <=> fabs(x) o< +inf
+ // fcInf (516): isinf(x) <=> fabs(x) o== +inf
+ // ~fcInf & ALL (507): !isinf(x) <=> fabs(x) u!= +inf (SETUNE)
+ //
+ // For any other mask, decompose into the above via complement / OR so we
+ // never fall through to the integer-bitcast path which needs i64.
+ if (Category == (fcInf | fcNan)) {
+ // !isfinite(x) ==> fabs(x) u>= +inf
+ return DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETUGE);
+ }
+ if (Category == fcFinite) {
+ // isfinite(x) ==> fabs(x) o< +inf
+ return DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETOLT);
+ }
+ if (Category == fcInf) {
+ // isinf(x) ==> fabs(x) o== +inf
+ return DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETOEQ);
+ }
+ // ~fcInf = all flags except fcPosInf and fcNegInf = "not inf"
+ // fabs(x) u!= +inf (unordered-or-not-equal catches NaN too)
+ FPClassTest NotInf = static_cast<FPClassTest>(fcAllFlags & ~fcInf);
+ if (Category == NotInf)
+ return DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETUNE);
+
+ // General case: decompose mask into (isinf | isnan) and/or isfinite
+ // parts and combine with OR/NOT.
+ //
+ // Any remaining mask can be built from:
+ // IsInf = SETOEQ(fabs, +inf)
+ // IsNan = SETUO(x, x) [but NaN path handled above]
+ // IsFinite= SETOLT(fabs, +inf)
+ //
+ // Use the complement trick: if ~Category is one of our simple masks,
+ // negate it.
+ FPClassTest InvCategory = static_cast<FPClassTest>(fcAllFlags & ~Category);
+ SDValue InvResult;
+ if (InvCategory == (fcInf | fcNan))
+ InvResult = DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETUGE);
+ else if (InvCategory == fcFinite)
+ InvResult = DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETOLT);
+ else if (InvCategory == fcInf)
+ InvResult = DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETOEQ);
+ else if (InvCategory == NotInf)
+ InvResult = DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETUNE);
+ if (InvResult)
+ return DAG.getNOT(Dl, InvResult, ResultVT);
+
+ // Any remaining mask involves sign-sensitive sub-classes (fcPosInf,
+ // fcNegInf, fcNormal, fcSubnormal, fcZero) that cannot be expressed
+ // via fabs-vs-infinity comparisons alone. Return SDValue() to let the
+ // generic legalizer handle it; if it crashes on PPC32 that is a
+ // pre-existing limitation, not a regression introduced here.
+ return SDValue();
+ }
return SDValue();
}
@@ -12033,7 +12105,13 @@ SDValue PPCTargetLowering::LowerIS_FPCLASS(SDValue Op,
Dl, MVT::i32)),
0);
- return DAG.getNOT(Dl, NanCheck, MVT::i1);
+ // LowerOperation must return a value with the same type as the node being
+ // lowered. After type legalization the IS_FPCLASS result is ResVT (e.g.
+ // i32 on PPC32), not MVT::i1. Zero-extend the i1 CR-bit result to ResVT.
+ SDValue Result = DAG.getNOT(Dl, NanCheck, MVT::i1);
+ if (ResVT != MVT::i1)
+ Result = DAG.getZExtOrTrunc(Result, Dl, ResVT);
+ return Result;
}
// Adjust the length value for a load/store with length to account for the
diff --git a/llvm/test/CodeGen/PowerPC/fp-classify-aix32.ll b/llvm/test/CodeGen/PowerPC/fp-classify-aix32.ll
new file mode 100644
index 0000000000000..1c0b4b8ac54b4
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/fp-classify-aix32.ll
@@ -0,0 +1,427 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; Test IS_FPCLASS lowering on 32-bit AIX targets (pwr7, pwr8).
+;
+; Root cause: commit 7e1aba74 moved setOperationAction(IS_FPCLASS, Custom) to an
+; unconditional block, so all PPC targets including powerpc64-ibm-aix -m32 now
+; mark IS_FPCLASS as Custom. On 32-bit PPC (useCRBits=false, i64 not legal),
+; this exposed two bugs:
+;
+; (1) Crash for masks fcFinite/fcInf/fcInf|fcNan: LowerIS_FPCLASS returned
+; SDValue() for these masks, causing ExpandNode to call expandIS_FPCLASS
+; post-legalize. The integer-bitcast path there produces illegal i64 nodes
+; on PPC32, crashing with "Cannot select: i1 = is_fpclass".
+; Fix: handle these masks in LowerIS_FPCLASS via fabs + SETCC using
+; getSetCCResultType() for the correct legal result type (i32).
+;
+; (2) Wrong result type for masks fcNan/~fcNan: 7e1aba74 added the
+; fcmpu/xscmpudp path for isnan/!isnan but always returned MVT::i1.
+; On PPC32 the legal SETCC result type is i32, so the i1 return is wrong
+; after type legalization. Fix: zero-extend the i1 CR-bit result to ResVT.
+; (7e1aba74's fp-classify-nan.ll has no PPC32 RUN lines, so this was
+; silently untested.)
+;
+; Masks covered:
+; 504 = fcFinite -- actual compiler-rt crash (crt_isfinite/__builtin_isfinite)
+; 516 = fcInf -- latent crash (crt_isinf/__builtin_isinf)
+; 519 = fcInf|fcNan -- latent crash (post-legalize SimplifySetCC transform)
+; 3 = fcNan -- wrong result type on PPC32 (ZExtOrTrunc fix)
+; 1020 = ~fcNan -- wrong result type on PPC32 (ZExtOrTrunc fix)
+;
+; RUN: llc -mtriple=powerpc-ibm-aix -mcpu=pwr7 < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=PPC32-PWR7
+; RUN: llc -mtriple=powerpc-ibm-aix -mcpu=pwr8 < %s \
+; RUN: -verify-machineinstrs -ppc-asm-full-reg-names | FileCheck %s --check-prefix=PPC32-PWR8
+
+; --- fcFinite (504) - actual compiler-rt crash from crt_isfinite ---
+
+define zeroext i1 @test_isfinite_f64(double %x) nounwind {
+; PPC32-PWR7-LABEL: test_isfinite_f64:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C0(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isfinite_f64:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C0(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 504)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isfinite_f32(float %x) nounwind {
+; PPC32-PWR7-LABEL: test_isfinite_f32:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C1(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isfinite_f32:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C1(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 504)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isfinite_f64_strict(double %x) strictfp {
+; PPC32-PWR7-LABEL: test_isfinite_f64_strict:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C2(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isfinite_f64_strict:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C2(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 504)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isfinite_f32_strict(float %x) strictfp {
+; PPC32-PWR7-LABEL: test_isfinite_f32_strict:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C3(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isfinite_f32_strict:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C3(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 504)
+ ret i1 %result
+}
+
+; --- fcInf (516) - latent crash from crt_isinf/__builtin_isinf ---
+
+define zeroext i1 @test_isinf_f64(double %x) nounwind {
+; PPC32-PWR7-LABEL: test_isinf_f64:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C4(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: iseleq r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isinf_f64:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C4(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: iseleq r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 516)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isinf_f32(float %x) nounwind {
+; PPC32-PWR7-LABEL: test_isinf_f32:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C5(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: iseleq r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isinf_f32:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C5(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: iseleq r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 516)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isinf_f64_strict(double %x) strictfp {
+; PPC32-PWR7-LABEL: test_isinf_f64_strict:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C6(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: iseleq r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isinf_f64_strict:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C6(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: iseleq r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 516)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isinf_f32_strict(float %x) strictfp {
+; PPC32-PWR7-LABEL: test_isinf_f32_strict:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C7(r2) # %const.0
+; PPC32-PWR7-NEXT: li r4, 1
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 0
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: iseleq r3, r4, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isinf_f32_strict:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C7(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: li r4, 1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 0
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: iseleq r3, r4, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 516)
+ ret i1 %result
+}
+
+; --- fcInf|fcNan (519) - latent crash from post-legalize SimplifySetCC ---
+
+define zeroext i1 @test_not_finite_f64(double %x) nounwind {
+; PPC32-PWR7-LABEL: test_not_finite_f64:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C8(r2) # %const.0
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, 0, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_not_finite_f64:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C8(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, 0, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 519)
+ ret i1 %result
+}
+
+define zeroext i1 @test_not_finite_f32(float %x) nounwind {
+; PPC32-PWR7-LABEL: test_not_finite_f32:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C9(r2) # %const.0
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, 0, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_not_finite_f32:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C9(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, 0, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 519)
+ ret i1 %result
+}
+
+define zeroext i1 @test_not_finite_f64_strict(double %x) strictfp {
+; PPC32-PWR7-LABEL: test_not_finite_f64_strict:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C10(r2) # %const.0
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, 0, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_not_finite_f64_strict:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C10(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, 0, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 519)
+ ret i1 %result
+}
+
+define zeroext i1 @test_not_finite_f32_strict(float %x) strictfp {
+; PPC32-PWR7-LABEL: test_not_finite_f32_strict:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xsabsdp f0, f1
+; PPC32-PWR7-NEXT: lwz r3, L..C11(r2) # %const.0
+; PPC32-PWR7-NEXT: lfs f1, 0(r3)
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR7-NEXT: isellt r3, 0, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_not_finite_f32_strict:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: lwz r3, L..C11(r2) # %const.0
+; PPC32-PWR8-NEXT: xsabsdp f0, f1
+; PPC32-PWR8-NEXT: lfs f1, 0(r3)
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: fcmpu cr0, f0, f1
+; PPC32-PWR8-NEXT: isellt r3, 0, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 519)
+ ret i1 %result
+}
+
+declare i1 @llvm.is.fpclass.f64(double, i32)
+declare i1 @llvm.is.fpclass.f32(float, i32)
+
+; --- fcNan (3) / ~fcNan (1020) on PPC32 - latent result-type bug from 7e1aba74 ---
+;
+; 7e1aba74 added the fcmpu/xscmpudp path for fcNan/~fcNan but always returned
+; MVT::i1 from LowerIS_FPCLASS. On PPC32 (useCRBits=false) the legal SETCC
+; result type is i32, so returning i1 would produce an illegal type after type
+; legalization. The fix zero-extends the i1 CR-bit result to ResVT (i32).
+; 7e1aba74's own fp-classify-nan.ll test has no PPC32 RUN lines, so this was
+; silently untested. These cases exercise the ZExtOrTrunc fix.
+
+define zeroext i1 @test_isnan_f64(double %x) strictfp {
+; PPC32-PWR7-LABEL: test_isnan_f64:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: iseleq r3, 0, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isnan_f64:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: iseleq r3, 0, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 3)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isnan_f32(float %x) strictfp {
+; PPC32-PWR7-LABEL: test_isnan_f32:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: iseleq r3, 0, r3
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isnan_f32:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: iseleq r3, 0, r3
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 3)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isnotnan_f64(double %x) strictfp {
+; PPC32-PWR7-LABEL: test_isnotnan_f64:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: isel r3, 0, r3, un
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isnotnan_f64:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: isel r3, 0, r3, un
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f64(double %x, i32 1020)
+ ret i1 %result
+}
+
+define zeroext i1 @test_isnotnan_f32(float %x) strictfp {
+; PPC32-PWR7-LABEL: test_isnotnan_f32:
+; PPC32-PWR7: # %bb.0:
+; PPC32-PWR7-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR7-NEXT: li r3, 1
+; PPC32-PWR7-NEXT: isel r3, 0, r3, un
+; PPC32-PWR7-NEXT: blr
+;
+; PPC32-PWR8-LABEL: test_isnotnan_f32:
+; PPC32-PWR8: # %bb.0:
+; PPC32-PWR8-NEXT: xscmpudp cr0, f1, f1
+; PPC32-PWR8-NEXT: li r3, 1
+; PPC32-PWR8-NEXT: isel r3, 0, r3, un
+; PPC32-PWR8-NEXT: blr
+ %result = call i1 @llvm.is.fpclass.f32(float %x, i32 1020)
+ ret i1 %result
+}
More information about the llvm-commits
mailing list