[llvm] [AIX BB] Fix IS_FPCLASS expansion crash for mask fcInf|fcNan on targets without SETUEQ (PR #213298)
Daniel Chen via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 31 10:52:57 PDT 2026
https://github.com/DanielCChen updated https://github.com/llvm/llvm-project/pull/213298
>From 2d7b5053bfcb970a45e32c21bbca9c083f43b375 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 | 24 ++++
.../PowerPC/fp-classify-not-finite-aix32.ll | 127 ++++++++++++++++++
2 files changed, 151 insertions(+)
create mode 100644 llvm/test/CodeGen/PowerPC/fp-classify-not-finite-aix32.ll
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 1a80d0a05655a..e0b59644b8ece 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -11991,6 +11991,30 @@ SDValue PPCTargetLowering::LowerIS_FPCLASS(SDValue Op,
// - If value is not NaN, the comparison is equal (EQ bit set)
if ((Category != fcNan) && (Category != ~fcNan)) {
+ // On 32-bit PPC targets (where i64 is not a legal type), the generic
+ // integer-bitcast expansion of IS_FPCLASS produces illegal i64 nodes and
+ // crashes. Handle fcInf|fcNan (!isfinite) and fcFinite (isfinite) here
+ // using fabs + SETCC with the target's legal result type.
+ // On 64-bit PPC targets, leave these for the generic expander which uses
+ // the integer-bitcast path and 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();
+ if (Category == (fcInf | fcNan)) {
+ // !isfinite(x) ==> fabs(x) >= +inf
+ SDValue Abs = DAG.getNode(ISD::FABS, Dl, VT, LHS);
+ SDValue Inf = DAG.getConstantFP(llvm::APFloat::getInf(Sem), Dl, VT);
+ return DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETUGE);
+ }
+ if (Category == fcFinite) {
+ // isfinite(x) ==> fabs(x) < +inf
+ SDValue Abs = DAG.getNode(ISD::FABS, Dl, VT, LHS);
+ SDValue Inf = DAG.getConstantFP(llvm::APFloat::getInf(Sem), Dl, VT);
+ return DAG.getSetCC(Dl, ResultVT, Abs, Inf, ISD::SETOLT);
+ }
+ }
// If not checking for NaN or non-NaN, we can't handle this without P9Vector
return SDValue();
}
diff --git a/llvm/test/CodeGen/PowerPC/fp-classify-not-finite-aix32.ll b/llvm/test/CodeGen/PowerPC/fp-classify-not-finite-aix32.ll
new file mode 100644
index 0000000000000..0cf20ac2ac3be
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/fp-classify-not-finite-aix32.ll
@@ -0,0 +1,127 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; Test that IS_FPCLASS with mask fcInf|fcNan (519, "!isfinite") is correctly
+; lowered 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. When SimplifySetCC sees IS_FPCLASS is
+; LegalOrCustom, it transforms SETUEQ(fabs(x), +inf) into a new
+; IS_FPCLASS(x, fcInf|fcNan=519) node post-legalize. LowerIS_FPCLASS returned
+; SDValue() for mask 519 (only handling fcNan and ~fcNan), causing ExpandNode to
+; call expandIS_FPCLASS post-legalize with an i1 result type that is not legal
+; on PPC32 (i32 is the legal SETCC result type when useCRBits=false), crashing
+; with "Cannot select: i1 = is_fpclass" or "Unexpected illegal type".
+;
+; Fix: LowerIS_FPCLASS now handles fcInf|fcNan and fcFinite on !isPPC64() targets
+; by emitting fabs(x) u>= +inf (SETUGE) and fabs(x) olt +inf (SETOLT)
+; respectively, using getSetCCResultType() for the correct legal result type.
+; Both the strictfp (hasNoFPExcept=false) and non-strictfp (hasNoFPExcept=true)
+; paths are covered since the fix is in LowerIS_FPCLASS, not expandIS_FPCLASS.
+;
+; 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
+
+; Non-strictfp (nounwind): IS_FPCLASS node carries nofpexcept=true.
+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..C0(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..C0(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..C1(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..C1(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
+}
+
+; Strictfp: IS_FPCLASS node carries nofpexcept=false.
+; This matches the actual compiler-rt build scenario where -ffp-model=strict
+; causes IS_FPCLASS(519) to be emitted and reach LowerIS_FPCLASS unexpanded.
+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..C2(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..C2(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..C3(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..C3(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)
More information about the llvm-commits
mailing list