Index: include/llvm/Analysis/ValueTracking.h =================================================================== --- include/llvm/Analysis/ValueTracking.h +++ include/llvm/Analysis/ValueTracking.h @@ -292,12 +292,32 @@ /// \brief Specific patterns of select instructions we can match. enum SelectPatternFlavor { SPF_UNKNOWN = 0, - SPF_SMIN, // Signed minimum - SPF_UMIN, // Unsigned minimum - SPF_SMAX, // Signed maximum - SPF_UMAX, // Unsigned maximum - SPF_ABS, // Absolute value - SPF_NABS // Negated absolute value + SPF_SMIN, /// Signed minimum + SPF_UMIN, /// Unsigned minimum + SPF_SMAX, /// Signed maximum + SPF_UMAX, /// Unsigned maximum + SPF_FMINNUM, /// Floating point minnum + SPF_FMAXNUM, /// Floating point maxnum + SPF_ABS, /// Absolute value + SPF_NABS /// Negated absolute value + }; + /// \brief Behavior when a floating point min/max is given one NaN and one + /// non-NaN as input. + enum SelectPatternNaNBehavior { + SPNB_NA = 0, /// NaN behavior not applicable. + SPNB_RETURNS_NAN, /// Given one NaN input, returns the NaN. + SPNB_RETURNS_OTHER, /// Given one NaN input, returns the non-NaN. + SPNB_RETURNS_ANY /// Given one NaN input, can return either (or + /// it has been determined that no operands can + /// be NaN). + }; + struct SelectPatternResult { + SelectPatternFlavor Flavor; + SelectPatternNaNBehavior NaNBehavior; /// Only applicable if Flavor is + /// SPF_FMINNUM or SPF_FMAXNUM. + bool Ordered; /// When implementing this min/max pattern as + /// fcmp; select, does the fcmp have to be + /// ordered? }; /// Pattern match integer [SU]MIN, [SU]MAX and ABS idioms, returning the kind /// and providing the out parameter results if we successfully match. @@ -314,7 +334,7 @@ /// /// -> LHS = %a, RHS = i32 4, *CastOp = Instruction::SExt /// - SelectPatternFlavor matchSelectPattern(Value *V, Value *&LHS, Value *&RHS, + SelectPatternResult matchSelectPattern(Value *V, Value *&LHS, Value *&RHS, Instruction::CastOps *CastOp = nullptr); } // end namespace llvm Index: lib/Analysis/ValueTracking.cpp =================================================================== --- lib/Analysis/ValueTracking.cpp +++ lib/Analysis/ValueTracking.cpp @@ -3316,40 +3316,115 @@ return OverflowResult::MayOverflow; } -static SelectPatternFlavor matchSelectPattern(ICmpInst::Predicate Pred, +static bool isKnownNonNaN(Value *V, FastMathFlags FMF) { + if (FMF.noNaNs()) + return true; + + if (ConstantFP *C = dyn_cast(V)) + return !C->isNaN(); + return false; +} + +static bool isKnownNonZero(Value *V) { + if (ConstantFP *C = dyn_cast(V)) + return !C->isZero(); + return false; +} + +static SelectPatternResult matchSelectPattern(CmpInst::Predicate Pred, + FastMathFlags FMF, Value *CmpLHS, Value *CmpRHS, Value *TrueVal, Value *FalseVal, Value *&LHS, Value *&RHS) { LHS = CmpLHS; RHS = CmpRHS; - // (icmp X, Y) ? X : Y - if (TrueVal == CmpLHS && FalseVal == CmpRHS) { - switch (Pred) { - default: return SPF_UNKNOWN; // Equality. - case ICmpInst::ICMP_UGT: - case ICmpInst::ICMP_UGE: return SPF_UMAX; - case ICmpInst::ICMP_SGT: - case ICmpInst::ICMP_SGE: return SPF_SMAX; - case ICmpInst::ICMP_ULT: - case ICmpInst::ICMP_ULE: return SPF_UMIN; - case ICmpInst::ICMP_SLT: - case ICmpInst::ICMP_SLE: return SPF_SMIN; + // If the predicate is an "or-equal" (FP) predicate, then signed zeroes will + // return the wrong result under minnum/maxnum: + // (0.0 <= -0.0) ? 0.0 : -0.0 // Returns 0.0 + // fminnum(0.0, -0.0) // Returns -0.0 + // Therefore we only proceed if at least one of the operands is known to not + // be zero. + switch (Pred) { + default: break; + case CmpInst::FCMP_OGE: case CmpInst::FCMP_OLE: + case CmpInst::FCMP_UGE: case CmpInst::FCMP_ULE: + if (!FMF.noSignedZeros() && !isKnownNonZero(CmpLHS) && + !isKnownNonZero(CmpRHS)) + return {SPF_UNKNOWN, SPNB_NA, false}; + } + + SelectPatternNaNBehavior NaNBehavior = SPNB_NA; + bool Ordered = false; + + // When given one NaN and one non-NaN input: + // - maxnum/minnum (C99 fmaxf()/fminf()) return the non-NaN input. + // - A simple C99 (a < b ? a : b) construction will return the NaN input. + // so here we discover exactly what NaN behavior is required/accepted. + if (CmpInst::isFPPredicate(Pred)) { + bool LHSSafe = isKnownNonNaN(CmpLHS, FMF); + bool RHSSafe = isKnownNonNaN(CmpRHS, FMF); + + if (LHSSafe && RHSSafe) { + // Both operands are known non-NaN. + NaNBehavior = SPNB_RETURNS_ANY; + } else if (CmpInst::isOrdered(Pred)) { + // An ordered comparison will return false when given a NaN, so it + // returns the RHS. + Ordered = true; + if (LHSSafe) + // LHS is non-NaN, so RHS is NaN. + NaNBehavior = SPNB_RETURNS_NAN; + else if (RHSSafe) + NaNBehavior = SPNB_RETURNS_OTHER; + else + // Completely unsafe. + return {SPF_UNKNOWN, SPNB_NA, false}; + } else { + Ordered = false; + // An unordered comparison will return true when given a NaN, so it + // returns the LHS. + if (LHSSafe) + // LHS is non-NaN. + NaNBehavior = SPNB_RETURNS_OTHER; + else if (RHSSafe) + NaNBehavior = SPNB_RETURNS_NAN; + else + // Completely unsafe. + return {SPF_UNKNOWN, SPNB_NA, false}; } } - // (icmp X, Y) ? Y : X if (TrueVal == CmpRHS && FalseVal == CmpLHS) { + std::swap(CmpLHS, CmpRHS); + Pred = CmpInst::getSwappedPredicate(Pred); + if (NaNBehavior == SPNB_RETURNS_NAN) + NaNBehavior = SPNB_RETURNS_OTHER; + else if (NaNBehavior == SPNB_RETURNS_OTHER) + NaNBehavior = SPNB_RETURNS_NAN; + Ordered = !Ordered; + } + + // (icmp X, Y) ? X : Y + if (TrueVal == CmpLHS && FalseVal == CmpRHS) { switch (Pred) { - default: return SPF_UNKNOWN; // Equality. + default: return {SPF_UNKNOWN, SPNB_NA, false}; // Equality. case ICmpInst::ICMP_UGT: - case ICmpInst::ICMP_UGE: return SPF_UMIN; + case ICmpInst::ICMP_UGE: return {SPF_UMAX, SPNB_NA, false}; case ICmpInst::ICMP_SGT: - case ICmpInst::ICMP_SGE: return SPF_SMIN; + case ICmpInst::ICMP_SGE: return {SPF_SMAX, SPNB_NA, false}; case ICmpInst::ICMP_ULT: - case ICmpInst::ICMP_ULE: return SPF_UMAX; + case ICmpInst::ICMP_ULE: return {SPF_UMIN, SPNB_NA, false}; case ICmpInst::ICMP_SLT: - case ICmpInst::ICMP_SLE: return SPF_SMAX; + case ICmpInst::ICMP_SLE: return {SPF_SMIN, SPNB_NA, false}; + case FCmpInst::FCMP_UGT: + case FCmpInst::FCMP_UGE: + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_OGE: return {SPF_FMAXNUM, NaNBehavior, Ordered}; + case FCmpInst::FCMP_ULT: + case FCmpInst::FCMP_ULE: + case FCmpInst::FCMP_OLT: + case FCmpInst::FCMP_OLE: return {SPF_FMINNUM, NaNBehavior, Ordered}; } } @@ -3360,13 +3435,13 @@ // ABS(X) ==> (X >s 0) ? X : -X and (X >s -1) ? X : -X // NABS(X) ==> (X >s 0) ? -X : X and (X >s -1) ? -X : X if (Pred == ICmpInst::ICMP_SGT && (C1->isZero() || C1->isMinusOne())) { - return (CmpLHS == TrueVal) ? SPF_ABS : SPF_NABS; + return {(CmpLHS == TrueVal) ? SPF_ABS : SPF_NABS, SPNB_NA, false}; } // ABS(X) ==> (X (X isZero() || C1->isOne())) { - return (CmpLHS == FalseVal) ? SPF_ABS : SPF_NABS; + return {(CmpLHS == FalseVal) ? SPF_ABS : SPF_NABS, SPNB_NA, false}; } } @@ -3377,17 +3452,17 @@ match(CmpLHS, m_Not(m_Specific(TrueVal))))) { LHS = TrueVal; RHS = FalseVal; - return SPF_SMIN; + return {SPF_SMIN, SPNB_NA, false}; } } } // TODO: (X > 4) ? X : 5 --> (X >= 5) ? X : 5 --> MAX(X, 5) - return SPF_UNKNOWN; + return {SPF_UNKNOWN, SPNB_NA, false}; } -static Constant *lookThroughCast(ICmpInst *CmpI, Value *V1, Value *V2, +static Constant *lookThroughCast(CmpInst *CmpI, Value *V1, Value *V2, Instruction::CastOps *CastOp) { CastInst *CI = dyn_cast(V1); Constant *C = dyn_cast(V2); @@ -3409,39 +3484,60 @@ if (isa(CI)) return ConstantExpr::getIntegerCast(C, CI->getSrcTy(), CmpI->isSigned()); + if (isa(CI)) + return ConstantExpr::getUIToFP(C, CI->getSrcTy(), true); + + if (isa(CI)) + return ConstantExpr::getSIToFP(C, CI->getSrcTy(), true); + + if (isa(CI)) + return ConstantExpr::getFPToUI(C, CI->getSrcTy(), true); + + if (isa(CI)) + return ConstantExpr::getFPToSI(C, CI->getSrcTy(), true); + + if (isa(CI)) + return ConstantExpr::getFPExtend(C, CI->getSrcTy(), true); + + if (isa(CI)) + return ConstantExpr::getFPTrunc(C, CI->getSrcTy(), true); + return nullptr; } -SelectPatternFlavor llvm::matchSelectPattern(Value *V, +SelectPatternResult llvm::matchSelectPattern(Value *V, Value *&LHS, Value *&RHS, Instruction::CastOps *CastOp) { SelectInst *SI = dyn_cast(V); - if (!SI) return SPF_UNKNOWN; + if (!SI) return {SPF_UNKNOWN, SPNB_NA, false}; - ICmpInst *CmpI = dyn_cast(SI->getCondition()); - if (!CmpI) return SPF_UNKNOWN; + CmpInst *CmpI = dyn_cast(SI->getCondition()); + if (!CmpI) return {SPF_UNKNOWN, SPNB_NA, false}; - ICmpInst::Predicate Pred = CmpI->getPredicate(); + CmpInst::Predicate Pred = CmpI->getPredicate(); Value *CmpLHS = CmpI->getOperand(0); Value *CmpRHS = CmpI->getOperand(1); Value *TrueVal = SI->getTrueValue(); Value *FalseVal = SI->getFalseValue(); + FastMathFlags FMF; + if (isa(CmpI)) + FMF = CmpI->getFastMathFlags(); // Bail out early. if (CmpI->isEquality()) - return SPF_UNKNOWN; + return {SPF_UNKNOWN, SPNB_NA, false}; // Deal with type mismatches. if (CastOp && CmpLHS->getType() != TrueVal->getType()) { if (Constant *C = lookThroughCast(CmpI, TrueVal, FalseVal, CastOp)) - return ::matchSelectPattern(Pred, CmpLHS, CmpRHS, + return ::matchSelectPattern(Pred, FMF, CmpLHS, CmpRHS, cast(TrueVal)->getOperand(0), C, LHS, RHS); if (Constant *C = lookThroughCast(CmpI, FalseVal, TrueVal, CastOp)) - return ::matchSelectPattern(Pred, CmpLHS, CmpRHS, + return ::matchSelectPattern(Pred, FMF, CmpLHS, CmpRHS, C, cast(FalseVal)->getOperand(0), LHS, RHS); } - return ::matchSelectPattern(Pred, CmpLHS, CmpRHS, TrueVal, FalseVal, + return ::matchSelectPattern(Pred, FMF, CmpLHS, CmpRHS, TrueVal, FalseVal, LHS, RHS); } Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2274,7 +2274,7 @@ // Min/max matching is only viable if all output VTs are the same. if (std::equal(ValueVTs.begin(), ValueVTs.end(), ValueVTs.begin())) { Value *LHS, *RHS; - SelectPatternFlavor SPF = matchSelectPattern(const_cast(&I), LHS, RHS); + SelectPatternFlavor SPF = matchSelectPattern(const_cast(&I), LHS, RHS).Flavor; ISD::NodeType Opc = ISD::DELETED_NODE; switch (SPF) { case SPF_UMAX: Opc = ISD::UMAX; break; Index: lib/Transforms/InstCombine/InstCombineCasts.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineCasts.cpp +++ lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -441,7 +441,7 @@ // min/max. Value *LHS, *RHS; if (SelectInst *SI = dyn_cast(CI.getOperand(0))) - if (matchSelectPattern(SI, LHS, RHS) != SPF_UNKNOWN) + if (matchSelectPattern(SI, LHS, RHS).Flavor != SPF_UNKNOWN) return nullptr; // See if we can simplify any instructions used by the input whose sole @@ -1307,10 +1307,15 @@ // (fptrunc (select cond, R1, Cst)) --> // (select cond, (fptrunc R1), (fptrunc Cst)) + // + // - but only if this isn't part of a min/max operation, else we'll + // ruin min/max canonical form. + Value *LHS, *RHS; SelectInst *SI = dyn_cast(CI.getOperand(0)); if (SI && (isa(SI->getOperand(1)) || - isa(SI->getOperand(2)))) { + isa(SI->getOperand(2))) && + matchSelectPattern(SI, LHS, RHS).Flavor == SPF_UNKNOWN) { Value *LHSTrunc = Builder->CreateFPTrunc(SI->getOperand(1), CI.getType()); Value *RHSTrunc = Builder->CreateFPTrunc(SI->getOperand(2), Index: lib/Transforms/InstCombine/InstCombineSelect.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineSelect.cpp +++ lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -38,7 +38,8 @@ } } -static CmpInst::Predicate getICmpPredicateForMinMax(SelectPatternFlavor SPF) { +static CmpInst::Predicate getCmpPredicateForMinMax(SelectPatternFlavor SPF, + bool Ordered=false) { switch (SPF) { default: llvm_unreachable("unhandled!"); @@ -51,13 +52,18 @@ return ICmpInst::ICMP_SGT; case SPF_UMAX: return ICmpInst::ICMP_UGT; + case SPF_FMINNUM: + return Ordered ? FCmpInst::FCMP_OLT : FCmpInst::FCMP_ULT; + case SPF_FMAXNUM: + return Ordered ? FCmpInst::FCMP_OGT : FCmpInst::FCMP_UGT; } } static Value *generateMinMaxSelectPattern(InstCombiner::BuilderTy *Builder, SelectPatternFlavor SPF, Value *A, Value *B) { - CmpInst::Predicate Pred = getICmpPredicateForMinMax(SPF); + CmpInst::Predicate Pred = getCmpPredicateForMinMax(SPF); + assert(CmpInst::isIntPredicate(Pred)); return Builder->CreateSelect(Builder->CreateICmp(Pred, A, B), A, B); } @@ -1054,20 +1060,31 @@ } // See if we can fold the select into one of our operands. - if (SI.getType()->isIntOrIntVectorTy()) { + if (SI.getType()->isIntOrIntVectorTy() || SI.getType()->isFPOrFPVectorTy()) { if (Instruction *FoldI = FoldSelectIntoOp(SI, TrueVal, FalseVal)) return FoldI; Value *LHS, *RHS, *LHS2, *RHS2; Instruction::CastOps CastOp; - SelectPatternFlavor SPF = matchSelectPattern(&SI, LHS, RHS, &CastOp); + SelectPatternResult SPR = matchSelectPattern(&SI, LHS, RHS, &CastOp); + auto SPF = SPR.Flavor; if (SPF) { // Canonicalize so that type casts are outside select patterns. if (LHS->getType()->getPrimitiveSizeInBits() != SI.getType()->getPrimitiveSizeInBits()) { - CmpInst::Predicate Pred = getICmpPredicateForMinMax(SPF); - Value *Cmp = Builder->CreateICmp(Pred, LHS, RHS); + CmpInst::Predicate Pred = getCmpPredicateForMinMax(SPF, SPR.Ordered); + + Value *Cmp; + if (CmpInst::isIntPredicate(Pred)) { + Cmp = Builder->CreateICmp(Pred, LHS, RHS); + } else { + IRBuilder<>::FastMathFlagGuard FMFG(*Builder); + auto FMF = cast(SI.getCondition())->getFastMathFlags(); + Builder->SetFastMathFlags(FMF); + Cmp = Builder->CreateFCmp(Pred, LHS, RHS); + } + Value *NewSI = Builder->CreateCast(CastOp, Builder->CreateSelect(Cmp, LHS, RHS), SI.getType()); @@ -1078,11 +1095,11 @@ // MIN(MIN(a, b), a) -> MIN(a, b) // MAX(MIN(a, b), a) -> a // MIN(MAX(a, b), a) -> a - if (SelectPatternFlavor SPF2 = matchSelectPattern(LHS, LHS2, RHS2)) + if (SelectPatternFlavor SPF2 = matchSelectPattern(LHS, LHS2, RHS2).Flavor) if (Instruction *R = FoldSPFofSPF(cast(LHS),SPF2,LHS2,RHS2, SI, SPF, RHS)) return R; - if (SelectPatternFlavor SPF2 = matchSelectPattern(RHS, LHS2, RHS2)) + if (SelectPatternFlavor SPF2 = matchSelectPattern(RHS, LHS2, RHS2).Flavor) if (Instruction *R = FoldSPFofSPF(cast(RHS),SPF2,LHS2,RHS2, SI, SPF, LHS)) return R; Index: lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp +++ lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp @@ -410,7 +410,7 @@ // If this is a select as part of a min/max pattern, don't simplify any // further in case we break the structure. Value *LHS, *RHS; - if (matchSelectPattern(I, LHS, RHS) != SPF_UNKNOWN) + if (matchSelectPattern(I, LHS, RHS).Flavor != SPF_UNKNOWN) return nullptr; if (SimplifyDemandedBits(I->getOperandUse(2), DemandedMask, RHSKnownZero, Index: test/Transforms/InstCombine/minmax-fp.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/minmax-fp.ll @@ -0,0 +1,156 @@ +; RUN: opt -S -instcombine < %s | FileCheck %s + +; CHECK-LABEL: @t1 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fpext +define double @t1(float %a) { + ; This is the canonical form for a type-changing min/max. + %1 = fcmp ult float %a, 5.0 + %2 = select i1 %1, float %a, float 5.0 + %3 = fpext float %2 to double + ret double %3 +} + +; CHECK-LABEL: @t2 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fpext +define double @t2(float %a) { + ; Check this is converted into canonical form, as above. + %1 = fcmp ult float %a, 5.0 + %2 = fpext float %a to double + %3 = select i1 %1, double %2, double 5.0 + ret double %3 +} + +; CHECK-LABEL: @t4 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fptrunc +define float @t4(double %a) { + ; Same again, with trunc. + %1 = fcmp ult double %a, 5.0 + %2 = fptrunc double %a to float + %3 = select i1 %1, float %2, float 5.0 + ret float %3 +} + +; CHECK-LABEL: @t5 +; CHECK-NEXT: fcmp +; CHECK-NEXT: fpext +; CHECK-NEXT: select +define double @t5(float %a) { + ; different values, should not be converted. + %1 = fcmp ult float %a, 5.0 + %2 = fpext float %a to double + %3 = select i1 %1, double %2, double 5.001 + ret double %3 +} + +; CHECK-LABEL: @t6 +; CHECK-NEXT: fcmp +; CHECK-NEXT: fpext +; CHECK-NEXT: select +define double @t6(float %a) { + ; Signed zero, should not be converted + %1 = fcmp ult float %a, -0.0 + %2 = fpext float %a to double + %3 = select i1 %1, double %2, double 0.0 + ret double %3 +} + +; CHECK-LABEL: @t7 +; CHECK-NEXT: fcmp +; CHECK-NEXT: fpext +; CHECK-NEXT: select +define double @t7(float %a) { + ; Signed zero, should not be converted + %1 = fcmp ult float %a, 0.0 + %2 = fpext float %a to double + %3 = select i1 %1, double %2, double -0.0 + ret double %3 +} + +; CHECK-LABEL: @t8 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fptoui +define i64 @t8(float %a) { + %1 = fcmp ult float %a, 5.0 + %2 = fptoui float %a to i64 + %3 = select i1 %1, i64 %2, i64 5 + ret i64 %3 +} + +; CHECK-LABEL: @t9 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fptosi +define i8 @t9(float %a) { + %1 = fcmp ult float %a, 0.0 + %2 = fptosi float %a to i8 + %3 = select i1 %1, i8 %2, i8 0 + ret i8 %3 +} + +; CHECK-LABEL: @t11 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fptosi +define i8 @t11(float %a, float %b) { + ; Either operand could be NaN, but fast modifier applied. + %1 = fcmp fast ult float %b, %a + %2 = fptosi float %a to i8 + %3 = fptosi float %b to i8 + %4 = select i1 %1, i8 %3, i8 %2 + ret i8 %4 +} + +; CHECK-LABEL: @t12 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fptosi +define i8 @t12(float %a, float %b) { + ; Either operand could be NaN, but nnan modifier applied. + %1 = fcmp nnan ult float %b, %a + %2 = fptosi float %a to i8 + %3 = fptosi float %b to i8 + %4 = select i1 %1, i8 %3, i8 %2 + ret i8 %4 +} + +; CHECK-LABEL: @t13 +; CHECK-NEXT: fcmp +; CHECK-NEXT: fptosi +; CHECK-NEXT: select +define i8 @t13(float %a) { + ; Float and int values do not match. + %1 = fcmp ult float %a, 1.5 + %2 = fptosi float %a to i8 + %3 = select i1 %1, i8 %2, i8 1 + ret i8 %3 +} + +; CHECK-LABEL: @t14 +; CHECK-NEXT: fcmp +; CHECK-NEXT: fptosi +; CHECK-NEXT: select +define i8 @t14(float %a) { + ; <= comparison, where %a could be -0.0. Not safe. + %1 = fcmp ule float %a, 0.0 + %2 = fptosi float %a to i8 + %3 = select i1 %1, i8 %2, i8 0 + ret i8 %3 +} + +; CHECK-LABEL: @t15 +; CHECK-NEXT: fcmp +; CHECK-NEXT: select +; CHECK-NEXT: fptosi +define i8 @t15(float %a) { + %1 = fcmp nsz ule float %a, 0.0 + %2 = fptosi float %a to i8 + %3 = select i1 %1, i8 %2, i8 0 + ret i8 %3 +} Index: unittests/Analysis/CMakeLists.txt =================================================================== --- unittests/Analysis/CMakeLists.txt +++ unittests/Analysis/CMakeLists.txt @@ -13,4 +13,5 @@ LazyCallGraphTest.cpp ScalarEvolutionTest.cpp MixedTBAATest.cpp + ValueTrackingTest.cpp ) Index: unittests/Analysis/ValueTrackingTest.cpp =================================================================== --- /dev/null +++ unittests/Analysis/ValueTrackingTest.cpp @@ -0,0 +1,148 @@ +//===- ValueTrackingTest.cpp - ValueTracking tests ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SourceMgr.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class MatchSelectPatternTest : public testing::Test { +protected: + void parseAssembly(const char *Assembly) { + SMDiagnostic Error; + M = parseAssemblyString(Assembly, Error, getGlobalContext()); + + std::string errMsg; + raw_string_ostream os(errMsg); + Error.print("", os); + + // A failure here means that the test itself is buggy. + if (!M) + report_fatal_error(os.str()); + + Function *F = M->getFunction("test"); + if (F == nullptr) + report_fatal_error("Test must have a function named @test"); + + A = nullptr; + for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { + if (I->hasName()) { + if (I->getName() == "A") + A = &*I; + } + } + if (A == nullptr) + report_fatal_error("@test must have an instruction %A"); + } + + void expectPattern(const SelectPatternResult &P) { + Value *LHS, *RHS; + Instruction::CastOps CastOp; + SelectPatternResult R = matchSelectPattern(A, LHS, RHS, &CastOp); + EXPECT_EQ(P.Flavor, R.Flavor); + EXPECT_EQ(P.NaNBehavior, R.NaNBehavior); + EXPECT_EQ(P.Ordered, R.Ordered); + } + + std::unique_ptr M; + Instruction *A, *B; +}; + +} + +TEST_F(MatchSelectPatternTest, SimpleFMin) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp ult float %a, 5.0\n" + " %A = select i1 %1, float %a, float 5.0\n" + " ret float %A\n" + "}\n"); + expectPattern({SPF_FMINNUM, SPNB_RETURNS_NAN, false}); +} + +TEST_F(MatchSelectPatternTest, SimpleFMax) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp ogt float %a, 5.0\n" + " %A = select i1 %1, float %a, float 5.0\n" + " ret float %A\n" + "}\n"); + expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true}); +} + +TEST_F(MatchSelectPatternTest, SwappedFMax) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp olt float 5.0, %a\n" + " %A = select i1 %1, float %a, float 5.0\n" + " ret float %A\n" + "}\n"); + expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, false}); +} + +TEST_F(MatchSelectPatternTest, SwappedFMax2) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp olt float %a, 5.0\n" + " %A = select i1 %1, float 5.0, float %a\n" + " ret float %A\n" + "}\n"); + expectPattern({SPF_FMAXNUM, SPNB_RETURNS_NAN, false}); +} + +TEST_F(MatchSelectPatternTest, SwappedFMax3) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp ult float %a, 5.0\n" + " %A = select i1 %1, float 5.0, float %a\n" + " ret float %A\n" + "}\n"); + expectPattern({SPF_FMAXNUM, SPNB_RETURNS_OTHER, true}); +} + +TEST_F(MatchSelectPatternTest, FastFMin) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp nnan olt float %a, 5.0\n" + " %A = select i1 %1, float %a, float 5.0\n" + " ret float %A\n" + "}\n"); + expectPattern({SPF_FMINNUM, SPNB_RETURNS_ANY, false}); +} + +TEST_F(MatchSelectPatternTest, FMinConstantZero) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp ole float %a, 0.0\n" + " %A = select i1 %1, float %a, float 0.0\n" + " ret float %A\n" + "}\n"); + // This shouldn't be matched, as %a could be -0.0. + expectPattern({SPF_UNKNOWN, SPNB_NA, false}); +} + +TEST_F(MatchSelectPatternTest, FMinConstantZeroNsz) { + parseAssembly( + "define float @test(float %a) {\n" + " %1 = fcmp nsz ole float %a, 0.0\n" + " %A = select i1 %1, float %a, float 0.0\n" + " ret float %A\n" + "}\n"); + // But this should be, because we've ignored signed zeroes. + expectPattern({SPF_FMINNUM, SPNB_RETURNS_OTHER, true}); +}