Index: llvm/include/llvm/ADT/FloatingPointMode.h =================================================================== --- llvm/include/llvm/ADT/FloatingPointMode.h +++ llvm/include/llvm/ADT/FloatingPointMode.h @@ -143,6 +143,12 @@ Input != DenormalModeKind::Invalid; } + /// Return true if input denormals must be implicitly treated as 0. + constexpr bool inputsAreZero() const { + return Input == DenormalModeKind::PreserveSign || + Input == DenormalModeKind::PositiveZero; + } + /// Get the effective denormal mode if the mode if this caller calls into a /// function with \p Callee. This promotes dynamic modes to the mode of the /// caller. @@ -223,7 +229,9 @@ /// Floating-point class tests, supported by 'is_fpclass' intrinsic. Actual /// test may be an OR combination of basic tests. -enum FPClassTest { +enum FPClassTest : unsigned { + fcNone = 0x0000, + fcSNan = 0x0001, fcQNan = 0x0002, fcNegInf = 0x0004, Index: llvm/include/llvm/CodeGen/CodeGenCommonISel.h =================================================================== --- llvm/include/llvm/CodeGen/CodeGenCommonISel.h +++ llvm/include/llvm/CodeGen/CodeGenCommonISel.h @@ -16,9 +16,14 @@ #include "llvm/CodeGen/MachineBasicBlock.h" #include + +// FIXME: Why isn't this in namespace llvm? +enum FPClassTest : unsigned; + namespace llvm { class BasicBlock; + /// Encapsulates all of the information needed to generate a stack protector /// check, and signals to isel when initialized that one needs to be generated. /// @@ -218,7 +223,7 @@ /// \param Test The test as specified in 'is_fpclass' intrinsic invocation. /// \returns The inverted test, or zero, if inversion does not produce simpler /// test. -unsigned getInvertedFPClassTest(unsigned Test); +FPClassTest getInvertedFPClassTest(FPClassTest Test); /// Assuming the instruction \p MI is going to be deleted, attempt to salvage /// debug users of \p MI by writing the effect of \p MI in a DIExpression. Index: llvm/include/llvm/CodeGen/TargetLowering.h =================================================================== --- llvm/include/llvm/CodeGen/TargetLowering.h +++ llvm/include/llvm/CodeGen/TargetLowering.h @@ -4928,7 +4928,7 @@ /// \param Test The test to perform. /// \param Flags The optimization flags. /// \returns The expansion result or SDValue() if it fails. - SDValue expandIS_FPCLASS(EVT ResultVT, SDValue Op, unsigned Test, + SDValue expandIS_FPCLASS(EVT ResultVT, SDValue Op, FPClassTest Test, SDNodeFlags Flags, const SDLoc &DL, SelectionDAG &DAG) const; Index: llvm/lib/CodeGen/CodeGenCommonISel.cpp =================================================================== --- llvm/lib/CodeGen/CodeGenCommonISel.cpp +++ llvm/lib/CodeGen/CodeGenCommonISel.cpp @@ -173,8 +173,8 @@ return SplitPoint; } -unsigned llvm::getInvertedFPClassTest(unsigned Test) { - unsigned InvertedTest = ~Test & fcAllFlags; +FPClassTest llvm::getInvertedFPClassTest(FPClassTest Test) { + FPClassTest InvertedTest = static_cast(~Test & fcAllFlags); switch (InvertedTest) { default: break; @@ -198,7 +198,7 @@ case fcNegFinite: return InvertedTest; } - return 0; + return fcNone; } static MachineOperand *getSalvageOpsForCopy(const MachineRegisterInfo &MRI, Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6521,7 +6521,8 @@ const DataLayout DLayout = DAG.getDataLayout(); EVT DestVT = TLI.getValueType(DLayout, I.getType()); EVT ArgVT = TLI.getValueType(DLayout, I.getArgOperand(0)->getType()); - unsigned Test = cast(I.getArgOperand(1))->getZExtValue(); + FPClassTest Test = static_cast( + cast(I.getArgOperand(1))->getZExtValue()); MachineFunction &MF = DAG.getMachineFunction(); const Function &F = MF.getFunction(); SDValue Op = getValue(I.getArgOperand(0)); Index: llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -8006,17 +8006,31 @@ return SDValue(); } +/// If this FPClassTest can be performed with a fcmp to 0, return the test mask +/// for the floating-point mode. +static FPClassTest isFCmpEqualZero(FPClassTest Test, + const fltSemantics &Semantics, + const MachineFunction &MF) { + if (Test == fcZero && + MF.getDenormalMode(Semantics).Input == DenormalMode::IEEE) + return fcZero; + if (Test == (fcZero | fcSubnormal) && + MF.getDenormalMode(Semantics).inputsAreZero()) + return static_cast(fcZero | fcSubnormal); + return fcNone; +} + SDValue TargetLowering::expandIS_FPCLASS(EVT ResultVT, SDValue Op, - unsigned Test, SDNodeFlags Flags, - const SDLoc &DL, + FPClassTest TestMask, + SDNodeFlags Flags, const SDLoc &DL, SelectionDAG &DAG) const { EVT OperandVT = Op.getValueType(); assert(OperandVT.isFloatingPoint()); // Degenerated cases. - if (Test == 0) + if (TestMask == fcNone) return DAG.getBoolConstant(false, DL, ResultVT, OperandVT); - if ((Test & fcAllFlags) == fcAllFlags) + if ((TestMask & fcAllFlags) == fcAllFlags) return DAG.getBoolConstant(true, DL, ResultVT, OperandVT); // PPC double double is a pair of doubles, of which the higher part determines @@ -8030,9 +8044,9 @@ // Some checks may be represented as inversion of simpler check, for example // "inf|normal|subnormal|zero" => !"nan". bool IsInverted = false; - if (unsigned InvertedCheck = getInvertedFPClassTest(Test)) { + if (FPClassTest InvertedCheck = getInvertedFPClassTest(TestMask)) { IsInverted = true; - Test = InvertedCheck; + TestMask = InvertedCheck; } // Floating-point type properties. @@ -8044,26 +8058,25 @@ // Some checks can be implemented using float comparisons, if floating point // exceptions are ignored. if (Flags.hasNoFPExcept() && + // TODO: Should check isCondCodeLegal isOperationLegalOrCustom(ISD::SETCC, OperandVT.getScalarType())) { - if (Test == fcZero) { - DenormalMode Mode = DAG.getMachineFunction().getDenormalMode(Semantics); - if (Mode.Input == DenormalMode::IEEE) { - // If denormals could be implicitly treated as 0, this is not equivalent - // to a compare with 0 since it will also be true for denormals. - // - // TODO: With DAZ, check == fcZero | fcSubnormal - return DAG.getSetCC(DL, ResultVT, Op, - DAG.getConstantFP(0.0, DL, OperandVT), - IsInverted ? ISD::SETUNE : ISD::SETOEQ); - } - + if (isFCmpEqualZero(TestMask, Semantics, DAG.getMachineFunction()) != + fcNone) { + // If denormals could be implicitly treated as 0, this is not equivalent + // to a compare with 0 since it will also be true for denormals. + return DAG.getSetCC(DL, ResultVT, Op, + DAG.getConstantFP(0.0, DL, OperandVT), + IsInverted ? ISD::SETUNE : ISD::SETOEQ); } - if (Test == fcNan) + if (TestMask == fcNan) return DAG.getSetCC(DL, ResultVT, Op, Op, IsInverted ? ISD::SETO : ISD::SETUO); } + // FIXME: Placeholder until FPClassTest is marked as a BitmaskEnum. + unsigned Test = static_cast(TestMask); + // In the general case use integer operations. unsigned BitSize = OperandVT.getScalarSizeInBits(); EVT IntVT = EVT::getIntegerVT(*DAG.getContext(), BitSize); Index: llvm/test/CodeGen/X86/is_fpclass.ll =================================================================== --- llvm/test/CodeGen/X86/is_fpclass.ll +++ llvm/test/CodeGen/X86/is_fpclass.ll @@ -711,24 +711,15 @@ define i1 @iszero_f_daz(float %x) #0 { ; CHECK-32-LABEL: iszero_f_daz: ; CHECK-32: # %bb.0: # %entry -; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) -; CHECK-32-NEXT: fldz -; CHECK-32-NEXT: fucompp -; CHECK-32-NEXT: fnstsw %ax -; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax -; CHECK-32-NEXT: sahf -; CHECK-32-NEXT: setnp %cl +; CHECK-32-NEXT: testl $2147483647, {{[0-9]+}}(%esp) # imm = 0x7FFFFFFF ; CHECK-32-NEXT: sete %al -; CHECK-32-NEXT: andb %cl, %al ; CHECK-32-NEXT: retl ; ; CHECK-64-LABEL: iszero_f_daz: ; CHECK-64: # %bb.0: # %entry -; CHECK-64-NEXT: xorps %xmm1, %xmm1 -; CHECK-64-NEXT: cmpeqss %xmm0, %xmm1 -; CHECK-64-NEXT: movd %xmm1, %eax -; CHECK-64-NEXT: andl $1, %eax -; CHECK-64-NEXT: # kill: def $al killed $al killed $eax +; CHECK-64-NEXT: movd %xmm0, %eax +; CHECK-64-NEXT: testl $2147483647, %eax # imm = 0x7FFFFFFF +; CHECK-64-NEXT: sete %al ; CHECK-64-NEXT: retq entry: %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 96) ; 0x60 = "zero" @@ -846,24 +837,24 @@ define i1 @issubnormal_or_zero_f_daz(float %x) #0 { ; CHECK-32-LABEL: issubnormal_or_zero_f_daz: ; CHECK-32: # %bb.0: # %entry -; CHECK-32-NEXT: movl $2147483647, %eax # imm = 0x7FFFFFFF -; CHECK-32-NEXT: andl {{[0-9]+}}(%esp), %eax -; CHECK-32-NEXT: sete %cl -; CHECK-32-NEXT: decl %eax -; CHECK-32-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF -; CHECK-32-NEXT: setb %al -; CHECK-32-NEXT: orb %cl, %al +; CHECK-32-NEXT: flds {{[0-9]+}}(%esp) +; CHECK-32-NEXT: fldz +; CHECK-32-NEXT: fucompp +; CHECK-32-NEXT: fnstsw %ax +; CHECK-32-NEXT: # kill: def $ah killed $ah killed $ax +; CHECK-32-NEXT: sahf +; CHECK-32-NEXT: setnp %cl +; CHECK-32-NEXT: sete %al +; CHECK-32-NEXT: andb %cl, %al ; CHECK-32-NEXT: retl ; ; CHECK-64-LABEL: issubnormal_or_zero_f_daz: ; CHECK-64: # %bb.0: # %entry -; CHECK-64-NEXT: movd %xmm0, %eax -; CHECK-64-NEXT: andl $2147483647, %eax # imm = 0x7FFFFFFF -; CHECK-64-NEXT: sete %cl -; CHECK-64-NEXT: decl %eax -; CHECK-64-NEXT: cmpl $8388607, %eax # imm = 0x7FFFFF -; CHECK-64-NEXT: setb %al -; CHECK-64-NEXT: orb %cl, %al +; CHECK-64-NEXT: xorps %xmm1, %xmm1 +; CHECK-64-NEXT: cmpeqss %xmm0, %xmm1 +; CHECK-64-NEXT: movd %xmm1, %eax +; CHECK-64-NEXT: andl $1, %eax +; CHECK-64-NEXT: # kill: def $al killed $al killed $eax ; CHECK-64-NEXT: retq entry: %0 = tail call i1 @llvm.is.fpclass.f32(float %x, i32 240) ; 0xf0 = "subnormal|zero" Index: llvm/unittests/ADT/FloatingPointMode.cpp =================================================================== --- llvm/unittests/ADT/FloatingPointMode.cpp +++ llvm/unittests/ADT/FloatingPointMode.cpp @@ -219,4 +219,11 @@ DenormalMode::getIEEE(), DenormalMode::getPreserveSign().mergeCalleeMode(DenormalMode::getIEEE())); } + +TEST(FloatingPointModeTest, DenormalModePredicates) { + EXPECT_TRUE(DenormalMode::getPreserveSign().inputsAreZero()); + EXPECT_TRUE(DenormalMode::getPositiveZero().inputsAreZero()); + EXPECT_FALSE(DenormalMode::getIEEE().inputsAreZero()); + EXPECT_FALSE(DenormalMode::getDynamic().inputsAreZero()); +} }