diff --git a/llvm/lib/Analysis/DomConditionCache.cpp b/llvm/lib/Analysis/DomConditionCache.cpp index 3dad0c2e07133..c07a8a76f111d 100644 --- a/llvm/lib/Analysis/DomConditionCache.cpp +++ b/llvm/lib/Analysis/DomConditionCache.cpp @@ -43,7 +43,7 @@ static void findAffectedValues(Value *Cond, if (!Visited.insert(V).second) continue; - ICmpInst::Predicate Pred; + CmpInst::Predicate Pred; Value *A, *B; // Only recurse into and/or if it matches the top-level and/or type. if (TopLevelIsAnd ? match(V, m_LogicalAnd(m_Value(A), m_Value(B))) @@ -67,6 +67,11 @@ static void findAffectedValues(Value *Cond, if (match(A, m_Add(m_Value(X), m_ConstantInt()))) AddAffected(X); } + } else if (match(Cond, m_CombineOr(m_FCmp(Pred, m_Value(A), m_Constant()), + m_Intrinsic( + m_Value(A), m_Constant())))) { + // Handle patterns that computeKnownFPClass() support. + AddAffected(A); } } } diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 0e40a02fd4de6..220ef32c2d3ce 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -4225,9 +4225,53 @@ llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS, return fcmpImpliesClass(Pred, F, LHS, *ConstRHS, LookThroughSrc); } -static FPClassTest computeKnownFPClassFromAssumes(const Value *V, - const SimplifyQuery &Q) { - FPClassTest KnownFromAssume = fcAllFlags; +static void computeKnownFPClassFromCond(const Value *V, Value *Cond, + bool CondIsTrue, + const Instruction *CxtI, + KnownFPClass &KnownFromContext) { + CmpInst::Predicate Pred; + Value *LHS; + uint64_t ClassVal = 0; + const APFloat *CRHS; + // TODO: handle sign-bit check idiom + if (match(Cond, m_FCmp(Pred, m_Value(LHS), m_APFloat(CRHS)))) { + auto [CmpVal, MaskIfTrue, MaskIfFalse] = fcmpImpliesClass( + Pred, *CxtI->getParent()->getParent(), LHS, *CRHS, LHS != V); + if (CmpVal == V) + KnownFromContext.knownNot(~(CondIsTrue ? MaskIfTrue : MaskIfFalse)); + } else if (match(Cond, m_Intrinsic( + m_Value(LHS), m_ConstantInt(ClassVal)))) { + FPClassTest Mask = static_cast(ClassVal); + KnownFromContext.knownNot(CondIsTrue ? ~Mask : Mask); + } +} + +static KnownFPClass computeKnownFPClassFromContext(const Value *V, + const SimplifyQuery &Q) { + KnownFPClass KnownFromContext; + + if (!Q.CxtI) + return KnownFromContext; + + if (Q.DC && Q.DT) { + // Handle dominating conditions. + for (BranchInst *BI : Q.DC->conditionsFor(V)) { + Value *Cond = BI->getCondition(); + + BasicBlockEdge Edge0(BI->getParent(), BI->getSuccessor(0)); + if (Q.DT->dominates(Edge0, Q.CxtI->getParent())) + computeKnownFPClassFromCond(V, Cond, /*CondIsTrue=*/true, Q.CxtI, + KnownFromContext); + + BasicBlockEdge Edge1(BI->getParent(), BI->getSuccessor(1)); + if (Q.DT->dominates(Edge1, Q.CxtI->getParent())) + computeKnownFPClassFromCond(V, Cond, /*CondIsTrue=*/false, Q.CxtI, + KnownFromContext); + } + } + + if (!Q.AC) + return KnownFromContext; // Try to restrict the floating-point classes based on information from // assumptions. @@ -4245,25 +4289,11 @@ static FPClassTest computeKnownFPClassFromAssumes(const Value *V, if (!isValidAssumeForContext(I, Q.CxtI, Q.DT)) continue; - CmpInst::Predicate Pred; - Value *LHS, *RHS; - uint64_t ClassVal = 0; - if (match(I->getArgOperand(0), m_FCmp(Pred, m_Value(LHS), m_Value(RHS)))) { - const APFloat *CRHS; - if (match(RHS, m_APFloat(CRHS))) { - auto [CmpVal, MaskIfTrue, MaskIfFalse] = - fcmpImpliesClass(Pred, *F, LHS, *CRHS, LHS != V); - if (CmpVal == V) - KnownFromAssume &= MaskIfTrue; - } - } else if (match(I->getArgOperand(0), - m_Intrinsic( - m_Value(LHS), m_ConstantInt(ClassVal)))) { - KnownFromAssume &= static_cast(ClassVal); - } + computeKnownFPClassFromCond(V, I->getArgOperand(0), /*CondIsTrue=*/true, + Q.CxtI, KnownFromContext); } - return KnownFromAssume; + return KnownFromContext; } void computeKnownFPClass(const Value *V, const APInt &DemandedElts, @@ -4371,10 +4401,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts, KnownNotFromFlags |= fcInf; } - if (Q.AC) { - FPClassTest AssumedClasses = computeKnownFPClassFromAssumes(V, Q); - KnownNotFromFlags |= ~AssumedClasses; - } + KnownFPClass AssumedClasses = computeKnownFPClassFromContext(V, Q); + KnownNotFromFlags |= ~AssumedClasses.KnownFPClasses; // We no longer need to find out about these bits from inputs if we can // assume this from flags/attributes. @@ -4382,6 +4410,12 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts, auto ClearClassesFromFlags = make_scope_exit([=, &Known] { Known.knownNot(KnownNotFromFlags); + if (!Known.SignBit && AssumedClasses.SignBit) { + if (*AssumedClasses.SignBit) + Known.signBitMustBeOne(); + else + Known.signBitMustBeZero(); + } }); if (!Op) @@ -5283,7 +5317,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts, bool First = true; - for (Value *IncValue : P->incoming_values()) { + for (const Use &U : P->operands()) { + Value *IncValue = U.get(); // Skip direct self references. if (IncValue == P) continue; @@ -5292,8 +5327,10 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts, // Recurse, but cap the recursion to two levels, because we don't want // to waste time spinning around in loops. We need at least depth 2 to // detect known sign bits. - computeKnownFPClass(IncValue, DemandedElts, InterestedClasses, KnownSrc, - PhiRecursionLimit, Q); + computeKnownFPClass( + IncValue, DemandedElts, InterestedClasses, KnownSrc, + PhiRecursionLimit, + Q.getWithInstruction(P->getIncomingBlock(U)->getTerminator())); if (First) { Known = KnownSrc; diff --git a/llvm/test/Transforms/InstCombine/fpclass-from-dom-cond.ll b/llvm/test/Transforms/InstCombine/fpclass-from-dom-cond.ll new file mode 100644 index 0000000000000..5d4840159dc9a --- /dev/null +++ b/llvm/test/Transforms/InstCombine/fpclass-from-dom-cond.ll @@ -0,0 +1,322 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt -S -passes=instcombine < %s | FileCheck %s + +define i1 @test1(float %x) { +; CHECK-LABEL: define i1 @test1( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND:%.*]] = fcmp ueq float [[X]], 0.000000e+00 +; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: ret i1 false +; CHECK: if.else: +; CHECK-NEXT: [[RET:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 780) +; CHECK-NEXT: ret i1 [[RET]] +; +entry: + %cond = fcmp ueq float %x, 0.000000e+00 + br i1 %cond, label %if.then, label %if.else + +if.then: + ret i1 false + +if.else: + %ret = call i1 @llvm.is.fpclass.f32(float %x, i32 783) + ret i1 %ret +} + +define i1 @test2(double %x) { +; CHECK-LABEL: define i1 @test2( +; CHECK-SAME: double [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = fcmp olt double [[X]], 0x3EB0C6F7A0000000 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: ret i1 false +; CHECK: if.end: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = fcmp olt double %x, 0x3EB0C6F7A0000000 + br i1 %cmp, label %if.then, label %if.end +if.then: + ret i1 false +if.end: + %cmp.i = fcmp oeq double %x, 0.000000e+00 + ret i1 %cmp.i +} + +define i1 @test3(float %x) { +; CHECK-LABEL: define i1 @test3( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = fcmp ogt float [[X]], 3.000000e+00 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[RET:%.*]] = fcmp oeq float [[X]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[RET]] +; CHECK: if.else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = fcmp ogt float %x, 3.000000e+00 + br i1 %cmp, label %if.then, label %if.else +if.then: + %abs = call float @llvm.fabs.f32(float %x) + %ret = fcmp oeq float %abs, 0x7FF0000000000000 + ret i1 %ret +if.else: + ret i1 false +} + +define float @test4(float %x) { +; CHECK-LABEL: define float @test4( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = fcmp olt float [[X]], 0x3EB0C6F7A0000000 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: ret float 1.000000e+00 +; CHECK: if.end: +; CHECK-NEXT: [[DIV:%.*]] = fdiv float 1.000000e+00, [[X]] +; CHECK-NEXT: ret float [[DIV]] +; +entry: + %cmp = fcmp olt float %x, 0x3EB0C6F7A0000000 + br i1 %cmp, label %if.then, label %if.end + +if.then: + ret float 1.0 + +if.end: + %cmp.i = fcmp oeq float %x, 0.000000e+00 + %div = fdiv float 1.000000e+00, %x + %ret = select i1 %cmp.i, float 1.000000e+00, float %div + ret float %ret +} + +define i1 @test5(double %x, i1 %cond) { +; CHECK-LABEL: define i1 @test5( +; CHECK-SAME: double [[X:%.*]], i1 [[COND:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: br i1 [[COND]], label [[IF:%.*]], label [[EXIT:%.*]] +; CHECK: if: +; CHECK-NEXT: [[CMP:%.*]] = fcmp uno double [[X]], 0.000000e+00 +; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: ret i1 false +; CHECK: if.end: +; CHECK-NEXT: br label [[EXIT]] +; CHECK: exit: +; CHECK-NEXT: [[Y:%.*]] = phi double [ -1.000000e+00, [[ENTRY:%.*]] ], [ [[X]], [[IF_END]] ] +; CHECK-NEXT: [[RET:%.*]] = tail call i1 @llvm.is.fpclass.f64(double [[Y]], i32 408) +; CHECK-NEXT: ret i1 [[RET]] +; +entry: + br i1 %cond, label %if, label %exit +if: + %cmp = fcmp uno double %x, 0.000000e+00 + br i1 %cmp, label %if.then, label %if.end +if.then: + ret i1 false +if.end: + br label %exit +exit: + %y = phi double [ -1.000000e+00, %entry ], [ %x, %if.end ] + %ret = tail call i1 @llvm.is.fpclass.f64(double %y, i32 411) + ret i1 %ret +} + +define i1 @test6(double %x) { +; CHECK-LABEL: define i1 @test6( +; CHECK-SAME: double [[X:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = fcmp ogt double [[X]], 0.000000e+00 +; CHECK-NEXT: br i1 [[CMP]], label [[LAND_RHS:%.*]], label [[LAND_END:%.*]] +; CHECK: land.rhs: +; CHECK-NEXT: [[CMP_I:%.*]] = fcmp oeq double [[X]], 0x7FF0000000000000 +; CHECK-NEXT: br label [[LAND_END]] +; CHECK: land.end: +; CHECK-NEXT: [[RET:%.*]] = phi i1 [ false, [[ENTRY:%.*]] ], [ [[CMP_I]], [[LAND_RHS]] ] +; CHECK-NEXT: ret i1 [[RET]] +; +entry: + %cmp = fcmp ogt double %x, 0.000000e+00 + br i1 %cmp, label %land.rhs, label %land.end + +land.rhs: + %abs = tail call double @llvm.fabs.f64(double %x) + %and.i = bitcast double %abs to i64 + %cmp.i = icmp eq i64 %and.i, 9218868437227405312 + br label %land.end + +land.end: + %ret = phi i1 [ false, %entry ], [ %cmp.i, %land.rhs ] + ret i1 %ret +} + +define i1 @test7(float %x) { +; CHECK-LABEL: define i1 @test7( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: [[COND:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 345) +; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[RET1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 328) +; CHECK-NEXT: ret i1 [[RET1]] +; CHECK: if.else: +; CHECK-NEXT: [[RET2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 128) +; CHECK-NEXT: ret i1 [[RET2]] +; + %cond = call i1 @llvm.is.fpclass.f32(float %x, i32 345) + br i1 %cond, label %if.then, label %if.else +if.then: + %ret1 = call i1 @llvm.is.fpclass.f32(float %x, i32 456) + ret i1 %ret1 +if.else: + %ret2 = call i1 @llvm.is.fpclass.f32(float %x, i32 456) + ret i1 %ret2 +} + +; TODO: These two is.fpclass can be simplified. +define i1 @test8(float %x) { +; CHECK-LABEL: define i1 @test8( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: [[ABS:%.*]] = call float @llvm.fabs.f32(float [[X]]) +; CHECK-NEXT: [[COND:%.*]] = fcmp oeq float [[ABS]], 0x7FF0000000000000 +; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[RET1:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 575) +; CHECK-NEXT: ret i1 [[RET1]] +; CHECK: if.else: +; CHECK-NEXT: [[RET2:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 575) +; CHECK-NEXT: ret i1 [[RET2]] +; + %abs = call float @llvm.fabs.f32(float %x) + %cond = fcmp oeq float %abs, 0x7FF0000000000000 + br i1 %cond, label %if.then, label %if.else +if.then: + %ret1 = call i1 @llvm.is.fpclass.f32(float %x, i32 575) + ret i1 %ret1 +if.else: + %ret2 = call i1 @llvm.is.fpclass.f32(float %x, i32 575) + ret i1 %ret2 +} + +define i1 @test9(float %x) { +; CHECK-LABEL: define i1 @test9( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: [[COND:%.*]] = fcmp olt float [[X]], -1.000000e+00 +; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: ret i1 false +; CHECK: if.else: +; CHECK-NEXT: ret i1 false +; + %cond = fcmp olt float %x, -1.0 + br i1 %cond, label %if.then, label %if.else +if.then: + %ret1 = fcmp oeq float %x, 0x7FF0000000000000 + ret i1 %ret1 +if.else: + ret i1 false +} + +define i1 @test10(float %x) { +; CHECK-LABEL: define i1 @test10( +; CHECK-SAME: float [[X:%.*]]) { +; CHECK-NEXT: [[COND:%.*]] = fcmp olt float [[X]], -1.000000e+00 +; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: ret i1 false +; CHECK: if.else: +; CHECK-NEXT: ret i1 false +; + %cond = fcmp olt float %x, -1.0 + %neg = fneg float %x + br i1 %cond, label %if.then, label %if.else +if.then: + %ret1 = fcmp oeq float %neg, 0xFFF0000000000000 + ret i1 %ret1 +if.else: + ret i1 false +} + +; TODO: handle and/or conditions +define i1 @test11_and(float %x, i1 %cond2) { +; CHECK-LABEL: define i1 @test11_and( +; CHECK-SAME: float [[X:%.*]], i1 [[COND2:%.*]]) { +; CHECK-NEXT: [[COND:%.*]] = fcmp olt float [[X]], -1.000000e+00 +; CHECK-NEXT: [[AND:%.*]] = and i1 [[COND]], [[COND2]] +; CHECK-NEXT: br i1 [[AND]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[RET1:%.*]] = fcmp oeq float [[X]], 0x7FF0000000000000 +; CHECK-NEXT: ret i1 [[RET1]] +; CHECK: if.else: +; CHECK-NEXT: ret i1 false +; + %cond = fcmp olt float %x, -1.0 + %neg = fneg float %x + %and = and i1 %cond, %cond2 + br i1 %and, label %if.then, label %if.else +if.then: + %ret1 = fcmp oeq float %neg, 0xFFF0000000000000 + ret i1 %ret1 +if.else: + ret i1 false +} + +; TODO: handle and/or conditions +define i1 @test12_or(float %x, i1 %cond2) { +; CHECK-LABEL: define i1 @test12_or( +; CHECK-SAME: float [[X:%.*]], i1 [[COND2:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[COND:%.*]] = fcmp ueq float [[X]], 0.000000e+00 +; CHECK-NEXT: [[OR:%.*]] = or i1 [[COND]], [[COND2]] +; CHECK-NEXT: br i1 [[OR]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]] +; CHECK: if.then: +; CHECK-NEXT: ret i1 false +; CHECK: if.else: +; CHECK-NEXT: [[RET:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 783) +; CHECK-NEXT: ret i1 [[RET]] +; +entry: + %cond = fcmp ueq float %x, 0.000000e+00 + %or = or i1 %cond, %cond2 + br i1 %or, label %if.then, label %if.else + +if.then: + ret i1 false + +if.else: + %ret = call i1 @llvm.is.fpclass.f32(float %x, i32 783) + ret i1 %ret +} + +define i1 @test1_no_dominating(float %x, i1 %c) { +; CHECK-LABEL: define i1 @test1_no_dominating( +; CHECK-SAME: float [[X:%.*]], i1 [[C:%.*]]) { +; CHECK-NEXT: entry0: +; CHECK-NEXT: br i1 [[C]], label [[ENTRY:%.*]], label [[IF_ELSE:%.*]] +; CHECK: entry: +; CHECK-NEXT: [[COND:%.*]] = fcmp ueq float [[X]], 0.000000e+00 +; CHECK-NEXT: br i1 [[COND]], label [[IF_THEN:%.*]], label [[IF_ELSE]] +; CHECK: if.then: +; CHECK-NEXT: ret i1 false +; CHECK: if.else: +; CHECK-NEXT: [[RET:%.*]] = call i1 @llvm.is.fpclass.f32(float [[X]], i32 783) +; CHECK-NEXT: ret i1 [[RET]] +; +entry0: + br i1 %c, label %entry, label %if.else + +entry: + %cond = fcmp ueq float %x, 0.000000e+00 + br i1 %cond, label %if.then, label %if.else + +if.then: + ret i1 false + +if.else: + %ret = call i1 @llvm.is.fpclass.f32(float %x, i32 783) + ret i1 %ret +}