[llvm] [HashRecognize] Prevent values other than `ComputedValue` from exiting loop (PR #213080)
Sean Clarke via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 31 08:42:10 PDT 2026
https://github.com/xarkenz updated https://github.com/llvm/llvm-project/pull/213080
>From 5075ee799afe95a307bdb8d49627010cf38ef33a Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Thu, 30 Jul 2026 11:36:55 -0500
Subject: [PATCH 1/6] Add baseline tests
---
.../HashRecognize/cyclic-redundancy-check.ll | 94 +++++++++++++++++++
1 file changed, 94 insertions(+)
diff --git a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
index 1a17a82f1bff1..6585d31549049 100644
--- a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
+++ b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
@@ -1886,4 +1886,98 @@ exit:
ret i16 %ret
}
+define i16 @not.crc.crc.phi.outside.user.nodata(i16 %crc.init) {
+; CHECK-LABEL: 'not.crc.crc.phi.outside.user.nodata'
+; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8
+; CHECK-NEXT: Initial CRC: i16 %crc.init
+; CHECK-NEXT: Generating polynomial: 3
+; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl
+; CHECK-NEXT: Computed CRC lookup table:
+; CHECK-NEXT: 0 3 6 5 12 15 10 9 24 27 30 29 20 23 18 17
+; CHECK-NEXT: 48 51 54 53 60 63 58 57 40 43 46 45 36 39 34 33
+; CHECK-NEXT: 96 99 102 101 108 111 106 105 120 123 126 125 116 119 114 113
+; CHECK-NEXT: 80 83 86 85 92 95 90 89 72 75 78 77 68 71 66 65
+; CHECK-NEXT: 192 195 198 197 204 207 202 201 216 219 222 221 212 215 210 209
+; CHECK-NEXT: 240 243 246 245 252 255 250 249 232 235 238 237 228 231 226 225
+; CHECK-NEXT: 160 163 166 165 172 175 170 169 184 187 190 189 180 183 178 177
+; CHECK-NEXT: 144 147 150 149 156 159 154 153 136 139 142 141 132 135 130 129
+; CHECK-NEXT: 384 387 390 389 396 399 394 393 408 411 414 413 404 407 402 401
+; CHECK-NEXT: 432 435 438 437 444 447 442 441 424 427 430 429 420 423 418 417
+; CHECK-NEXT: 480 483 486 485 492 495 490 489 504 507 510 509 500 503 498 497
+; CHECK-NEXT: 464 467 470 469 476 479 474 473 456 459 462 461 452 455 450 449
+; CHECK-NEXT: 320 323 326 325 332 335 330 329 344 347 350 349 340 343 338 337
+; CHECK-NEXT: 368 371 374 373 380 383 378 377 360 363 366 365 356 359 354 353
+; CHECK-NEXT: 288 291 294 293 300 303 298 297 312 315 318 317 308 311 306 305
+; CHECK-NEXT: 272 275 278 277 284 287 282 281 264 267 270 269 260 263 258 257
+; CHECK-NEXT: Computed CRC Barrett constants:
+; CHECK-NEXT: Mu = 256, FullGenPoly = 65539
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ]
+ %crc.shl = shl i16 %crc, 1
+ %crc.xor = xor i16 %crc.shl, 3
+ %check.sb = icmp slt i16 %crc, 0
+ %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl
+ %iv.next = add nuw nsw i32 %iv, 1
+ %exit.cond = icmp samesign ult i32 %iv, 7
+ br i1 %exit.cond, label %loop, label %exit
+
+exit:
+ %ret = xor i16 %crc, %crc.next
+ ret i16 %ret
+}
+
+define i16 @not.crc.iv.phi.outside.user(i16 %crc.init, i16 %data.init) {
+; CHECK-LABEL: 'not.crc.iv.phi.outside.user'
+; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8
+; CHECK-NEXT: Initial CRC: i16 %crc.init
+; CHECK-NEXT: Generating polynomial: 3
+; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl
+; CHECK-NEXT: Auxiliary data: i16 %data.init
+; CHECK-NEXT: Computed CRC lookup table:
+; CHECK-NEXT: 0 3 6 5 12 15 10 9 24 27 30 29 20 23 18 17
+; CHECK-NEXT: 48 51 54 53 60 63 58 57 40 43 46 45 36 39 34 33
+; CHECK-NEXT: 96 99 102 101 108 111 106 105 120 123 126 125 116 119 114 113
+; CHECK-NEXT: 80 83 86 85 92 95 90 89 72 75 78 77 68 71 66 65
+; CHECK-NEXT: 192 195 198 197 204 207 202 201 216 219 222 221 212 215 210 209
+; CHECK-NEXT: 240 243 246 245 252 255 250 249 232 235 238 237 228 231 226 225
+; CHECK-NEXT: 160 163 166 165 172 175 170 169 184 187 190 189 180 183 178 177
+; CHECK-NEXT: 144 147 150 149 156 159 154 153 136 139 142 141 132 135 130 129
+; CHECK-NEXT: 384 387 390 389 396 399 394 393 408 411 414 413 404 407 402 401
+; CHECK-NEXT: 432 435 438 437 444 447 442 441 424 427 430 429 420 423 418 417
+; CHECK-NEXT: 480 483 486 485 492 495 490 489 504 507 510 509 500 503 498 497
+; CHECK-NEXT: 464 467 470 469 476 479 474 473 456 459 462 461 452 455 450 449
+; CHECK-NEXT: 320 323 326 325 332 335 330 329 344 347 350 349 340 343 338 337
+; CHECK-NEXT: 368 371 374 373 380 383 378 377 360 363 366 365 356 359 354 353
+; CHECK-NEXT: 288 291 294 293 300 303 298 297 312 315 318 317 308 311 306 305
+; CHECK-NEXT: 272 275 278 277 284 287 282 281 264 267 270 269 260 263 258 257
+; CHECK-NEXT: Computed CRC Barrett constants:
+; CHECK-NEXT: Mu = 256, FullGenPoly = 65539
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i16 [ 0, %entry ], [ %iv.next, %loop ]
+ %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ]
+ %data = phi i16 [ %data.init, %entry ], [ %data.next, %loop ]
+ %xor.crc.data = xor i16 %data, %crc
+ %crc.shl = shl i16 %crc, 1
+ %crc.xor = xor i16 %crc.shl, 3
+ %check.sb = icmp slt i16 %xor.crc.data, 0
+ %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl
+ %data.next = shl i16 %data, 1
+ %iv.next = add nuw nsw i16 %iv, 1
+ %exit.cond = icmp samesign ult i16 %iv, 7
+ br i1 %exit.cond, label %loop, label %exit
+
+exit:
+ %ret = xor i16 %iv, %crc.next
+ ret i16 %ret
+}
+
declare i16 @side.effect()
>From 607cee2749499f2f939c035ef914bc5bc4836813 Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Thu, 30 Jul 2026 12:20:00 -0500
Subject: [PATCH 2/6] Add check for extraneous incoming values in exit block
---
llvm/lib/Analysis/HashRecognize.cpp | 10 ++++
.../HashRecognize/cyclic-redundancy-check.ll | 51 ++-----------------
2 files changed, 14 insertions(+), 47 deletions(-)
diff --git a/llvm/lib/Analysis/HashRecognize.cpp b/llvm/lib/Analysis/HashRecognize.cpp
index ade4be565ee11..c73bea4d1930e 100644
--- a/llvm/lib/Analysis/HashRecognize.cpp
+++ b/llvm/lib/Analysis/HashRecognize.cpp
@@ -605,6 +605,16 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
if (containsUnreachable(L, Roots))
return "Found stray unvisited instructions";
+ // Ensure nothing other than the computed value makes its way out of the loop.
+ // Since the loop is in LCSSA form, this is as simple as checking the PHI
+ // nodes in the exit block.
+ for (PHINode &PN : L.getExitBlock()->phis()) {
+ for (auto &Incoming : PN.incoming_values()) {
+ if (Incoming.get() != ComputedValue)
+ return "Found stray incoming values in loop exit block";
+ }
+ }
+
return PolynomialInfo(TC, LHS, GenPoly, ComputedValue, *IsBigEndian, LHSAux);
}
diff --git a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
index 6585d31549049..a6823f61af75d 100644
--- a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
+++ b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
@@ -1888,29 +1888,8 @@ exit:
define i16 @not.crc.crc.phi.outside.user.nodata(i16 %crc.init) {
; CHECK-LABEL: 'not.crc.crc.phi.outside.user.nodata'
-; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8
-; CHECK-NEXT: Initial CRC: i16 %crc.init
-; CHECK-NEXT: Generating polynomial: 3
-; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl
-; CHECK-NEXT: Computed CRC lookup table:
-; CHECK-NEXT: 0 3 6 5 12 15 10 9 24 27 30 29 20 23 18 17
-; CHECK-NEXT: 48 51 54 53 60 63 58 57 40 43 46 45 36 39 34 33
-; CHECK-NEXT: 96 99 102 101 108 111 106 105 120 123 126 125 116 119 114 113
-; CHECK-NEXT: 80 83 86 85 92 95 90 89 72 75 78 77 68 71 66 65
-; CHECK-NEXT: 192 195 198 197 204 207 202 201 216 219 222 221 212 215 210 209
-; CHECK-NEXT: 240 243 246 245 252 255 250 249 232 235 238 237 228 231 226 225
-; CHECK-NEXT: 160 163 166 165 172 175 170 169 184 187 190 189 180 183 178 177
-; CHECK-NEXT: 144 147 150 149 156 159 154 153 136 139 142 141 132 135 130 129
-; CHECK-NEXT: 384 387 390 389 396 399 394 393 408 411 414 413 404 407 402 401
-; CHECK-NEXT: 432 435 438 437 444 447 442 441 424 427 430 429 420 423 418 417
-; CHECK-NEXT: 480 483 486 485 492 495 490 489 504 507 510 509 500 503 498 497
-; CHECK-NEXT: 464 467 470 469 476 479 474 473 456 459 462 461 452 455 450 449
-; CHECK-NEXT: 320 323 326 325 332 335 330 329 344 347 350 349 340 343 338 337
-; CHECK-NEXT: 368 371 374 373 380 383 378 377 360 363 366 365 356 359 354 353
-; CHECK-NEXT: 288 291 294 293 300 303 298 297 312 315 318 317 308 311 306 305
-; CHECK-NEXT: 272 275 278 277 284 287 282 281 264 267 270 269 260 263 258 257
-; CHECK-NEXT: Computed CRC Barrett constants:
-; CHECK-NEXT: Mu = 256, FullGenPoly = 65539
+; CHECK-NEXT: Did not find a hash algorithm
+; CHECK-NEXT: Reason: Found stray incoming values in loop exit block
;
entry:
br label %loop
@@ -1933,30 +1912,8 @@ exit:
define i16 @not.crc.iv.phi.outside.user(i16 %crc.init, i16 %data.init) {
; CHECK-LABEL: 'not.crc.iv.phi.outside.user'
-; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8
-; CHECK-NEXT: Initial CRC: i16 %crc.init
-; CHECK-NEXT: Generating polynomial: 3
-; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl
-; CHECK-NEXT: Auxiliary data: i16 %data.init
-; CHECK-NEXT: Computed CRC lookup table:
-; CHECK-NEXT: 0 3 6 5 12 15 10 9 24 27 30 29 20 23 18 17
-; CHECK-NEXT: 48 51 54 53 60 63 58 57 40 43 46 45 36 39 34 33
-; CHECK-NEXT: 96 99 102 101 108 111 106 105 120 123 126 125 116 119 114 113
-; CHECK-NEXT: 80 83 86 85 92 95 90 89 72 75 78 77 68 71 66 65
-; CHECK-NEXT: 192 195 198 197 204 207 202 201 216 219 222 221 212 215 210 209
-; CHECK-NEXT: 240 243 246 245 252 255 250 249 232 235 238 237 228 231 226 225
-; CHECK-NEXT: 160 163 166 165 172 175 170 169 184 187 190 189 180 183 178 177
-; CHECK-NEXT: 144 147 150 149 156 159 154 153 136 139 142 141 132 135 130 129
-; CHECK-NEXT: 384 387 390 389 396 399 394 393 408 411 414 413 404 407 402 401
-; CHECK-NEXT: 432 435 438 437 444 447 442 441 424 427 430 429 420 423 418 417
-; CHECK-NEXT: 480 483 486 485 492 495 490 489 504 507 510 509 500 503 498 497
-; CHECK-NEXT: 464 467 470 469 476 479 474 473 456 459 462 461 452 455 450 449
-; CHECK-NEXT: 320 323 326 325 332 335 330 329 344 347 350 349 340 343 338 337
-; CHECK-NEXT: 368 371 374 373 380 383 378 377 360 363 366 365 356 359 354 353
-; CHECK-NEXT: 288 291 294 293 300 303 298 297 312 315 318 317 308 311 306 305
-; CHECK-NEXT: 272 275 278 277 284 287 282 281 264 267 270 269 260 263 258 257
-; CHECK-NEXT: Computed CRC Barrett constants:
-; CHECK-NEXT: Mu = 256, FullGenPoly = 65539
+; CHECK-NEXT: Did not find a hash algorithm
+; CHECK-NEXT: Reason: Found stray incoming values in loop exit block
;
entry:
br label %loop
>From 101fe6cc4b5ca8d56c29f69b059ef765f27581e8 Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Thu, 30 Jul 2026 14:21:40 -0500
Subject: [PATCH 3/6] Check only incoming value from latch block
---
llvm/lib/Analysis/HashRecognize.cpp | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Analysis/HashRecognize.cpp b/llvm/lib/Analysis/HashRecognize.cpp
index c73bea4d1930e..3506c7834d002 100644
--- a/llvm/lib/Analysis/HashRecognize.cpp
+++ b/llvm/lib/Analysis/HashRecognize.cpp
@@ -588,6 +588,14 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
}))
return "Unable to find use of computed value in loop exit block";
+ // Ensure nothing other than the computed value makes its way out of the loop.
+ // Since the loop is in LCSSA form, this is as simple as checking the PHI
+ // nodes in the exit block.
+ if (any_of(Exit->phis(), [Latch, ComputedValue](PHINode &PN) {
+ return PN.getIncomingValueForBlock(Latch) != ComputedValue;
+ }))
+ return "Found stray incoming values in loop exit block";
+
assert(ConditionalRecurrence.ExtraConst &&
"Expected ExtraConst in conditional recurrence");
const APInt &GenPoly = *ConditionalRecurrence.ExtraConst;
@@ -605,16 +613,6 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
if (containsUnreachable(L, Roots))
return "Found stray unvisited instructions";
- // Ensure nothing other than the computed value makes its way out of the loop.
- // Since the loop is in LCSSA form, this is as simple as checking the PHI
- // nodes in the exit block.
- for (PHINode &PN : L.getExitBlock()->phis()) {
- for (auto &Incoming : PN.incoming_values()) {
- if (Incoming.get() != ComputedValue)
- return "Found stray incoming values in loop exit block";
- }
- }
-
return PolynomialInfo(TC, LHS, GenPoly, ComputedValue, *IsBigEndian, LHSAux);
}
>From 7d317ff454f4bde59acd5388600d366715ee1213 Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Thu, 30 Jul 2026 15:33:35 -0500
Subject: [PATCH 4/6] Simplify computed value use check
---
llvm/lib/Analysis/HashRecognize.cpp | 5 +----
llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll | 4 ++--
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Analysis/HashRecognize.cpp b/llvm/lib/Analysis/HashRecognize.cpp
index 3506c7834d002..ead1e715a0c25 100644
--- a/llvm/lib/Analysis/HashRecognize.cpp
+++ b/llvm/lib/Analysis/HashRecognize.cpp
@@ -582,10 +582,7 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
// true even if it is only really used in an outer loop's exit block, since
// the loop is in LCSSA form.
auto *ComputedValue = cast<SelectInst>(ConditionalRecurrence.Step);
- if (none_of(ComputedValue->users(), [Exit](User *U) {
- auto *UI = dyn_cast<Instruction>(U);
- return UI && UI->getParent() == Exit;
- }))
+ if (Exit->phis().empty())
return "Unable to find use of computed value in loop exit block";
// Ensure nothing other than the computed value makes its way out of the loop.
diff --git a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
index a6823f61af75d..68c35996dd2b3 100644
--- a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
+++ b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
@@ -1027,7 +1027,7 @@ exit: ; preds = %loop
ret i16 %crc.next
}
-define i16 @not.crc.result.unused(i16 %crc.init) {
+define void @not.crc.result.unused(i16 %crc.init) {
; CHECK-LABEL: 'not.crc.result.unused'
; CHECK-NEXT: Did not find a hash algorithm
; CHECK-NEXT: Reason: Unable to find use of computed value in loop exit block
@@ -1047,7 +1047,7 @@ loop: ; preds = %loop, %entry
br i1 %exit.cond, label %loop, label %exit
exit: ; preds = %loop
- ret i16 %crc
+ ret void
}
define i16 @not.crc.wrong.sb.check.const(i8 %msg, i16 %checksum) {
>From 1726e4d512a5ab705bc586d89971a4453a8a2035 Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Thu, 30 Jul 2026 16:35:45 -0500
Subject: [PATCH 5/6] Remove now-pointless original check
---
llvm/lib/Analysis/HashRecognize.cpp | 5 -----
llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll | 6 +++---
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Analysis/HashRecognize.cpp b/llvm/lib/Analysis/HashRecognize.cpp
index ead1e715a0c25..5ed6318e05f81 100644
--- a/llvm/lib/Analysis/HashRecognize.cpp
+++ b/llvm/lib/Analysis/HashRecognize.cpp
@@ -578,12 +578,7 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
: LHS->getType()->getIntegerBitWidth()))
return "Loop iterations exceed bitwidth of data";
- // Make sure that the computed value is used in the exit block: this should be
- // true even if it is only really used in an outer loop's exit block, since
- // the loop is in LCSSA form.
auto *ComputedValue = cast<SelectInst>(ConditionalRecurrence.Step);
- if (Exit->phis().empty())
- return "Unable to find use of computed value in loop exit block";
// Ensure nothing other than the computed value makes its way out of the loop.
// Since the loop is in LCSSA form, this is as simple as checking the PHI
diff --git a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
index 68c35996dd2b3..9c6b41ad2cf3a 100644
--- a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
+++ b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll
@@ -1027,10 +1027,10 @@ exit: ; preds = %loop
ret i16 %crc.next
}
-define void @not.crc.result.unused(i16 %crc.init) {
+define i16 @not.crc.result.unused(i16 %crc.init) {
; CHECK-LABEL: 'not.crc.result.unused'
; CHECK-NEXT: Did not find a hash algorithm
-; CHECK-NEXT: Reason: Unable to find use of computed value in loop exit block
+; CHECK-NEXT: Reason: Found stray incoming values in loop exit block
;
entry:
br label %loop
@@ -1047,7 +1047,7 @@ loop: ; preds = %loop, %entry
br i1 %exit.cond, label %loop, label %exit
exit: ; preds = %loop
- ret void
+ ret i16 %crc
}
define i16 @not.crc.wrong.sb.check.const(i8 %msg, i16 %checksum) {
>From 150366a03e78d8961b5aa73ffebecd2a5c5ad415 Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Fri, 31 Jul 2026 10:41:50 -0500
Subject: [PATCH 6/6] Move ComputedValue definition under comment
---
llvm/lib/Analysis/HashRecognize.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/HashRecognize.cpp b/llvm/lib/Analysis/HashRecognize.cpp
index 5ed6318e05f81..c7699ccfbd9ce 100644
--- a/llvm/lib/Analysis/HashRecognize.cpp
+++ b/llvm/lib/Analysis/HashRecognize.cpp
@@ -578,11 +578,10 @@ std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const {
: LHS->getType()->getIntegerBitWidth()))
return "Loop iterations exceed bitwidth of data";
- auto *ComputedValue = cast<SelectInst>(ConditionalRecurrence.Step);
-
// Ensure nothing other than the computed value makes its way out of the loop.
// Since the loop is in LCSSA form, this is as simple as checking the PHI
// nodes in the exit block.
+ auto *ComputedValue = cast<SelectInst>(ConditionalRecurrence.Step);
if (any_of(Exit->phis(), [Latch, ComputedValue](PHINode &PN) {
return PN.getIncomingValueForBlock(Latch) != ComputedValue;
}))
More information about the llvm-commits
mailing list