[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
This commit is contained in:
Nikita Popov 2020-12-22 21:20:56 +01:00
parent fb77d95022
commit 0af42d3dc7
3 changed files with 62 additions and 16 deletions

View File

@ -2387,6 +2387,58 @@ inline VScaleVal_match m_VScale(const DataLayout &DL) {
return VScaleVal_match(DL);
}
template <typename LHS, typename RHS, unsigned Opcode>
struct LogicalOp_match {
LHS L;
RHS R;
LogicalOp_match(const LHS &L, const RHS &R) : L(L), R(R) {}
template <typename T> bool match(T *V) {
if (auto *I = dyn_cast<Instruction>(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<SelectInst>(I)) {
if (Opcode == Instruction::And) {
if (const auto *C = dyn_cast<Constant>(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<Constant>(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 <typename LHS, typename RHS>
inline LogicalOp_match<LHS, RHS, Instruction::And>
m_LogicalAnd(const LHS &L, const RHS &R) {
return LogicalOp_match<LHS, RHS, Instruction::And>(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 <typename LHS, typename RHS>
inline LogicalOp_match<LHS, RHS, Instruction::Or>
m_LogicalOr(const LHS &L, const RHS &R) {
return LogicalOp_match<LHS, RHS, Instruction::Or>(L, R);
}
} // end namespace PatternMatch
} // end namespace llvm

View File

@ -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<BinaryOperator>(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

View File

@ -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