Skip to content

Commit 22cf537

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 8467b98 commit 22cf537

File tree

2 files changed

+30
-47
lines changed

2 files changed

+30
-47
lines changed

llvm/lib/Analysis/HashRecognize.cpp

Lines changed: 24 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -497,45 +497,29 @@ 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.
504-
static bool arePHIsIntertwined(
505-
const PHINode *Needle, const PHINode *Reference, const Loop &L,
506-
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;
500+
/// A small helper for arePHIsIntertwined that is asymmetric in \p L and \p R,
501+
/// and hence needs to be called twice.
502+
static bool isXoredWith(const Value *L, const Value *R) {
503+
return count_if(L->users(), [L, R](const User *U) {
504+
return match(U, m_c_Xor(m_Specific(L),
505+
m_CombineOr(m_ZExtOrSelf(m_Specific(R)),
506+
m_Trunc(m_Specific(R)))));
507+
}) == 1;
508+
}
531509

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;
510+
/// Checks that \p L and \p R are used together in an XOR, which is a recurrence
511+
/// over both, ignoring zext and trunc. In other words, it checks for the
512+
/// following pattern:
513+
///
514+
/// loop:
515+
/// %L = phi [_, %entry], [%L.next, %loop]
516+
/// %R = phi [_, %entry], [%R.next, %loop]
517+
/// ...
518+
/// _ = xor ((trunc|zext|self) %L) ((trunc|zext|self) %R)
519+
///
520+
/// where at least one cast is self.
521+
static bool arePHIsIntertwined(const PHINode *L, const PHINode *R) {
522+
return isXoredWith(L, R) || isXoredWith(R, L);
539523
}
540524

541525
// Recognizes a multiplication or division by the constant two, using SCEV. By
@@ -586,9 +570,8 @@ HashRecognize::recognizeCRC() const {
586570
if (SimpleRecurrence) {
587571
if (isBigEndianBitShift(SimpleRecurrence.BO, SE) != ByteOrderSwapped)
588572
return "Loop with non-unit bitshifts";
589-
if (!arePHIsIntertwined(SimpleRecurrence.Phi, ConditionalRecurrence.Phi, L,
590-
Instruction::BinaryOps::Xor))
591-
return "Simple recurrence doesn't use conditional recurrence with XOR";
573+
if (!arePHIsIntertwined(SimpleRecurrence.Phi, ConditionalRecurrence.Phi))
574+
return "Recurrences not intertwined with XOR";
592575
}
593576

594577
// 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)