Skip to content

Commit 4e8df09

Browse files
committed
[HashRecognize] Match sb-check strongly
1 parent 0e78eae commit 4e8df09

File tree

2 files changed

+71
-73
lines changed

2 files changed

+71
-73
lines changed

llvm/lib/Analysis/HashRecognize.cpp

Lines changed: 63 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -73,60 +73,10 @@ using namespace SCEVPatternMatch;
7373

7474
#define DEBUG_TYPE "hash-recognize"
7575

76-
/// Check the well-formedness of the (most|least) significant bit check \p SI,
77-
/// where \p BitShift is the bit-shift branch: the other branch must be a
78-
/// bit-shift-and-xor-poly branch, as already checked by
79-
/// matchConditionalRecurrence. We check that the compare is `>= 0` in the
80-
/// big-endian case, and `== 0` in the little-endian case (or the inverse, in
81-
/// which case the branches of the compare are swapped). We check LCR against
82-
/// CheckLCR, which is full-set in the big-endian case, and [0, 2) in the
83-
/// little-endian case: CheckLCR checks that the comparison is `>= 0` in the
84-
/// big-endian case, and that the compare is to 0 or 1 in the little-endian case
85-
/// (as a value and'ed with 1 is passed as the operand). We then check
86-
/// AllowedByR against CheckAllowedByR, which is [0, smin) in the big-endian
87-
/// case, and [0, 1) in the little-endian case: CheckAllowedByR checks for
88-
/// significant-bit-clear, and this must be equal to \p BitShift for
89-
/// well-formedness.
90-
static bool isSignificantBitCheckWellFormed(const SelectInst *SI,
91-
const BinaryOperator *BitShift,
92-
bool ByteOrderSwapped) {
93-
DataLayout DL = SI->getParent()->getDataLayout();
94-
CmpPredicate Pred;
95-
Value *L, *R;
96-
Instruction *TV, *FV;
97-
[[maybe_unused]] bool Match =
98-
match(SI, m_Select(m_ICmp(Pred, m_Value(L), m_Value(R)),
99-
m_Instruction(TV), m_Instruction(FV)));
100-
assert(Match && "Select(ICmp()) expected in signficant-bit-check");
101-
102-
KnownBits KnownL = computeKnownBits(L, DL);
103-
unsigned ICmpBW = KnownL.getBitWidth();
104-
auto LCR = ConstantRange::fromKnownBits(KnownL, false);
105-
auto CheckLCR = ConstantRange::getNonEmpty(
106-
APInt::getZero(ICmpBW),
107-
ByteOrderSwapped ? APInt::getZero(ICmpBW) : APInt(ICmpBW, 2));
108-
if (LCR != CheckLCR)
109-
return false;
110-
111-
KnownBits KnownR = computeKnownBits(R, DL);
112-
auto RCR = ConstantRange::fromKnownBits(KnownR, false);
113-
auto AllowedByR = ConstantRange::makeAllowedICmpRegion(Pred, RCR);
114-
ConstantRange CheckAllowedByR(
115-
APInt::getZero(ICmpBW),
116-
ByteOrderSwapped ? APInt::getSignedMinValue(ICmpBW) : APInt(ICmpBW, 1));
117-
118-
if (AllowedByR == CheckAllowedByR)
119-
return TV == BitShift;
120-
if (AllowedByR.inverse() == CheckAllowedByR)
121-
return FV == BitShift;
122-
return false;
123-
}
124-
125-
/// Checks if Loop \p L contains instructions unreachable or unhandled from \p
126-
/// Roots on the use-def chain.
127-
static bool
128-
containsUnreachableOrUnhandled(const Loop &L,
129-
ArrayRef<const Instruction *> Roots) {
76+
/// Checks if Loop \p L contains instructions unreachable \p Roots on the
77+
/// use-def chain.
78+
static bool containsUnreachable(const Loop &L,
79+
ArrayRef<const Instruction *> Roots) {
13080
SmallPtrSet<const Instruction *, 16> Visited;
13181
BasicBlock *Latch = L.getLoopLatch();
13282

@@ -138,18 +88,13 @@ containsUnreachableOrUnhandled(const Loop &L,
13888
if (isa<PHINode>(I))
13989
continue;
14090

141-
if (!isa<TruncInst, BinaryOperator, SelectInst, CmpInst, BranchInst>(I))
142-
return true;
143-
14491
for (const Use &U : I->operands()) {
14592
if (auto *UI = dyn_cast<Instruction>(U)) {
14693
if (!L.contains(UI))
14794
return true;
14895
Worklist.push_back(UI);
14996
continue;
15097
}
151-
if (!isa<ConstantInt, BasicBlock>(U))
152-
return true;
15398
}
15499
}
155100
return std::distance(Latch->begin(), Latch->end()) != Visited.size();
@@ -204,6 +149,60 @@ struct RecurrenceInfo {
204149
Instruction::BinaryOps BOWithConstOpToMatch = Instruction::BinaryOpsEnd);
205150
};
206151

152+
/// Check the well-formedness of the (most|least) significant bit check given \p
153+
/// ConditionalRecurrence, \p SimpleRecurrence, depending on \p
154+
/// ByteOrderSwapped. We check that ConditionalRecurrence.Step is a
155+
/// Select(Cmp()) where the compare is `>= 0` in the big-endian case, and `== 0`
156+
/// in the little-endian case (or the inverse, in which case the branches of the
157+
/// compare are swapped). We check that the LHS is (ConditionalRecurrence.Phi
158+
/// [xor SimpleRecurrence.Phi]) in the big-endian case, and additionally check
159+
/// for an AND with one in the little-endian case. We then check AllowedByR
160+
/// against CheckAllowedByR, which is [0, smin) in the big-endian case, and [0,
161+
/// 1) in the little-endian case: CheckAllowedByR checks for
162+
/// significant-bit-clear, and this must be equal to ConditionalRecurrence.BO
163+
/// (which is the bit-shift, as already checked by isBigEndianBitShift) for
164+
/// well-formedness.
165+
static bool
166+
isSignificantBitCheckWellFormed(const RecurrenceInfo &ConditionalRecurrence,
167+
const RecurrenceInfo &SimpleRecurrence,
168+
bool ByteOrderSwapped) {
169+
auto *SI = cast<SelectInst>(ConditionalRecurrence.Step);
170+
DataLayout DL = SI->getParent()->getDataLayout();
171+
CmpPredicate Pred;
172+
const Value *L;
173+
const APInt *R;
174+
Instruction *TV, *FV;
175+
if (!match(SI, m_Select(m_ICmp(Pred, m_Value(L), m_APInt(R)),
176+
m_Instruction(TV), m_Instruction(FV))))
177+
return false;
178+
179+
// Match predicate with or without a SimpleRecurrence (the corresponding data
180+
// is LHSAux).
181+
auto MatchPred =
182+
m_CombineOr(m_Specific(ConditionalRecurrence.Phi),
183+
m_c_Xor(m_CastOrSelf(m_Specific(ConditionalRecurrence.Phi)),
184+
m_CastOrSelf(m_Specific(SimpleRecurrence.Phi))));
185+
bool LWellFormed = ByteOrderSwapped ? match(L, MatchPred)
186+
: match(L, m_c_And(MatchPred, m_One()));
187+
if (!LWellFormed)
188+
return false;
189+
190+
KnownBits KnownR = KnownBits::makeConstant(*R);
191+
unsigned BW = KnownR.getBitWidth();
192+
auto RCR = ConstantRange::fromKnownBits(KnownR, false);
193+
auto AllowedByR = ConstantRange::makeAllowedICmpRegion(Pred, RCR);
194+
ConstantRange CheckAllowedByR(APInt::getZero(BW),
195+
ByteOrderSwapped ? APInt::getSignedMinValue(BW)
196+
: APInt(BW, 1));
197+
198+
BinaryOperator *BitShift = ConditionalRecurrence.BO;
199+
if (AllowedByR == CheckAllowedByR)
200+
return TV == BitShift;
201+
if (AllowedByR.inverse() == CheckAllowedByR)
202+
return FV == BitShift;
203+
return false;
204+
}
205+
207206
/// Wraps llvm::matchSimpleRecurrence. Match a simple first order recurrence
208207
/// cycle of the form:
209208
///
@@ -375,7 +374,7 @@ CRCTable HashRecognize::genSarwateTable(const APInt &GenPoly,
375374
/// Checks that \p P1 and \p P2 are used together in an XOR in the use-def chain
376375
/// of \p SI's condition, ignoring any casts. The purpose of this function is to
377376
/// ensure that LHSAux from the SimpleRecurrence is used correctly in the CRC
378-
/// computation. We cannot check the correctness of casts at this point.
377+
/// computation.
379378
///
380379
/// In other words, it checks for the following pattern:
381380
///
@@ -497,9 +496,8 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
497496
"Expected ExtraConst in conditional recurrence");
498497
const APInt &GenPoly = *ConditionalRecurrence.ExtraConst;
499498

500-
if (!isSignificantBitCheckWellFormed(
501-
cast<SelectInst>(ConditionalRecurrence.Step),
502-
ConditionalRecurrence.BO, *ByteOrderSwapped))
499+
if (!isSignificantBitCheckWellFormed(ConditionalRecurrence, SimpleRecurrence,
500+
*ByteOrderSwapped))
503501
return "Malformed significant-bit check";
504502

505503
SmallVector<const Instruction *> Roots(
@@ -508,8 +506,8 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
508506
L.getLatchCmpInst(), Latch->getTerminator()});
509507
if (SimpleRecurrence)
510508
Roots.push_back(SimpleRecurrence.BO);
511-
if (containsUnreachableOrUnhandled(L, Roots))
512-
return "Found stray unvisited or unhandled instructions";
509+
if (containsUnreachable(L, Roots))
510+
return "Found stray unvisited instructions";
513511

514512
return PolynomialInfo(TC, LHS, GenPoly, ComputedValue, *ByteOrderSwapped,
515513
LHSAux);

llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,7 @@ exit: ; preds = %loop
11091109
define i16 @not.crc.unknown.value(i16 %msg, i16 %checksum, i16 %corrupt) {
11101110
; CHECK-LABEL: 'not.crc.unknown.value'
11111111
; CHECK-NEXT: Did not find a hash algorithm
1112-
; CHECK-NEXT: Reason: Found stray unvisited or unhandled instructions
1112+
; CHECK-NEXT: Reason: Malformed significant-bit check
11131113
;
11141114
entry:
11151115
br label %loop
@@ -1137,7 +1137,7 @@ exit: ; preds = %loop
11371137
define i16 @not.crc.unknown.call.outside.loop(i16 %msg, i16 %checksum) {
11381138
; CHECK-LABEL: 'not.crc.unknown.call.outside.loop'
11391139
; CHECK-NEXT: Did not find a hash algorithm
1140-
; CHECK-NEXT: Reason: Found stray unvisited or unhandled instructions
1140+
; CHECK-NEXT: Reason: Malformed significant-bit check
11411141
;
11421142
entry:
11431143
%corrupt = call i16 @side.effect()
@@ -1250,7 +1250,7 @@ exit: ; preds = %loop
12501250
define i16 @not.crc.stray.unvisited.call(i16 %crc.init) {
12511251
; CHECK-LABEL: 'not.crc.stray.unvisited.call'
12521252
; CHECK-NEXT: Did not find a hash algorithm
1253-
; CHECK-NEXT: Reason: Found stray unvisited or unhandled instructions
1253+
; CHECK-NEXT: Reason: Found stray unvisited instructions
12541254
;
12551255
entry:
12561256
br label %loop
@@ -1276,7 +1276,7 @@ declare void @print(i16)
12761276
define i16 @not.crc.call.sb.check(i16 %crc.init) {
12771277
; CHECK-LABEL: 'not.crc.call.sb.check'
12781278
; CHECK-NEXT: Did not find a hash algorithm
1279-
; CHECK-NEXT: Reason: Found stray unvisited or unhandled instructions
1279+
; CHECK-NEXT: Reason: Malformed significant-bit check
12801280
;
12811281
entry:
12821282
br label %loop
@@ -1320,8 +1320,8 @@ exit: ; preds = %loop
13201320
ret i16 %crc.next
13211321
}
13221322

1323-
define i16 @not.crc.knownbits.sb.check.fail(i16 %crc.init) {
1324-
; CHECK-LABEL: 'not.crc.knownbits.sb.check.fail'
1323+
define i16 @not.crc.sb.check.patternmatch.fail(i16 %crc.init) {
1324+
; CHECK-LABEL: 'not.crc.sb.check.patternmatch.fail'
13251325
; CHECK-NEXT: Did not find a hash algorithm
13261326
; CHECK-NEXT: Reason: Malformed significant-bit check
13271327
;
@@ -1346,8 +1346,8 @@ exit: ; preds = %loop
13461346
ret i16 %crc.next
13471347
}
13481348

1349-
define i16 @not.crc.knownbits.sb.check.fail.call.outside.loop(i16 %crc.init) {
1350-
; CHECK-LABEL: 'not.crc.knownbits.sb.check.fail.call.outside.loop'
1349+
define i16 @not.crc.sb.check.patternmatch.fail.call.outside.loop(i16 %crc.init) {
1350+
; CHECK-LABEL: 'not.crc.sb.check.patternmatch.fail.call.outside.loop'
13511351
; CHECK-NEXT: Did not find a hash algorithm
13521352
; CHECK-NEXT: Reason: Malformed significant-bit check
13531353
;

0 commit comments

Comments
 (0)