From 0af42d3dc73e8f08e37811131c31358ecb9adf20 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 22 Dec 2020 21:20:56 +0100 Subject: [PATCH] [PatternMatch][LVI] Handle select-form and/or in LVI Following the discussion in D93065, this adds m_LogicalAnd() and m_LogicalOr() matchers, that match A && B and A || B logical operations, either as bitwise operations or select expressions. As an example usage, LVI is adapted to use these matchers for its condition reasoning. The plan here is to switch other parts of LLVM that reason about and/or of conditions to also support the select forms, and then merge D93065 (or a variant thereof) to disable the poison-unsafe select to and/or transform. Differential Revision: https://reviews.llvm.org/D93827 --- llvm/include/llvm/IR/PatternMatch.h | 52 +++++++++++++++++++ llvm/lib/Analysis/LazyValueInfo.cpp | 14 +++-- .../CorrelatedValuePropagation/basic.ll | 12 ++--- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h index c40f5d1d15d1..166ad23de969 100644 --- a/llvm/include/llvm/IR/PatternMatch.h +++ b/llvm/include/llvm/IR/PatternMatch.h @@ -2387,6 +2387,58 @@ inline VScaleVal_match m_VScale(const DataLayout &DL) { return VScaleVal_match(DL); } +template +struct LogicalOp_match { + LHS L; + RHS R; + + LogicalOp_match(const LHS &L, const RHS &R) : L(L), R(R) {} + + template bool match(T *V) { + if (auto *I = dyn_cast(V)) { + if (!I->getType()->isIntOrIntVectorTy(1)) + return false; + + if (I->getOpcode() == Opcode && L.match(I->getOperand(0)) && + R.match(I->getOperand(1))) + return true; + + if (auto *SI = dyn_cast(I)) { + if (Opcode == Instruction::And) { + if (const auto *C = dyn_cast(SI->getFalseValue())) + if (C->isNullValue() && L.match(SI->getCondition()) && + R.match(SI->getTrueValue())) + return true; + } else { + assert(Opcode == Instruction::Or); + if (const auto *C = dyn_cast(SI->getTrueValue())) + if (C->isOneValue() && L.match(SI->getCondition()) && + R.match(SI->getFalseValue())) + return true; + } + } + } + + return false; + } +}; + +/// Matches L && R either in the form of L & R or L ? R : false. +/// Note that the latter form is poison-blocking. +template +inline LogicalOp_match +m_LogicalAnd(const LHS &L, const RHS &R) { + return LogicalOp_match(L, R); +} + +/// Matches L || R either in the form of L | R or L ? true : R. +/// Note that the latter form is poison-blocking. +template +inline LogicalOp_match +m_LogicalOr(const LHS &L, const RHS &R) { + return LogicalOp_match(L, R); +} + } // end namespace PatternMatch } // end namespace llvm diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp index a2f0bd872573..aadb90d372f4 100644 --- a/llvm/lib/Analysis/LazyValueInfo.cpp +++ b/llvm/lib/Analysis/LazyValueInfo.cpp @@ -1205,22 +1205,20 @@ getValueFromConditionImpl(Value *Val, Value *Cond, bool isTrueDest, // true dest path both of the conditions hold. Similarly for conditions of // the form (cond1 || cond2), we know that on the false dest path neither // condition holds. - BinaryOperator *BO = dyn_cast(Cond); - if (!BO || (isTrueDest && BO->getOpcode() != BinaryOperator::And) || - (!isTrueDest && BO->getOpcode() != BinaryOperator::Or)) + Value *L, *R; + if (isTrueDest ? !match(Cond, m_LogicalAnd(m_Value(L), m_Value(R))) + : !match(Cond, m_LogicalOr(m_Value(L), m_Value(R)))) return ValueLatticeElement::getOverdefined(); // Prevent infinite recursion if Cond references itself as in this example: // Cond: "%tmp4 = and i1 %tmp4, undef" // BL: "%tmp4 = and i1 %tmp4, undef" // BR: "i1 undef" - Value *BL = BO->getOperand(0); - Value *BR = BO->getOperand(1); - if (BL == Cond || BR == Cond) + if (L == Cond || R == Cond) return ValueLatticeElement::getOverdefined(); - return intersect(getValueFromCondition(Val, BL, isTrueDest, Visited), - getValueFromCondition(Val, BR, isTrueDest, Visited)); + return intersect(getValueFromCondition(Val, L, isTrueDest, Visited), + getValueFromCondition(Val, R, isTrueDest, Visited)); } static ValueLatticeElement diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/basic.ll b/llvm/test/Transforms/CorrelatedValuePropagation/basic.ll index e4748e71baa5..bec6f9431c9d 100644 --- a/llvm/test/Transforms/CorrelatedValuePropagation/basic.ll +++ b/llvm/test/Transforms/CorrelatedValuePropagation/basic.ll @@ -1069,10 +1069,8 @@ define void @select_and(i32 %a, i1* %p) { ; CHECK-NEXT: [[AND:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false ; CHECK-NEXT: br i1 [[AND]], label [[GUARD:%.*]], label [[EXIT:%.*]] ; CHECK: guard: -; CHECK-NEXT: [[C1:%.*]] = icmp sgt i32 [[A]], 20 -; CHECK-NEXT: store i1 [[C1]], i1* [[P:%.*]], align 1 -; CHECK-NEXT: [[C2:%.*]] = icmp slt i32 [[A]], -20 -; CHECK-NEXT: store i1 [[C2]], i1* [[P]], align 1 +; CHECK-NEXT: store i1 false, i1* [[P:%.*]], align 1 +; CHECK-NEXT: store i1 false, i1* [[P]], align 1 ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: ret void @@ -1168,10 +1166,8 @@ define void @select_or(i32 %a, i1* %p) { ; CHECK-NEXT: [[OR:%.*]] = select i1 [[CMP1]], i1 true, i1 [[CMP2]] ; CHECK-NEXT: br i1 [[OR]], label [[EXIT:%.*]], label [[GUARD:%.*]] ; CHECK: guard: -; CHECK-NEXT: [[C1:%.*]] = icmp sgt i32 [[A]], 20 -; CHECK-NEXT: store i1 [[C1]], i1* [[P:%.*]], align 1 -; CHECK-NEXT: [[C2:%.*]] = icmp slt i32 [[A]], -20 -; CHECK-NEXT: store i1 [[C2]], i1* [[P]], align 1 +; CHECK-NEXT: store i1 false, i1* [[P:%.*]], align 1 +; CHECK-NEXT: store i1 false, i1* [[P]], align 1 ; CHECK-NEXT: br label [[EXIT]] ; CHECK: exit: ; CHECK-NEXT: ret void