Skip to content

Commit 201d906

Browse files
committed
[HashRecognize] Rewrite arePHIsIntertwined
The test crc8.le.tc16 is a valid CRC algorithm, but isn't recognized as such due to a buggy arePHIsIntertwined, which is asymmetric in its PHI node arguments. Another issue is that arePHIsIntertwined is unnecessarily complicated: the core functionality is to match a BinOp that's a recurrence in both PHI nodes, ignoring zext and trunc casts. Hence, rewrite it for clarity, and make it symmetric in both PHI operands. crc8.le.tc16 is still not recognized as a valid CRC algorithm, due to an incorrect check for loop iterations exceeding the bitwidth of the result: in reality, it should not exceed the bitwidth of LHSAux, but we leave this fix to a follow-up.
1 parent 05f587a commit 201d906

File tree

3 files changed

+42
-43
lines changed

3 files changed

+42
-43
lines changed

llvm/include/llvm/IR/PatternMatch.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,13 @@ m_ZExtOrSelf(const OpTy &Op) {
21602160
return m_CombineOr(m_ZExt(Op), Op);
21612161
}
21622162

2163+
template <typename OpTy>
2164+
inline match_combine_or<match_combine_or<CastInst_match<OpTy, ZExtInst>, OpTy>,
2165+
CastInst_match<OpTy, TruncInst>>
2166+
m_ZExtOrTruncOrSelf(const OpTy &Op) {
2167+
return m_CombineOr(m_ZExtOrSelf(Op), m_Trunc(Op));
2168+
}
2169+
21632170
template <typename OpTy>
21642171
inline match_combine_or<CastInst_match<OpTy, SExtInst>, OpTy>
21652172
m_SExtOrSelf(const OpTy &Op) {

llvm/lib/Analysis/HashRecognize.cpp

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -497,45 +497,37 @@ CRCTable HashRecognize::genSarwateTable(const APInt &GenPoly,
497497
return Table;
498498
}
499499

500-
/// Checks if \p Reference is reachable from \p Needle on the use-def chain, and
501-
/// that there are no stray PHI nodes while digging the use-def chain. \p
502-
/// BOToMatch is a CRC peculiarity: at least one of the Users of Needle needs to
503-
/// match this OpCode, which is XOR for CRC.
500+
/// Checks that \p Needle and \p Reference are used together in a
501+
/// BinaryOperator, which is a recurrence over both, ignoring zext and trunc.
502+
/// Additionally checks that the BinaryOperator has opcode \p BOToMatch, which
503+
/// is XOR in the case of CRC. In other words, it checks for the following
504+
/// pattern:
505+
///
506+
/// loop:
507+
/// %needle = phi [_, %entry], [%needle.next, %loop]
508+
/// %reference = phi [_, %entry], [%reference.next, %loop]
509+
/// ...
510+
/// _ = BOTOMatch ((trunc|zext|self) %needle) ((trunc|zext|self) %reference)
511+
///
504512
static bool arePHIsIntertwined(
505513
const PHINode *Needle, const PHINode *Reference, const Loop &L,
506514
Instruction::BinaryOps BOToMatch = Instruction::BinaryOpsEnd) {
507-
// Initialize the worklist with Users of the Needle.
508-
SmallVector<const Instruction *> Worklist;
509-
for (const User *U : Needle->users()) {
510-
if (auto *UI = dyn_cast<Instruction>(U))
511-
if (L.contains(UI))
512-
Worklist.push_back(UI);
513-
}
514-
515-
// BOToMatch is usually XOR for CRC.
516-
if (BOToMatch != Instruction::BinaryOpsEnd) {
517-
if (count_if(Worklist, [BOToMatch](const Instruction *I) {
518-
return I->getOpcode() == BOToMatch;
519-
}) != 1)
520-
return false;
521-
}
522-
523-
while (!Worklist.empty()) {
524-
const Instruction *I = Worklist.pop_back_val();
525-
526-
// Since Needle is never pushed onto the Worklist, I must either be the
527-
// Reference PHI node (in which case we're done), or a stray PHI node (in
528-
// which case we abort).
529-
if (isa<PHINode>(I))
530-
return I == Reference;
531-
532-
for (const Use &U : I->operands())
533-
if (auto *UI = dyn_cast<Instruction>(U))
534-
// Don't push Needle back onto the Worklist.
535-
if (UI != Needle && L.contains(UI))
536-
Worklist.push_back(UI);
537-
}
538-
return false;
515+
return any_of(ArrayRef({Needle, Reference}), [&](const PHINode *S) {
516+
return count_if(S->users(), [&](const User *U) {
517+
auto *BO = dyn_cast<BinaryOperator>(U);
518+
if (!BO)
519+
return false;
520+
if (BOToMatch != Instruction::BinaryOpsEnd &&
521+
BO->getOpcode() != BOToMatch)
522+
return false;
523+
return all_of(BO->operands(), [Needle, Reference](const Use &U) {
524+
return match(
525+
U.get(),
526+
m_CombineOr(m_ZExtOrTruncOrSelf(m_Specific(Reference)),
527+
m_ZExtOrTruncOrSelf(m_Specific(Needle))));
528+
});
529+
}) == 1;
530+
});
539531
}
540532

541533
// Recognizes a multiplication or division by the constant two, using SCEV. By
@@ -588,7 +580,7 @@ HashRecognize::recognizeCRC() const {
588580
return "Loop with non-unit bitshifts";
589581
if (!arePHIsIntertwined(SimpleRecurrence.Phi, ConditionalRecurrence.Phi, L,
590582
Instruction::BinaryOps::Xor))
591-
return "Simple recurrence doesn't use conditional recurrence with XOR";
583+
return "Recurrences not intertwined with XOR";
592584
}
593585

594586
// Make sure that the computed value is used in the exit block: this should be

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ exit: ; preds = %loop
147147
define i8 @crc8.le.tc16(i16 %msg, i8 %checksum) {
148148
; CHECK-LABEL: 'crc8.le.tc16'
149149
; CHECK-NEXT: Did not find a hash algorithm
150-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
150+
; CHECK-NEXT: Reason: Loop iterations exceed bitwidth of result
151151
;
152152
entry:
153153
br label %loop
@@ -629,7 +629,7 @@ exit: ; preds = %loop
629629
define i16 @not.crc.wrong.sb.check.const(i8 %msg, i16 %checksum) {
630630
; CHECK-LABEL: 'not.crc.wrong.sb.check.const'
631631
; CHECK-NEXT: Did not find a hash algorithm
632-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
632+
; CHECK-NEXT: Reason: Bad RHS of significant-bit-check
633633
;
634634
entry:
635635
br label %loop
@@ -868,7 +868,7 @@ exit: ; preds = %loop
868868
define i16 @not.crc.bad.cast(i8 %msg, i16 %checksum) {
869869
; CHECK-LABEL: 'not.crc.bad.cast'
870870
; CHECK-NEXT: Did not find a hash algorithm
871-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
871+
; CHECK-NEXT: Reason: Expected bottom 8 bits zero (????????00001011)
872872
;
873873
entry:
874874
br label %loop
@@ -895,7 +895,7 @@ exit: ; preds = %loop
895895
define i32 @not.crc.dead.msg.bad.use(i32 %checksum, i32 %msg) {
896896
; CHECK-LABEL: 'not.crc.dead.msg.bad.use'
897897
; CHECK-NEXT: Did not find a hash algorithm
898-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
898+
; CHECK-NEXT: Reason: Recurrences not intertwined with XOR
899899
;
900900
entry:
901901
br label %loop
@@ -923,7 +923,7 @@ exit: ; preds = %loop
923923
define i16 @not.crc.dead.msg.no.use(i8 %msg, i16 %checksum) {
924924
; CHECK-LABEL: 'not.crc.dead.msg.no.use'
925925
; CHECK-NEXT: Did not find a hash algorithm
926-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
926+
; CHECK-NEXT: Reason: Recurrences not intertwined with XOR
927927
;
928928
entry:
929929
br label %loop
@@ -952,7 +952,7 @@ exit: ; preds = %loop
952952
define i32 @not.crc.dead.msg.wrong.op(i32 %checksum, i32 %msg) {
953953
; CHECK-LABEL: 'not.crc.dead.msg.wrong.op'
954954
; CHECK-NEXT: Did not find a hash algorithm
955-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
955+
; CHECK-NEXT: Reason: Recurrences not intertwined with XOR
956956
;
957957
entry:
958958
br label %loop

0 commit comments

Comments
 (0)