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 @@ -70,11 +70,11 @@ /// 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); +/// For fcmp, denormal constant input will be folded further according to 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 +95,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 @@ -1186,7 +1186,7 @@ if (const auto *CI = dyn_cast(I)) return ConstantFoldCompareInstOperands(CI->getPredicate(), Ops[0], Ops[1], - DL, TLI); + DL, TLI, CI); if (const auto *LI = dyn_cast(I)) { if (LI->isVolatile()) @@ -1216,10 +1216,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 @@ -1321,6 +1320,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); } @@ -1342,41 +1346,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; } @@ -1384,15 +1387,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); @@ -4211,7 +4212,7 @@ if (CmpInst *C = dyn_cast(I)) return ConstantFoldCompareInstOperands(C->getPredicate(), ConstOps[0], - ConstOps[1], Q.DL, Q.TLI); + ConstOps[1], Q.DL, Q.TLI, C); if (LoadInst *LI = dyn_cast(I)) if (!LI->isVolatile()) 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 0x0, 0x8000000000000 @@ -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 0x0, 0x3800000000000000 @@ -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 0x0, 0x8000000000000 @@ -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 0x0, 0x3800000000000000 @@ -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 0x0, 0x8000000000000 @@ -833,7 +833,7 @@ define i1 @fcmp_double_ieee_out_ps_in() #3 { ; 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 0x0, 0x8000000000000 @@ -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 0x8100000000000, 0x8000000000000