diff --git a/llvm/include/llvm/Analysis/ConstantFolding.h b/llvm/include/llvm/Analysis/ConstantFolding.h --- a/llvm/include/llvm/Analysis/ConstantFolding.h +++ b/llvm/include/llvm/Analysis/ConstantFolding.h @@ -67,14 +67,13 @@ const DataLayout &DL, const TargetLibraryInfo *TLI = nullptr); -/// ConstantFoldCompareInstOperands - Attempt to constant fold a compare -/// instruction (icmp/fcmp) with the specified operands. If it fails, it -/// returns a constant expression of the specified operands. -/// -Constant * -ConstantFoldCompareInstOperands(unsigned Predicate, Constant *LHS, - Constant *RHS, const DataLayout &DL, - const TargetLibraryInfo *TLI = nullptr); +/// Attempt to constant fold a compare instruction (icmp/fcmp) with the +/// specified operands. If it fails, it returns a constant expression of the +/// specified operands. +/// Denormal inputs may be flushed based on the denormal handling mode. +Constant *ConstantFoldCompareInstOperands( + unsigned Predicate, Constant *LHS, Constant *RHS, const DataLayout &DL, + const TargetLibraryInfo *TLI = nullptr, const Instruction *I = nullptr); /// Attempt to constant fold a unary operation with the specified /// operand. If it fails, it returns a constant expression of the specified @@ -95,6 +94,14 @@ Constant *RHS, const DataLayout &DL, const Instruction *I); +/// Attempt to flush float point constant according to denormal mode set in the +/// instruction's parent function attributes. If so, return a zero with the +/// correct sign, otherwise return the original constant. Inputs and outputs to +/// floating point instructions can have their mode set separately, so the +/// direction is also needed. +Constant *FlushFPConstant(Constant *Operand, const Instruction *I, + bool IsOutput); + /// Attempt to constant fold a select instruction with the specified /// operands. The constant result is returned if successful; if not, null is /// returned. diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -1071,9 +1071,11 @@ switch (Opcode) { default: return nullptr; case Instruction::ICmp: - case Instruction::FCmp: - return ConstantFoldCompareInstOperands( - cast(InstOrCE)->getPredicate(), Ops[0], Ops[1], DL, TLI); + case Instruction::FCmp: { + auto *C = cast(InstOrCE); + return ConstantFoldCompareInstOperands(C->getPredicate(), Ops[0], Ops[1], + DL, TLI, C); + } case Instruction::Freeze: return isGuaranteedNotToBeUndefOrPoison(Ops[0]) ? Ops[0] : nullptr; case Instruction::Call: @@ -1210,10 +1212,9 @@ return ConstantFoldInstOperandsImpl(I, I->getOpcode(), Ops, DL, TLI); } -Constant *llvm::ConstantFoldCompareInstOperands(unsigned IntPredicate, - Constant *Ops0, Constant *Ops1, - const DataLayout &DL, - const TargetLibraryInfo *TLI) { +Constant *llvm::ConstantFoldCompareInstOperands( + unsigned IntPredicate, Constant *Ops0, Constant *Ops1, const DataLayout &DL, + const TargetLibraryInfo *TLI, const Instruction *I) { CmpInst::Predicate Predicate = (CmpInst::Predicate)IntPredicate; // fold: icmp (inttoptr x), null -> icmp x, 0 // fold: icmp null, (inttoptr x) -> icmp 0, x @@ -1315,6 +1316,11 @@ return ConstantFoldCompareInstOperands(Predicate, Ops1, Ops0, DL, TLI); } + // Flush any denormal constant float input according to denormal handling + // mode. + Ops0 = FlushFPConstant(Ops0, I, /* IsOutput */ false); + Ops1 = FlushFPConstant(Ops1, I, /* IsOutput */ false); + return ConstantExpr::getCompare(Predicate, Ops0, Ops1); } @@ -1336,41 +1342,40 @@ return ConstantExpr::get(Opcode, LHS, RHS); } -// Check whether a constant is a floating point denormal that should be flushed -// to zero according to the denormal handling mode set in the function -// attributes. If so, return a zero with the correct sign, otherwise return the -// original constant. Inputs and outputs to floating point instructions can have -// their mode set separately, so the direction is also needed. -Constant *FlushFPConstant(Constant *Operand, const llvm::Function *F, - bool IsOutput) { - if (F == nullptr) +Constant *llvm::FlushFPConstant(Constant *Operand, const Instruction *I, + bool IsOutput) { + if (!I || !I->getParent() || !I->getFunction()) return Operand; - if (auto *CFP = dyn_cast(Operand)) { - const APFloat &APF = CFP->getValueAPF(); - Type *Ty = CFP->getType(); - DenormalMode DenormMode = F->getDenormalMode(Ty->getFltSemantics()); - DenormalMode::DenormalModeKind Mode = - IsOutput ? DenormMode.Output : DenormMode.Input; - switch (Mode) { - default: - llvm_unreachable("unknown denormal mode"); - return Operand; - case DenormalMode::IEEE: - return Operand; - case DenormalMode::PreserveSign: - if (APF.isDenormal()) { - return ConstantFP::get( - Ty->getContext(), - APFloat::getZero(Ty->getFltSemantics(), APF.isNegative())); - } - return Operand; - case DenormalMode::PositiveZero: - if (APF.isDenormal()) { - return ConstantFP::get(Ty->getContext(), - APFloat::getZero(Ty->getFltSemantics(), false)); - } - return Operand; + + ConstantFP *CFP = dyn_cast(Operand); + if (!CFP) + return Operand; + + const APFloat &APF = CFP->getValueAPF(); + Type *Ty = CFP->getType(); + DenormalMode DenormMode = + I->getFunction()->getDenormalMode(Ty->getFltSemantics()); + DenormalMode::DenormalModeKind Mode = + IsOutput ? DenormMode.Output : DenormMode.Input; + switch (Mode) { + default: + llvm_unreachable("unknown denormal mode"); + return Operand; + case DenormalMode::IEEE: + return Operand; + case DenormalMode::PreserveSign: + if (APF.isDenormal()) { + return ConstantFP::get( + Ty->getContext(), + APFloat::getZero(Ty->getFltSemantics(), APF.isNegative())); } + return Operand; + case DenormalMode::PositiveZero: + if (APF.isDenormal()) { + return ConstantFP::get(Ty->getContext(), + APFloat::getZero(Ty->getFltSemantics(), false)); + } + return Operand; } return Operand; } @@ -1378,15 +1383,16 @@ Constant *llvm::ConstantFoldFPInstOperands(unsigned Opcode, Constant *LHS, Constant *RHS, const DataLayout &DL, const Instruction *I) { - if (auto *BB = I->getParent()) { - if (auto *F = BB->getParent()) { - if (Instruction::isBinaryOp(Opcode)) { - Constant *Op0 = FlushFPConstant(LHS, F, false); - Constant *Op1 = FlushFPConstant(RHS, F, false); - Constant *C = ConstantFoldBinaryOpOperands(Opcode, Op0, Op1, DL); - return FlushFPConstant(C, F, true); - } - } + if (Instruction::isBinaryOp(Opcode)) { + // Flush denormal inputs if needed. + Constant *Op0 = FlushFPConstant(LHS, I, /* IsOutput */ false); + Constant *Op1 = FlushFPConstant(RHS, I, /* IsOutput */ false); + + // Calculate constant result. + Constant *C = ConstantFoldBinaryOpOperands(Opcode, Op0, Op1, DL); + + // Flush denormal output if needed. + return FlushFPConstant(C, I, /* IsOutput */ true); } // If instruction lacks a parent/function and the denormal mode cannot be // determined, use the default (IEEE). diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -3900,7 +3900,8 @@ if (Constant *CLHS = dyn_cast(LHS)) { if (Constant *CRHS = dyn_cast(RHS)) - return ConstantFoldCompareInstOperands(Pred, CLHS, CRHS, Q.DL, Q.TLI); + return ConstantFoldCompareInstOperands(Pred, CLHS, CRHS, Q.DL, Q.TLI, + Q.CxtI); // If we have a constant, make sure it is on the RHS. std::swap(LHS, RHS); diff --git a/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll b/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll --- a/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll +++ b/llvm/test/Transforms/InstSimplify/constant-fold-fp-denormal.ll @@ -763,7 +763,7 @@ define i1 @fcmp_double_pz_in_pz_out() #6 { ; CHECK-LABEL: @fcmp_double_pz_in_pz_out( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp une double 0x0008000000000000, 0x0 @@ -773,7 +773,7 @@ define i1 @fcmp_float_pz_in_pz_out() #6 { ; CHECK-LABEL: @fcmp_float_pz_in_pz_out( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp une float 0x3800000000000000, 0x0 @@ -783,7 +783,7 @@ define i1 @fcmp_double_ps_in_ps_out() #7 { ; CHECK-LABEL: @fcmp_double_ps_in_ps_out( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp une double 0x0008000000000000, 0x0 @@ -793,7 +793,7 @@ define i1 @fcmp_float_ps_in_ps_out() #7 { ; CHECK-LABEL: @fcmp_float_ps_in_ps_out( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp une float 0x3800000000000000, 0x0 @@ -823,7 +823,7 @@ define i1 @fcmp_double_ieee_out_pz_in() #3 { ; CHECK-LABEL: @fcmp_double_ieee_out_pz_in( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp une double 0x0008000000000000, 0x0 @@ -833,7 +833,7 @@ define i1 @fcmp_double_ieee_out_ps_in() #4 { ; CHECK-LABEL: @fcmp_double_ieee_out_ps_in( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp une double 0x0008000000000000, 0x0 @@ -853,7 +853,7 @@ define i1 @fcmp_double_two_denormal_ins() #6 { ; CHECK-LABEL: @fcmp_double_two_denormal_ins( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp une double 0x0008100000000000, 0x0008000000000000 @@ -903,7 +903,7 @@ define i1 @fcmp_double_ps_in_ps_out_oeq() #6 { ; CHECK-LABEL: @fcmp_double_ps_in_ps_out_oeq( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 false +; CHECK-NEXT: ret i1 true ; entry: %cmp = fcmp oeq double 0x0008100000000000, 0x0008000000000000 @@ -923,7 +923,7 @@ define i1 @fcmp_double_ps_in_ps_out_one() #6 { ; CHECK-LABEL: @fcmp_double_ps_in_ps_out_one( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp one double 0x0008100000000000, 0x0008000000000000 @@ -983,7 +983,7 @@ define i1 @fcmp_double_pz_in_pz_out_ugt() #7 { ; CHECK-LABEL: @fcmp_double_pz_in_pz_out_ugt( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 true +; CHECK-NEXT: ret i1 false ; entry: %cmp = fcmp ugt double 0x0008000000000000, 0x0 @@ -1043,7 +1043,7 @@ define i1 @fcmp_double_pz_in_pz_out_ule() #7 { ; CHECK-LABEL: @fcmp_double_pz_in_pz_out_ule( ; CHECK-NEXT: entry: -; CHECK-NEXT: ret i1 false +; CHECK-NEXT: ret i1 true ; entry: %cmp = fcmp ule double 0x0008000000000000, 0x0