Skip to content

Commit cbfd0d6

Browse files
artagnonpfusik
andauthored
[HashRecognize] Rewrite arePHIsIntertwined (#144878)
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 PHINode arguments. There is also a fundamental correctness issue: the core functionality is to match a XOR that's a recurrence in both PHI nodes, ignoring casts, but the user of the XOR is never checked. Rewrite and rename the function. 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. Co-authored-by: Piotr Fusik <p.fusik@samsung.com>
1 parent 8dcdc0f commit cbfd0d6

File tree

3 files changed

+224
-35
lines changed

3 files changed

+224
-35
lines changed

llvm/include/llvm/IR/PatternMatch.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,6 +2119,13 @@ m_IntToPtr(const OpTy &Op) {
21192119
return CastOperator_match<OpTy, Instruction::IntToPtr>(Op);
21202120
}
21212121

2122+
/// Matches any cast or self. Used to ignore casts.
2123+
template <typename OpTy>
2124+
inline match_combine_or<CastInst_match<OpTy, CastInst>, OpTy>
2125+
m_CastOrSelf(const OpTy &Op) {
2126+
return m_CombineOr(CastInst_match<OpTy, CastInst>(Op), Op);
2127+
}
2128+
21222129
/// Matches Trunc.
21232130
template <typename OpTy>
21242131
inline CastInst_match<OpTy, TruncInst> m_Trunc(const OpTy &Op) {

llvm/lib/Analysis/HashRecognize.cpp

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -497,42 +497,46 @@ 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.
500+
/// Checks that \p P1 and \p P2 are used together in an XOR in the use-def chain
501+
/// of \p SI's condition, ignoring any casts. The purpose of this function is to
502+
/// ensure that LHSAux from the SimpleRecurrence is used correctly in the CRC
503+
/// computation. We cannot check the correctness of casts at this point, and
504+
/// rely on the KnownBits propagation to check correctness of the CRC
505+
/// computation.
506+
///
507+
/// In other words, it checks for the following pattern:
508+
///
509+
/// loop:
510+
/// %P1 = phi [_, %entry], [%P1.next, %loop]
511+
/// %P2 = phi [_, %entry], [%P2.next, %loop]
512+
/// ...
513+
/// %xor = xor (CastOrSelf %P1), (CastOrSelf %P2)
514+
///
515+
/// where %xor is in the use-def chain of \p SI's condition.
516+
static bool isConditionalOnXorOfPHIs(const SelectInst *SI, const PHINode *P1,
517+
const PHINode *P2, const Loop &L) {
508518
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-
}
514519

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-
}
520+
// matchConditionalRecurrence has already ensured that the SelectInst's
521+
// condition is an Instruction.
522+
Worklist.push_back(cast<Instruction>(SI->getCondition()));
522523

523524
while (!Worklist.empty()) {
524525
const Instruction *I = Worklist.pop_back_val();
525526

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).
527+
// Don't add a PHI's operands to the Worklist.
529528
if (isa<PHINode>(I))
530-
return I == Reference;
529+
continue;
530+
531+
// If we match an XOR of the two PHIs ignoring casts, we're done.
532+
if (match(I, m_c_Xor(m_CastOrSelf(m_Specific(P1)),
533+
m_CastOrSelf(m_Specific(P2)))))
534+
return true;
531535

536+
// Continue along the use-def chain.
532537
for (const Use &U : I->operands())
533538
if (auto *UI = dyn_cast<Instruction>(U))
534-
// Don't push Needle back onto the Worklist.
535-
if (UI != Needle && L.contains(UI))
539+
if (L.contains(UI))
536540
Worklist.push_back(UI);
537541
}
538542
return false;
@@ -586,9 +590,19 @@ HashRecognize::recognizeCRC() const {
586590
if (SimpleRecurrence) {
587591
if (isBigEndianBitShift(SimpleRecurrence.BO, SE) != ByteOrderSwapped)
588592
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";
593+
594+
// Ensure that the PHIs have exactly two uses:
595+
// the bit-shift, and the XOR (or a cast feeding into the XOR).
596+
if (!ConditionalRecurrence.Phi->hasNUses(2) ||
597+
!SimpleRecurrence.Phi->hasNUses(2))
598+
return "Recurrences have stray uses";
599+
600+
// Check that the SelectInst ConditionalRecurrence.Step is conditional on
601+
// the XOR of SimpleRecurrence.Phi and ConditionalRecurrence.Phi.
602+
if (!isConditionalOnXorOfPHIs(cast<SelectInst>(ConditionalRecurrence.Step),
603+
SimpleRecurrence.Phi,
604+
ConditionalRecurrence.Phi, L))
605+
return "Recurrences not intertwined with XOR";
592606
}
593607

594608
// 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: 174 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,34 @@ exit: ; preds = %loop
144144
ret i16 %crc.next
145145
}
146146

147+
define i8 @crc8.le.tc16(i16 %msg, i8 %checksum) {
148+
; CHECK-LABEL: 'crc8.le.tc16'
149+
; CHECK-NEXT: Did not find a hash algorithm
150+
; CHECK-NEXT: Reason: Loop iterations exceed bitwidth of result
151+
;
152+
entry:
153+
br label %loop
154+
155+
loop: ; preds = %loop, %entry
156+
%iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
157+
%crc = phi i8 [ %checksum, %entry ], [ %crc.next, %loop ]
158+
%data = phi i16 [ %msg, %entry ], [ %data.next, %loop ]
159+
%data.trunc = trunc i16 %data to i8
160+
%xor.crc.data = xor i8 %crc, %data.trunc
161+
%and.crc.data = and i8 %xor.crc.data, 1
162+
%data.next = lshr i16 %data, 1
163+
%check.sb = icmp eq i8 %and.crc.data, 0
164+
%crc.lshr = lshr i8 %crc, 1
165+
%crc.xor = xor i8 %crc.lshr, 29
166+
%crc.next = select i1 %check.sb, i8 %crc.lshr, i8 %crc.xor
167+
%iv.next = add nuw nsw i8 %iv, 1
168+
%exit.cond = icmp samesign ult i8 %iv, 15
169+
br i1 %exit.cond, label %loop, label %exit
170+
171+
exit: ; preds = %loop
172+
ret i8 %crc.next
173+
}
174+
147175
define i16 @crc16.be.tc8.crc.init.li(i16 %checksum, i8 %msg) {
148176
; CHECK-LABEL: 'crc16.be.tc8.crc.init.li'
149177
; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8
@@ -601,7 +629,7 @@ exit: ; preds = %loop
601629
define i16 @not.crc.wrong.sb.check.const(i8 %msg, i16 %checksum) {
602630
; CHECK-LABEL: 'not.crc.wrong.sb.check.const'
603631
; CHECK-NEXT: Did not find a hash algorithm
604-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
632+
; CHECK-NEXT: Reason: Bad RHS of significant-bit-check
605633
;
606634
entry:
607635
br label %loop
@@ -610,9 +638,8 @@ loop: ; preds = %loop, %entry
610638
%iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
611639
%data = phi i8 [ %msg, %entry ], [ %data.next, %loop ]
612640
%crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ]
613-
%crc.lshr = lshr i16 %crc, 8
614641
%data.ext = zext i8 %data to i16
615-
%xor.crc.data = xor i16 %crc.lshr, %data.ext
642+
%xor.crc.data = xor i16 %crc, %data.ext
616643
%check.sb = icmp samesign ult i16 %xor.crc.data, 128
617644
%crc.shl = shl i16 %crc, 1
618645
%crc.xor = xor i16 %crc.shl, 258
@@ -838,10 +865,37 @@ exit: ; preds = %loop
838865
ret i16 %crc.next
839866
}
840867

868+
define i16 @not.crc.bad.cast(i8 %msg, i16 %checksum) {
869+
; CHECK-LABEL: 'not.crc.bad.cast'
870+
; CHECK-NEXT: Did not find a hash algorithm
871+
; CHECK-NEXT: Reason: Expected bottom 8 bits zero (????????00001011)
872+
;
873+
entry:
874+
br label %loop
875+
876+
loop: ; preds = %loop, %entry
877+
%iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
878+
%data = phi i8 [ %msg, %entry ], [ %data.next, %loop ]
879+
%crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ]
880+
%data.ext = zext i8 %data to i16
881+
%xor.crc.data = xor i16 %crc, %data.ext
882+
%check.sb = icmp slt i16 %xor.crc.data, 0
883+
%crc.shl = shl i16 %crc, 1
884+
%crc.xor = xor i16 %crc.shl, 29
885+
%crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor
886+
%data.next = shl i8 %data, 1
887+
%iv.next = add nuw nsw i8 %iv, 1
888+
%exit.cond = icmp samesign ult i8 %iv, 7
889+
br i1 %exit.cond, label %loop, label %exit
890+
891+
exit: ; preds = %loop
892+
ret i16 %crc.next
893+
}
894+
841895
define i32 @not.crc.dead.msg.bad.use(i32 %checksum, i32 %msg) {
842896
; CHECK-LABEL: 'not.crc.dead.msg.bad.use'
843897
; CHECK-NEXT: Did not find a hash algorithm
844-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
898+
; CHECK-NEXT: Reason: Recurrences not intertwined with XOR
845899
;
846900
entry:
847901
br label %loop
@@ -869,7 +923,7 @@ exit: ; preds = %loop
869923
define i16 @not.crc.dead.msg.no.use(i8 %msg, i16 %checksum) {
870924
; CHECK-LABEL: 'not.crc.dead.msg.no.use'
871925
; CHECK-NEXT: Did not find a hash algorithm
872-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
926+
; CHECK-NEXT: Reason: Recurrences have stray uses
873927
;
874928
entry:
875929
br label %loop
@@ -898,7 +952,7 @@ exit: ; preds = %loop
898952
define i32 @not.crc.dead.msg.wrong.op(i32 %checksum, i32 %msg) {
899953
; CHECK-LABEL: 'not.crc.dead.msg.wrong.op'
900954
; CHECK-NEXT: Did not find a hash algorithm
901-
; CHECK-NEXT: Reason: Simple recurrence doesn't use conditional recurrence with XOR
955+
; CHECK-NEXT: Reason: Recurrences not intertwined with XOR
902956
;
903957
entry:
904958
br label %loop
@@ -922,6 +976,120 @@ exit: ; preds = %loop
922976
ret i32 %crc.next
923977
}
924978

979+
define i16 @not.crc.dead.msg.xor.notin.select.chain(i16 %msg, i16 %checksum) {
980+
; CHECK-LABEL: 'not.crc.dead.msg.xor.notin.select.chain'
981+
; CHECK-NEXT: Did not find a hash algorithm
982+
; CHECK-NEXT: Reason: Recurrences have stray uses
983+
;
984+
entry:
985+
br label %loop
986+
987+
loop: ; preds = %loop, %entry
988+
%iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
989+
%crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ]
990+
%data = phi i16 [ %msg, %entry ], [ %data.next, %loop ]
991+
%xor.crc.data = xor i16 %crc, %data
992+
%or.crc.data = or i16 %crc, %data
993+
%and.crc.data = and i16 %or.crc.data, 1
994+
%data.next = lshr i16 %data, 1
995+
%check.sb = icmp eq i16 %and.crc.data, 0
996+
%crc.lshr = lshr i16 %crc, 1
997+
%crc.xor = xor i16 %crc.lshr, -24575
998+
%crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor
999+
%iv.next = add nuw nsw i8 %iv, 1
1000+
%exit.cond = icmp samesign ult i8 %iv, 15
1001+
br i1 %exit.cond, label %loop, label %exit
1002+
1003+
exit: ; preds = %loop
1004+
ret i16 %crc.next
1005+
}
1006+
1007+
define i16 @not.crc.bad.xor.crc.data(i16 %msg, i16 %checksum) {
1008+
; CHECK-LABEL: 'not.crc.bad.xor.crc.data'
1009+
; CHECK-NEXT: Did not find a hash algorithm
1010+
; CHECK-NEXT: Reason: Recurrences have stray uses
1011+
;
1012+
entry:
1013+
br label %loop
1014+
1015+
loop: ; preds = %loop, %entry
1016+
%iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
1017+
%crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ]
1018+
%data = phi i16 [ %msg, %entry ], [ %data.next, %loop ]
1019+
%xor.crc.data = xor i16 %crc, %data
1020+
%mul.corrupt = mul i16 %xor.crc.data, 0
1021+
%xor.crc.data.corrupt = xor i16 %mul.corrupt, %crc
1022+
%and.crc.data = and i16 %xor.crc.data.corrupt, 1
1023+
%data.next = lshr i16 %data, 1
1024+
%check.sb = icmp eq i16 %and.crc.data, 0
1025+
%crc.lshr = lshr i16 %crc, 1
1026+
%crc.xor = xor i16 %crc.lshr, -24575
1027+
%crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor
1028+
%iv.next = add nuw nsw i8 %iv, 1
1029+
%exit.cond = icmp samesign ult i8 %iv, 15
1030+
br i1 %exit.cond, label %loop, label %exit
1031+
1032+
exit: ; preds = %loop
1033+
ret i16 %crc.next
1034+
}
1035+
1036+
define i16 @not.crc.dead.msg.or.zero(i16 %msg, i16 %checksum) {
1037+
; CHECK-LABEL: 'not.crc.dead.msg.or.zero'
1038+
; CHECK-NEXT: Did not find a hash algorithm
1039+
; CHECK-NEXT: Reason: Recurrences have stray uses
1040+
;
1041+
entry:
1042+
br label %loop
1043+
1044+
loop: ; preds = %loop, %entry
1045+
%iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
1046+
%crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ]
1047+
%data = phi i16 [ %msg, %entry ], [ %data.next, %loop ]
1048+
%xor.crc.data = xor i16 %crc, %data
1049+
%mul.corrupt = mul i16 %xor.crc.data, 0
1050+
%or.crc.data.corrupt = or i16 %mul.corrupt, %crc
1051+
%and.crc.data = and i16 %or.crc.data.corrupt, 1
1052+
%data.next = lshr i16 %data, 1
1053+
%check.sb = icmp eq i16 %and.crc.data, 0
1054+
%crc.lshr = lshr i16 %crc, 1
1055+
%crc.xor = xor i16 %crc.lshr, -24575
1056+
%crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor
1057+
%iv.next = add nuw nsw i8 %iv, 1
1058+
%exit.cond = icmp samesign ult i8 %iv, 15
1059+
br i1 %exit.cond, label %loop, label %exit
1060+
1061+
exit: ; preds = %loop
1062+
ret i16 %crc.next
1063+
}
1064+
1065+
define i16 @not.crc.unknown.value(i16 %msg, i16 %checksum, i16 %corrupt) {
1066+
; CHECK-LABEL: 'not.crc.unknown.value'
1067+
; CHECK-NEXT: Did not find a hash algorithm
1068+
; CHECK-NEXT: Reason: Unknown Value
1069+
;
1070+
entry:
1071+
br label %loop
1072+
1073+
loop: ; preds = %loop, %entry
1074+
%iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
1075+
%crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ]
1076+
%data = phi i16 [ %msg, %entry ], [ %data.next, %loop ]
1077+
%xor.crc.data = xor i16 %crc, %data
1078+
%xor.crc.data.corrupt = mul i16 %xor.crc.data, %corrupt
1079+
%and.crc.data = and i16 %xor.crc.data.corrupt, 1
1080+
%data.next = lshr i16 %data, 1
1081+
%check.sb = icmp eq i16 %and.crc.data, 0
1082+
%crc.lshr = lshr i16 %crc, 1
1083+
%crc.xor = xor i16 %crc.lshr, -24575
1084+
%crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor
1085+
%iv.next = add nuw nsw i8 %iv, 1
1086+
%exit.cond = icmp samesign ult i8 %iv, 15
1087+
br i1 %exit.cond, label %loop, label %exit
1088+
1089+
exit: ; preds = %loop
1090+
ret i16 %crc.next
1091+
}
1092+
9251093
define i16 @not.crc.float.simple.recurrence(float %msg, i16 %checksum) {
9261094
; CHECK-LABEL: 'not.crc.float.simple.recurrence'
9271095
; CHECK-NEXT: Did not find a hash algorithm

0 commit comments

Comments
 (0)