forked from OSchip/llvm-project
[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:
parent
fb77d95022
commit
0af42d3dc7
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue