From 8a3efcd40b48543d5b77ff9d6e0d1950847e824e Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Mon, 19 Dec 2022 11:47:51 +0000 Subject: [PATCH] [ValueTracking] Consider single poison operands in propgatesPoison. This patch updates propgatesPoison to take a Use as argument and propagatesPoison now returns true if the passed in operand causes the user to yield poison if the operand is poison This allows propagating poison if the condition of a select is poison. This helps improve results for programUndefinedIfUndefOrPoison. Reviewed By: nikic Differential Revision: https://reviews.llvm.org/D111643 --- llvm/include/llvm/Analysis/ValueTracking.h | 16 ++- llvm/lib/Analysis/ScalarEvolution.cpp | 5 +- llvm/lib/Analysis/ValueTracking.cpp | 35 +++-- .../Transforms/Instrumentation/PoisonChecking.cpp | 7 +- llvm/lib/Transforms/Scalar/IndVarSimplify.cpp | 4 +- .../ScalarEvolution/exit-count-select-safe.ll | 36 +++--- .../Analysis/ScalarEvolution/exit-count-select.ll | 4 +- llvm/test/Analysis/ScalarEvolution/nsw.ll | 2 +- .../Instrumentation/PoisonChecking/ub-checks.ll | 2 + llvm/unittests/Analysis/ValueTrackingTest.cpp | 143 ++++++++++++--------- 10 files changed, 141 insertions(+), 113 deletions(-) diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h index e5eb505..2419b89 100644 --- a/llvm/include/llvm/Analysis/ValueTracking.h +++ b/llvm/include/llvm/Analysis/ValueTracking.h @@ -603,20 +603,22 @@ bool isGuaranteedToTransferExecutionToSuccessor( bool isGuaranteedToExecuteForEveryIteration(const Instruction *I, const Loop *L); -/// Return true if I yields poison or raises UB if any of its operands is -/// poison. -/// Formally, given I = `r = op v1 v2 .. vN`, propagatesPoison returns true -/// if, for all i, r is evaluated to poison or op raises UB if vi = poison. -/// If vi is a vector or an aggregate and r is a single value, any poison -/// element in vi should make r poison or raise UB. +/// Return true if \p PoisonOp's user yields poison or raises UB if its +/// operand \p PoisonOp is poison. +/// +/// If \p PoisonOp is a vector or an aggregate and the operation's result is a +/// single value, any poison element in /p PoisonOp should make the result +/// poison or raise UB. +/// /// To filter out operands that raise UB on poison, you can use /// getGuaranteedNonPoisonOp. -bool propagatesPoison(const Operator *I); +bool propagatesPoison(const Use &PoisonOp); /// Insert operands of I into Ops such that I will trigger undefined behavior /// if I is executed and that operand has a poison value. void getGuaranteedNonPoisonOps(const Instruction *I, SmallPtrSetImpl &Ops); + /// Insert operands of I into Ops such that I will trigger undefined behavior /// if I is executed and that operand is not a well-defined value /// (i.e. has undef bits or poison). diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index bcfb8a8..3c445aa 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -7294,8 +7294,9 @@ bool ScalarEvolution::isAddRecNeverPoison(const Instruction *I, const Loop *L) { while (!PoisonStack.empty() && !LatchControlDependentOnPoison) { const Instruction *Poison = PoisonStack.pop_back_val(); - for (const auto *PoisonUser : Poison->users()) { - if (propagatesPoison(cast(PoisonUser))) { + for (const Use &U : Poison->uses()) { + const User *PoisonUser = U.getUser(); + if (propagatesPoison(U)) { if (Pushed.insert(cast(PoisonUser)).second) PoisonStack.push_back(cast(PoisonUser)); } else if (auto *BI = dyn_cast(PoisonUser)) { diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index dbd5d3c..36fcec9 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -5330,15 +5330,12 @@ static bool directlyImpliesPoison(const Value *ValAssumedPoison, return false; if (const auto *I = dyn_cast(V)) { - if (propagatesPoison(cast(I))) - return any_of(I->operands(), [=](const Value *Op) { - return directlyImpliesPoison(ValAssumedPoison, Op, Depth + 1); - }); + if (any_of(I->operands(), [=](const Use &Op) { + return propagatesPoison(Op) && + directlyImpliesPoison(ValAssumedPoison, Op, Depth + 1); + })) + return true; - // 'select ValAssumedPoison, _, _' is poison. - if (const auto *SI = dyn_cast(I)) - return directlyImpliesPoison(ValAssumedPoison, SI->getCondition(), - Depth + 1); // V = extractvalue V0, idx // V2 = extractvalue V0, idx2 // V0's elements are all poison or not. (e.g., add_with_overflow) @@ -5496,7 +5493,8 @@ static bool isGuaranteedNotToBeUndefOrPoison(const Value *V, else if (PoisonOnly && isa(Cond)) { // For poison, we can analyze further auto *Opr = cast(Cond); - if (propagatesPoison(Opr) && is_contained(Opr->operand_values(), V)) + if (any_of(Opr->operands(), + [V](const Use &U) { return V == U && propagatesPoison(U); })) return true; } } @@ -5618,13 +5616,15 @@ bool llvm::isGuaranteedToExecuteForEveryIteration(const Instruction *I, llvm_unreachable("Instruction not contained in its own parent basic block."); } -bool llvm::propagatesPoison(const Operator *I) { +bool llvm::propagatesPoison(const Use &PoisonOp) { + const Operator *I = cast(PoisonOp.getUser()); switch (I->getOpcode()) { case Instruction::Freeze: - case Instruction::Select: case Instruction::PHI: case Instruction::Invoke: return false; + case Instruction::Select: + return PoisonOp.getOperandNo() == 0; case Instruction::Call: if (auto *II = dyn_cast(I)) { switch (II->getIntrinsicID()) { @@ -5805,14 +5805,11 @@ static bool programUndefinedIfUndefOrPoison(const Value *V, if (!isGuaranteedToTransferExecutionToSuccessor(&I)) return false; - // If this instruction propagates poison, mark it as poison if any of - // its operands are poison - if (propagatesPoison(cast(&I))) { - for (const Value *Op : I.operands()) { - if (YieldsPoison.count(Op)) { - YieldsPoison.insert(&I); - break; - } + // If an operand is poison and propagates it, mark I as yielding poison. + for (const Use &Op : I.operands()) { + if (YieldsPoison.count(Op) && propagatesPoison(Op)) { + YieldsPoison.insert(&I); + break; } } } diff --git a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp index e26c3b1..54dd20f 100644 --- a/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp +++ b/llvm/lib/Transforms/Instrumentation/PoisonChecking.cpp @@ -289,9 +289,10 @@ static bool rewrite(Function &F) { } SmallVector Checks; - if (propagatesPoison(cast(&I))) - for (Value *V : I.operands()) - Checks.push_back(getPoisonFor(ValToPoison, V)); + for (const Use &U : I.operands()) { + if (ValToPoison.count(U) && propagatesPoison(U)) + Checks.push_back(getPoisonFor(ValToPoison, U)); + } if (canCreatePoison(cast(&I))) generateCreationChecks(I, Checks); diff --git a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp index 55a22ae..7c8be1d 100644 --- a/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp +++ b/llvm/lib/Transforms/Scalar/IndVarSimplify.cpp @@ -788,7 +788,9 @@ static bool mustExecuteUBIfPoisonOnPathTo(Instruction *Root, // If we can't analyze propagation through this instruction, just skip it // and transitive users. Safe as false is a conservative result. - if (!propagatesPoison(cast(I)) && I != Root) + if (I != Root && !any_of(I->operands(), [&KnownPoison](const Use &U) { + return KnownPoison.contains(U) && propagatesPoison(U); + })) continue; if (KnownPoison.insert(I).second) diff --git a/llvm/test/Analysis/ScalarEvolution/exit-count-select-safe.ll b/llvm/test/Analysis/ScalarEvolution/exit-count-select-safe.ll index d95f26f..ad89bb5 100644 --- a/llvm/test/Analysis/ScalarEvolution/exit-count-select-safe.ll +++ b/llvm/test/Analysis/ScalarEvolution/exit-count-select-safe.ll @@ -139,9 +139,9 @@ define i32 @logical_or_3ops_duplicate(i32 %n, i32 %m, i32 %k) { ; CHECK-NEXT: %cond_p4 = select i1 %cond_p0, i1 true, i1 %cond_p1 ; CHECK-NEXT: --> (true + ((true + %cond_p0) umin_seq (true + %cond_p1))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: %cond_p5 = select i1 %cond_p4, i1 true, i1 %cond_p2 -; CHECK-NEXT: --> (true + ((true + %cond_p0) umin_seq (true + %cond_p1) umin_seq (true + %cond_p2))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (true + ((true + %cond_p0) umin_seq ((true + %cond_p1) umin (true + %cond_p2)))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: %cond = select i1 %cond_p5, i1 true, i1 %cond_p3 -; CHECK-NEXT: --> (true + ((true + %cond_p0) umin_seq (true + %cond_p1) umin_seq (true + %cond_p2) umin_seq (true + %cond_p3))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (true + ((true + %cond_p0) umin_seq ((true + %cond_p1) umin (true + %cond_p2)) umin_seq (true + %cond_p3))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_or_3ops_duplicate ; CHECK-NEXT: Loop %loop: backedge-taken count is (%n umin_seq %m umin_seq %k) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -432,7 +432,7 @@ define i32 @logical_and_2ops_and_constant(i32 %n, i32 %m, i32 %k) { ; CHECK-NEXT: %umin = call i32 @llvm.umin.i32(i32 %n, i32 42) ; CHECK-NEXT: --> (42 umin %n) U: [0,43) S: [0,43) Exits: (42 umin %n) LoopDispositions: { %loop: Invariant } ; CHECK-NEXT: %cond = select i1 %cond_p1, i1 true, i1 %cond_p0 -; CHECK-NEXT: --> (true + ((true + %cond_p1) umin_seq (true + %cond_p0))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (true + ((true + %cond_p1) umin (true + %cond_p0))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_2ops_and_constant ; CHECK-NEXT: Loop %loop: backedge-taken count is (42 umin %n) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is 42 @@ -574,7 +574,7 @@ define i32 @logical_and_implies_poison1(i32 %n) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + ((1 + %n) umin %n)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p0 umin %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_poison1 ; CHECK-NEXT: Loop %loop: backedge-taken count is ((1 + %n) umin %n) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -607,7 +607,7 @@ define i32 @logical_and_implies_poison2(i32 %n) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + ((1 + %n) umin %n)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p1 umin %cond_p0) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_poison2 ; CHECK-NEXT: Loop %loop: backedge-taken count is ((1 + %n) umin %n) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -640,7 +640,7 @@ define i32 @logical_and_implies_poison3(i32 %n, i32 %m) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + ((%n + %m) umin %n)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p1 umin %cond_p0) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_poison3 ; CHECK-NEXT: Loop %loop: backedge-taken count is ((%n + %m) umin %n) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -704,7 +704,7 @@ define i32 @logical_and_implies_poison_noundef(i32 %n, i32 noundef %m) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + (%n umin %m)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p0 umin %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_poison_noundef ; CHECK-NEXT: Loop %loop: backedge-taken count is (%n umin %m) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -768,7 +768,7 @@ define i32 @logical_and_implies_poison_complex1(i32 %n, i32 %m) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + ((%n + %m) umin (1 + %n + %m))) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p0 umin %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_poison_complex1 ; CHECK-NEXT: Loop %loop: backedge-taken count is ((%n + %m) umin (1 + %n + %m)) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -804,7 +804,7 @@ define i32 @logical_and_implies_poison_complex2(i32 %n, i32 %m, i32 %l) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + ((%n + %m) umin (%n + %m + %l))) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p0 umin %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_poison_complex2 ; CHECK-NEXT: Loop %loop: backedge-taken count is ((%n + %m) umin (%n + %m + %l)) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -874,9 +874,9 @@ define i32 @logical_and_implies_multiple_ops(i32 %n, i32 %m) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + (((1 + %n) umin %n) umin_seq %m)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p0 umin %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: %cond2 = select i1 %cond, i1 %cond_p2, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1 umin_seq %cond_p2) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> ((%cond_p0 umin %cond_p1) umin_seq %cond_p2) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_multiple_ops ; CHECK-NEXT: Loop %loop: backedge-taken count is (((1 + %n) umin %n) umin_seq %m) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 @@ -907,18 +907,18 @@ define i32 @logical_and_implies_multiple_ops2(i32 %n, i32 %m) { ; CHECK-NEXT: %add = add i32 %n, 1 ; CHECK-NEXT: --> (1 + %n) U: full-set S: full-set ; CHECK-NEXT: %i = phi i32 [ 0, %entry ], [ %i.next, %loop ] -; CHECK-NEXT: --> {0,+,1}<%loop> U: full-set S: full-set Exits: (%n umin_seq %m umin_seq (1 + %n)) LoopDispositions: { %loop: Computable } +; CHECK-NEXT: --> {0,+,1}<%loop> U: full-set S: full-set Exits: (%n umin_seq ((1 + %n) umin %m)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %i.next = add i32 %i, 1 -; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + (%n umin_seq %m umin_seq (1 + %n))) LoopDispositions: { %loop: Computable } +; CHECK-NEXT: --> {1,+,1}<%loop> U: full-set S: full-set Exits: (1 + (%n umin_seq ((1 + %n) umin %m))) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false ; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: %cond2 = select i1 %cond, i1 %cond_p2, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1 umin_seq %cond_p2) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p0 umin_seq (%cond_p1 umin %cond_p2)) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_implies_multiple_ops2 -; CHECK-NEXT: Loop %loop: backedge-taken count is (%n umin_seq %m umin_seq (1 + %n)) +; CHECK-NEXT: Loop %loop: backedge-taken count is (%n umin_seq ((1 + %n) umin %m)) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is -1 -; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (%n umin_seq %m umin_seq (1 + %n)) -; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (%n umin_seq %m umin_seq (1 + %n)) +; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (%n umin_seq ((1 + %n) umin %m)) +; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (%n umin_seq ((1 + %n) umin %m)) ; CHECK-NEXT: Predicates: ; CHECK: Loop %loop: Trip multiple is 1 ; @@ -1352,7 +1352,7 @@ define i32 @logical_and_zero_arg2(i32 %n) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,2) S: [1,2) Exits: 1 LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_p0, i1 %cond_p1, i1 false -; CHECK-NEXT: --> (%cond_p0 umin_seq %cond_p1) U: full-set S: full-set Exits: false LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_p1 umin %cond_p0) U: full-set S: full-set Exits: false LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_zero_arg2 ; CHECK-NEXT: Loop %loop: backedge-taken count is 0 ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is 0 diff --git a/llvm/test/Analysis/ScalarEvolution/exit-count-select.ll b/llvm/test/Analysis/ScalarEvolution/exit-count-select.ll index 7bdb7c6..6fbd7f2 100644 --- a/llvm/test/Analysis/ScalarEvolution/exit-count-select.ll +++ b/llvm/test/Analysis/ScalarEvolution/exit-count-select.ll @@ -11,7 +11,7 @@ define void @logical_and_m_const(i32 %n) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,4) S: [1,4) Exits: (1 + (2 umin %n)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_i, i1 %cond_i2, i1 false -; CHECK-NEXT: --> (%cond_i umin_seq %cond_i2) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (%cond_i2 umin %cond_i) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_and_m_const ; CHECK-NEXT: Loop %loop: backedge-taken count is (2 umin %n) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is 2 @@ -140,7 +140,7 @@ define void @logical_or_m_const(i32 %n) { ; CHECK-NEXT: %i.next = add i32 %i, 1 ; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,4) S: [1,4) Exits: (1 + (2 umin %n)) LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %cond = select i1 %cond_i, i1 true, i1 %cond_i2 -; CHECK-NEXT: --> (true + ((true + %cond_i) umin_seq (true + %cond_i2))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } +; CHECK-NEXT: --> (true + ((true + %cond_i) umin (true + %cond_i2))) U: full-set S: full-set Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: Determining loop execution counts for: @logical_or_m_const ; CHECK-NEXT: Loop %loop: backedge-taken count is (2 umin %n) ; CHECK-NEXT: Loop %loop: constant max backedge-taken count is 2 diff --git a/llvm/test/Analysis/ScalarEvolution/nsw.ll b/llvm/test/Analysis/ScalarEvolution/nsw.ll index e21f951..876b7ce 100644 --- a/llvm/test/Analysis/ScalarEvolution/nsw.ll +++ b/llvm/test/Analysis/ScalarEvolution/nsw.ll @@ -412,7 +412,7 @@ define void @select_cond_poison_propagation(ptr %p, i32 %x) nounwind { ; CHECK-NEXT: %iv = phi i32 [ %iv.next, %loop ], [ 0, %entry ] ; CHECK-NEXT: --> {0,+,1}<%loop> U: [0,-2147483648) S: [0,-2147483648) Exits: <> LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %iv.next = add nsw i32 %iv, 1 -; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,0) S: [1,0) Exits: <> LoopDispositions: { %loop: Computable } +; CHECK-NEXT: --> {1,+,1}<%loop> U: [1,-2147483648) S: [1,-2147483648) Exits: <> LoopDispositions: { %loop: Computable } ; CHECK-NEXT: %sel = select i1 %cmp, i32 10, i32 20 ; CHECK-NEXT: --> %sel U: [0,31) S: [0,31) Exits: <> LoopDispositions: { %loop: Variant } ; CHECK-NEXT: %cond = call i1 @cond() diff --git a/llvm/test/Instrumentation/PoisonChecking/ub-checks.ll b/llvm/test/Instrumentation/PoisonChecking/ub-checks.ll index a5703ae..9f9b16e 100644 --- a/llvm/test/Instrumentation/PoisonChecking/ub-checks.ll +++ b/llvm/test/Instrumentation/PoisonChecking/ub-checks.ll @@ -140,6 +140,8 @@ define noundef i32 @select_cond_may_be_poison(i32 %a, i32 %b) { ; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1 ; CHECK-NEXT: [[T:%.*]] = trunc i32 [[ADD]] to i1 ; CHECK-NEXT: [[SEL:%.*]] = select i1 [[T]], i32 [[ADD]], i32 [[B:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[TMP2]], true +; CHECK-NEXT: call void @__poison_checker_assert(i1 [[TMP3]]) ; CHECK-NEXT: ret i32 [[SEL]] ; %add = add nuw i32 %a, 1 diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp index 7e59841..ef13052 100644 --- a/llvm/unittests/Analysis/ValueTrackingTest.cpp +++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp @@ -847,68 +847,89 @@ TEST(ValueTracking, propagatesPoison) { "i1 %cond, i8* %p) {\n"; std::string AsmTail = " ret void\n}"; // (propagates poison?, IR instruction) - SmallVector, 32> Data = { - {true, "add i32 %x, %y"}, - {true, "add nsw nuw i32 %x, %y"}, - {true, "ashr i32 %x, %y"}, - {true, "lshr exact i32 %x, 31"}, - {true, "fadd float %fx, %fy"}, - {true, "fsub float %fx, %fy"}, - {true, "fmul float %fx, %fy"}, - {true, "fdiv float %fx, %fy"}, - {true, "frem float %fx, %fy"}, - {true, "fneg float %fx"}, - {true, "fcmp oeq float %fx, %fy"}, - {true, "icmp eq i32 %x, %y"}, - {true, "getelementptr i8, i8* %p, i32 %x"}, - {true, "getelementptr inbounds i8, i8* %p, i32 %x"}, - {true, "bitcast float %fx to i32"}, - {false, "select i1 %cond, i32 %x, i32 %y"}, - {false, "freeze i32 %x"}, - {true, "udiv i32 %x, %y"}, - {true, "urem i32 %x, %y"}, - {true, "sdiv exact i32 %x, %y"}, - {true, "srem i32 %x, %y"}, - {false, "call i32 @g(i32 %x)"}, - {true, "call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)"}, - {true, "call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)"}, - {true, "call {i32, i1} @llvm.smul.with.overflow.i32(i32 %x, i32 %y)"}, - {true, "call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)"}, - {true, "call {i32, i1} @llvm.usub.with.overflow.i32(i32 %x, i32 %y)"}, - {true, "call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %y)"}, - {false, "call float @llvm.sqrt.f32(float %fx)"}, - {false, "call float @llvm.powi.f32.i32(float %fx, i32 %x)"}, - {false, "call float @llvm.sin.f32(float %fx)"}, - {false, "call float @llvm.cos.f32(float %fx)"}, - {false, "call float @llvm.pow.f32(float %fx, float %fy)"}, - {false, "call float @llvm.exp.f32(float %fx)"}, - {false, "call float @llvm.exp2.f32(float %fx)"}, - {false, "call float @llvm.log.f32(float %fx)"}, - {false, "call float @llvm.log10.f32(float %fx)"}, - {false, "call float @llvm.log2.f32(float %fx)"}, - {false, "call float @llvm.fma.f32(float %fx, float %fx, float %fy)"}, - {false, "call float @llvm.fabs.f32(float %fx)"}, - {false, "call float @llvm.minnum.f32(float %fx, float %fy)"}, - {false, "call float @llvm.maxnum.f32(float %fx, float %fy)"}, - {false, "call float @llvm.minimum.f32(float %fx, float %fy)"}, - {false, "call float @llvm.maximum.f32(float %fx, float %fy)"}, - {false, "call float @llvm.copysign.f32(float %fx, float %fy)"}, - {false, "call float @llvm.floor.f32(float %fx)"}, - {false, "call float @llvm.ceil.f32(float %fx)"}, - {false, "call float @llvm.trunc.f32(float %fx)"}, - {false, "call float @llvm.rint.f32(float %fx)"}, - {false, "call float @llvm.nearbyint.f32(float %fx)"}, - {false, "call float @llvm.round.f32(float %fx)"}, - {false, "call float @llvm.roundeven.f32(float %fx)"}, - {false, "call i32 @llvm.lround.f32(float %fx)"}, - {false, "call i64 @llvm.llround.f32(float %fx)"}, - {false, "call i32 @llvm.lrint.f32(float %fx)"}, - {false, "call i64 @llvm.llrint.f32(float %fx)"}, - {false, "call float @llvm.fmuladd.f32(float %fx, float %fx, float %fy)"}}; + SmallVector, 32> Data = { + {true, "add i32 %x, %y", 0}, + {true, "add i32 %x, %y", 1}, + {true, "add nsw nuw i32 %x, %y", 0}, + {true, "add nsw nuw i32 %x, %y", 1}, + {true, "ashr i32 %x, %y", 0}, + {true, "ashr i32 %x, %y", 1}, + {true, "lshr exact i32 %x, 31", 0}, + {true, "lshr exact i32 %x, 31", 1}, + {true, "fadd float %fx, %fy", 0}, + {true, "fadd float %fx, %fy", 1}, + {true, "fsub float %fx, %fy", 0}, + {true, "fsub float %fx, %fy", 1}, + {true, "fmul float %fx, %fy", 0}, + {true, "fmul float %fx, %fy", 1}, + {true, "fdiv float %fx, %fy", 0}, + {true, "fdiv float %fx, %fy", 1}, + {true, "frem float %fx, %fy", 0}, + {true, "frem float %fx, %fy", 1}, + {true, "fneg float %fx", 0}, + {true, "fcmp oeq float %fx, %fy", 0}, + {true, "fcmp oeq float %fx, %fy", 1}, + {true, "icmp eq i32 %x, %y", 0}, + {true, "icmp eq i32 %x, %y", 1}, + {true, "getelementptr i8, i8* %p, i32 %x", 0}, + {true, "getelementptr i8, i8* %p, i32 %x", 1}, + {true, "getelementptr inbounds i8, i8* %p, i32 %x", 0}, + {true, "getelementptr inbounds i8, i8* %p, i32 %x", 1}, + {true, "bitcast float %fx to i32", 0}, + {true, "select i1 %cond, i32 %x, i32 %y", 0}, + {false, "select i1 %cond, i32 %x, i32 %y", 1}, + {false, "select i1 %cond, i32 %x, i32 %y", 2}, + {false, "freeze i32 %x", 0}, + {true, "udiv i32 %x, %y", 0}, + {true, "udiv i32 %x, %y", 1}, + {true, "urem i32 %x, %y", 0}, + {true, "urem i32 %x, %y", 1}, + {true, "sdiv exact i32 %x, %y", 0}, + {true, "sdiv exact i32 %x, %y", 1}, + {true, "srem i32 %x, %y", 0}, + {true, "srem i32 %x, %y", 1}, + {false, "call i32 @g(i32 %x)", 0}, + {false, "call i32 @g(i32 %x)", 1}, + {true, "call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %x, i32 %y)", 0}, + {true, "call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %x, i32 %y)", 0}, + {true, "call {i32, i1} @llvm.smul.with.overflow.i32(i32 %x, i32 %y)", 0}, + {true, "call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %x, i32 %y)", 0}, + {true, "call {i32, i1} @llvm.usub.with.overflow.i32(i32 %x, i32 %y)", 0}, + {true, "call {i32, i1} @llvm.umul.with.overflow.i32(i32 %x, i32 %y)", 0}, + {false, "call float @llvm.sqrt.f32(float %fx)", 0}, + {false, "call float @llvm.powi.f32.i32(float %fx, i32 %x)", 0}, + {false, "call float @llvm.sin.f32(float %fx)", 0}, + {false, "call float @llvm.cos.f32(float %fx)", 0}, + {false, "call float @llvm.pow.f32(float %fx, float %fy)", 0}, + {false, "call float @llvm.exp.f32(float %fx)", 0}, + {false, "call float @llvm.exp2.f32(float %fx)", 0}, + {false, "call float @llvm.log.f32(float %fx)", 0}, + {false, "call float @llvm.log10.f32(float %fx)", 0}, + {false, "call float @llvm.log2.f32(float %fx)", 0}, + {false, "call float @llvm.fma.f32(float %fx, float %fx, float %fy)", 0}, + {false, "call float @llvm.fabs.f32(float %fx)", 0}, + {false, "call float @llvm.minnum.f32(float %fx, float %fy)", 0}, + {false, "call float @llvm.maxnum.f32(float %fx, float %fy)", 0}, + {false, "call float @llvm.minimum.f32(float %fx, float %fy)", 0}, + {false, "call float @llvm.maximum.f32(float %fx, float %fy)", 0}, + {false, "call float @llvm.copysign.f32(float %fx, float %fy)", 0}, + {false, "call float @llvm.floor.f32(float %fx)", 0}, + {false, "call float @llvm.ceil.f32(float %fx)", 0}, + {false, "call float @llvm.trunc.f32(float %fx)", 0}, + {false, "call float @llvm.rint.f32(float %fx)", 0}, + {false, "call float @llvm.nearbyint.f32(float %fx)", 0}, + {false, "call float @llvm.round.f32(float %fx)", 0}, + {false, "call float @llvm.roundeven.f32(float %fx)", 0}, + {false, "call i32 @llvm.lround.f32(float %fx)", 0}, + {false, "call i64 @llvm.llround.f32(float %fx)", 0}, + {false, "call i32 @llvm.lrint.f32(float %fx)", 0}, + {false, "call i64 @llvm.llrint.f32(float %fx)", 0}, + {false, "call float @llvm.fmuladd.f32(float %fx, float %fx, float %fy)", + 0}}; std::string AssemblyStr = AsmHead; for (auto &Itm : Data) - AssemblyStr += Itm.second + "\n"; + AssemblyStr += std::get<1>(Itm) + "\n"; AssemblyStr += AsmTail; LLVMContext Context; @@ -925,7 +946,9 @@ TEST(ValueTracking, propagatesPoison) { for (auto &I : BB) { if (isa(&I)) break; - EXPECT_EQ(propagatesPoison(cast(&I)), Data[Index].first) + bool ExpectedVal = std::get<0>(Data[Index]); + unsigned OpIdx = std::get<2>(Data[Index]); + EXPECT_EQ(propagatesPoison(I.getOperandUse(OpIdx)), ExpectedVal) << "Incorrect answer at instruction " << Index << " = " << I; Index++; } -- 2.7.4