[llvm] [LoopIdiom] Fix miscompile for big-endian sub-byte CRC (PR #213040)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 30 08:11:40 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Sean Clarke (xarkenz)

<details>
<summary>Changes</summary>

The Sarwate lookup table optimization shifts the top bits of the `Indexer` down to the low byte before indexing into the table. Though uncommon, if the CRC has a sub-byte width, the top bits of `Indexer` must be shifted *up* to align with the low byte, but this is never accounted for. Shift `Indexer` either right *or* left depending on CRC bitwidth.

---
Full diff: https://github.com/llvm/llvm-project/pull/213040.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp (+7-9) 
- (modified) llvm/test/Transforms/LoopIdiom/cyclic-redundancy-check.ll (+70) 


``````````diff
diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index 7dc303149aaa6..12264892fcab9 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -1816,15 +1816,13 @@ void LoopIdiomRecognize::optimizeCRCLoopUsingTableLookup(
     };
     auto HiIdx = [LoByte, CRCBW](IRBuilderBase &Builder, Value *Op,
                                  const Twine &Name) {
-      Type *OpTy = Op->getType();
-
-      // When the bitwidth of the CRC mismatches the Op's bitwidth, we need to
-      // use the CRC's bitwidth as the reference for shifting right.
-      return LoByte(Builder,
-                    CRCBW > 8 ? Builder.CreateLShr(
-                                    Op, ConstantInt::get(OpTy, CRCBW - 8), Name)
-                              : Op,
-                    Name + ".lo.byte");
+      // Shift the top bits of Op to the bottom byte by using the CRC bitwidth
+      // as a reference.
+      if (CRCBW != 8) {
+        Op = CRCBW > 8 ? Builder.CreateLShr(Op, CRCBW - 8, Name)
+                       : Builder.CreateShl(Op, 8 - CRCBW, Name);
+      }
+      return LoByte(Builder, Op, Name + ".lo.byte");
     };
 
     IRBuilder<> Builder(CurLoop->getHeader(),
diff --git a/llvm/test/Transforms/LoopIdiom/cyclic-redundancy-check.ll b/llvm/test/Transforms/LoopIdiom/cyclic-redundancy-check.ll
index 65f54263edcdc..904f155127bb5 100644
--- a/llvm/test/Transforms/LoopIdiom/cyclic-redundancy-check.ll
+++ b/llvm/test/Transforms/LoopIdiom/cyclic-redundancy-check.ll
@@ -18,6 +18,7 @@
 ; TABLE: @.crctable.12 = private constant [256 x i32] [i32 0, i32 4489, i32 8978, i32 12955, i32 17956, i32 22445, i32 25910, i32 29887, i32 35912, i32 40385, i32 44890, i32 48851, i32 51820, i32 56293, i32 59774, i32 63735, i32 4225, i32 264, i32 13203, i32 8730, i32 22181, i32 18220, i32 30135, i32 25662, i32 40137, i32 36160, i32 49115, i32 44626, i32 56045, i32 52068, i32 63999, i32 59510, i32 8450, i32 12427, i32 528, i32 5017, i32 26406, i32 30383, i32 17460, i32 21949, i32 44362, i32 48323, i32 36440, i32 40913, i32 60270, i32 64231, i32 51324, i32 55797, i32 12675, i32 8202, i32 4753, i32 792, i32 30631, i32 26158, i32 21685, i32 17724, i32 48587, i32 44098, i32 40665, i32 36688, i32 64495, i32 60006, i32 55549, i32 51572, i32 16900, i32 21389, i32 24854, i32 28831, i32 1056, i32 5545, i32 10034, i32 14011, i32 52812, i32 57285, i32 60766, i32 64727, i32 34920, i32 39393, i32 43898, i32 47859, i32 21125, i32 17164, i32 29079, i32 24606, i32 5281, i32 1320, i32 14259, i32 9786, i32 57037, i32 53060, i32 64991, i32 60502, i32 39145, i32 35168, i32 48123, i32 43634, i32 25350, i32 29327, i32 16404, i32 20893, i32 9506, i32 13483, i32 1584, i32 6073, i32 61262, i32 65223, i32 52316, i32 56789, i32 43370, i32 47331, i32 35448, i32 39921, i32 29575, i32 25102, i32 20629, i32 16668, i32 13731, i32 9258, i32 5809, i32 1848, i32 65487, i32 60998, i32 56541, i32 52564, i32 47595, i32 43106, i32 39673, i32 35696, i32 33800, i32 38273, i32 42778, i32 46739, i32 49708, i32 54181, i32 57662, i32 61623, i32 2112, i32 6601, i32 11090, i32 15067, i32 20068, i32 24557, i32 28022, i32 31999, i32 38025, i32 34048, i32 47003, i32 42514, i32 53933, i32 49956, i32 61887, i32 57398, i32 6337, i32 2376, i32 15315, i32 10842, i32 24293, i32 20332, i32 32247, i32 27774, i32 42250, i32 46211, i32 34328, i32 38801, i32 58158, i32 62119, i32 49212, i32 53685, i32 10562, i32 14539, i32 2640, i32 7129, i32 28518, i32 32495, i32 19572, i32 24061, i32 46475, i32 41986, i32 38553, i32 34576, i32 62383, i32 57894, i32 53437, i32 49460, i32 14787, i32 10314, i32 6865, i32 2904, i32 32743, i32 28270, i32 23797, i32 19836, i32 50700, i32 55173, i32 58654, i32 62615, i32 32808, i32 37281, i32 41786, i32 45747, i32 19012, i32 23501, i32 26966, i32 30943, i32 3168, i32 7657, i32 12146, i32 16123, i32 54925, i32 50948, i32 62879, i32 58390, i32 37033, i32 33056, i32 46011, i32 41522, i32 23237, i32 19276, i32 31191, i32 26718, i32 7393, i32 3432, i32 16371, i32 11898, i32 59150, i32 63111, i32 50204, i32 54677, i32 41258, i32 45219, i32 33336, i32 37809, i32 27462, i32 31439, i32 18516, i32 23005, i32 11618, i32 15595, i32 3696, i32 8185, i32 63375, i32 58886, i32 54429, i32 50452, i32 45483, i32 40994, i32 37561, i32 33584, i32 31687, i32 27214, i32 22741, i32 18780, i32 15843, i32 11370, i32 7921, i32 3960]
 ; TABLE: @.crctable.13 = private constant [256 x i8] c"\00\10 07'\17\07\19\099).>\0E\1E2\22\12\02\05\15%5+
 ; TABLE: @.crctable.14 = private constant [256 x i16] [i16 0, i16 258, i16 516, i16 774, i16 1032, i16 1290, i16 1548, i16 1806, i16 2064, i16 2322, i16 2580, i16 2838, i16 3096, i16 3354, i16 3612, i16 3870, i16 4128, i16 4386, i16 4644, i16 4902, i16 5160, i16 5418, i16 5676, i16 5934, i16 6192, i16 6450, i16 6708, i16 6966, i16 7224, i16 7482, i16 7740, i16 7998, i16 8256, i16 8514, i16 8772, i16 9030, i16 9288, i16 9546, i16 9804, i16 10062, i16 10320, i16 10578, i16 10836, i16 11094, i16 11352, i16 11610, i16 11868, i16 12126, i16 12384, i16 12642, i16 12900, i16 13158, i16 13416, i16 13674, i16 13932, i16 14190, i16 14448, i16 14706, i16 14964, i16 15222, i16 15480, i16 15738, i16 15996, i16 16254, i16 16512, i16 16770, i16 17028, i16 17286, i16 17544, i16 17802, i16 18060, i16 18318, i16 18576, i16 18834, i16 19092, i16 19350, i16 19608, i16 19866, i16 20124, i16 20382, i16 20640, i16 20898, i16 21156, i16 21414, i16 21672, i16 21930, i16 22188, i16 22446, i16 22704, i16 22962, i16 23220, i16 23478, i16 23736, i16 23994, i16 24252, i16 24510, i16 24768, i16 25026, i16 25284, i16 25542, i16 25800, i16 26058, i16 26316, i16 26574, i16 26832, i16 27090, i16 27348, i16 27606, i16 27864, i16 28122, i16 28380, i16 28638, i16 28896, i16 29154, i16 29412, i16 29670, i16 29928, i16 30186, i16 30444, i16 30702, i16 30960, i16 31218, i16 31476, i16 31734, i16 31992, i16 32250, i16 32508, i16 32766, i16 -32512, i16 -32766, i16 -31996, i16 -32250, i16 -31480, i16 -31734, i16 -30964, i16 -31218, i16 -30448, i16 -30702, i16 -29932, i16 -30186, i16 -29416, i16 -29670, i16 -28900, i16 -29154, i16 -28384, i16 -28638, i16 -27868, i16 -28122, i16 -27352, i16 -27606, i16 -26836, i16 -27090, i16 -26320, i16 -26574, i16 -25804, i16 -26058, i16 -25288, i16 -25542, i16 -24772, i16 -25026, i16 -24256, i16 -24510, i16 -23740, i16 -23994, i16 -23224, i16 -23478, i16 -22708, i16 -22962, i16 -22192, i16 -22446, i16 -21676, i16 -21930, i16 -21160, i16 -21414, i16 -20644, i16 -20898, i16 -20128, i16 -20382, i16 -19612, i16 -19866, i16 -19096, i16 -19350, i16 -18580, i16 -18834, i16 -18064, i16 -18318, i16 -17548, i16 -17802, i16 -17032, i16 -17286, i16 -16516, i16 -16770, i16 -16000, i16 -16254, i16 -15484, i16 -15738, i16 -14968, i16 -15222, i16 -14452, i16 -14706, i16 -13936, i16 -14190, i16 -13420, i16 -13674, i16 -12904, i16 -13158, i16 -12388, i16 -12642, i16 -11872, i16 -12126, i16 -11356, i16 -11610, i16 -10840, i16 -11094, i16 -10324, i16 -10578, i16 -9808, i16 -10062, i16 -9292, i16 -9546, i16 -8776, i16 -9030, i16 -8260, i16 -8514, i16 -7744, i16 -7998, i16 -7228, i16 -7482, i16 -6712, i16 -6966, i16 -6196, i16 -6450, i16 -5680, i16 -5934, i16 -5164, i16 -5418, i16 -4648, i16 -4902, i16 -4132, i16 -4386, i16 -3616, i16 -3870, i16 -3100, i16 -3354, i16 -2584, i16 -2838, i16 -2068, i16 -2322, i16 -1552, i16 -1806, i16 -1036, i16 -1290, i16 -520, i16 -774, i16 -4, i16 -258]
+; TABLE: @.crctable.15 = private constant [256 x i4] [i4 0, i4 3, i4 6, i4 5, i4 -4, i4 -1, i4 -6, i4 -7, i4 -5, i4 -8, i4 -3, i4 -2, i4 7, i4 4, i4 1, i4 2, i4 5, i4 6, i4 3, i4 0, i4 -7, i4 -6, i4 -1, i4 -4, i4 -2, i4 -3, i4 -8, i4 -5, i4 2, i4 1, i4 4, i4 7, i4 -6, i4 -7, i4 -4, i4 -1, i4 6, i4 5, i4 0, i4 3, i4 1, i4 2, i4 7, i4 4, i4 -3, i4 -2, i4 -5, i4 -8, i4 -1, i4 -4, i4 -7, i4 -6, i4 3, i4 0, i4 5, i4 6, i4 4, i4 7, i4 2, i4 1, i4 -8, i4 -5, i4 -2, i4 -3, i4 7, i4 4, i4 1, i4 2, i4 -5, i4 -8, i4 -3, i4 -2, i4 -4, i4 -1, i4 -6, i4 -7, i4 0, i4 3, i4 6, i4 5, i4 2, i4 1, i4 4, i4 7, i4 -2, i4 -3, i4 -8, i4 -5, i4 -7, i4 -6, i4 -1, i4 -4, i4 5, i4 6, i4 3, i4 0, i4 -3, i4 -2, i4 -5, i4 -8, i4 1, i4 2, i4 7, i4 4, i4 6, i4 5, i4 0, i4 3, i4 -6, i4 -7, i4 -4, i4 -1, i4 -8, i4 -5, i4 -2, i4 -3, i4 4, i4 7, i4 2, i4 1, i4 3, i4 0, i4 5, i4 6, i4 -1, i4 -4, i4 -7, i4 -6, i4 -2, i4 -3, i4 -8, i4 -5, i4 2, i4 1, i4 4, i4 7, i4 5, i4 6, i4 3, i4 0, i4 -7, i4 -6, i4 -1, i4 -4, i4 -5, i4 -8, i4 -3, i4 -2, i4 7, i4 4, i4 1, i4 2, i4 0, i4 3, i4 6, i4 5, i4 -4, i4 -1, i4 -6, i4 -7, i4 4, i4 7, i4 2, i4 1, i4 -8, i4 -5, i4 -2, i4 -3, i4 -1, i4 -4, i4 -7, i4 -6, i4 3, i4 0, i4 5, i4 6, i4 1, i4 2, i4 7, i4 4, i4 -3, i4 -2, i4 -5, i4 -8, i4 -6, i4 -7, i4 -4, i4 -1, i4 6, i4 5, i4 0, i4 3, i4 -7, i4 -6, i4 -1, i4 -4, i4 5, i4 6, i4 3, i4 0, i4 2, i4 1, i4 4, i4 7, i4 -2, i4 -3, i4 -8, i4 -5, i4 -4, i4 -1, i4 -6, i4 -7, i4 0, i4 3, i4 6, i4 5, i4 7, i4 4, i4 1, i4 2, i4 -5, i4 -8, i4 -3, i4 -2, i4 3, i4 0, i4 5, i4 6, i4 -1, i4 -4, i4 -7, i4 -6, i4 -8, i4 -5, i4 -2, i4 -3, i4 4, i4 7, i4 2, i4 1, i4 6, i4 5, i4 0, i4 3, i4 -6, i4 -7, i4 -4, i4 -1, i4 -3, i4 -2, i4 -5, i4 -8, i4 1, i4 2, i4 7, i4 4]
 ;.
 define i16 @crc16.le.tc8(i8 %msg, i16 %checksum) {
 ; TABLE-LABEL: define i16 @crc16.le.tc8(
@@ -1045,6 +1046,75 @@ exit:                                              ; preds = %loop
   ret i16 %crc.next
 }
 
+define i4 @crc4.be.tc8(i8 %msg, i4 %checksum) {
+; TABLE-LABEL: define i4 @crc4.be.tc8(
+; TABLE-SAME: i8 [[MSG:%.*]], i4 [[CHECKSUM:%.*]]) {
+; TABLE-NEXT:  [[ENTRY:.*]]:
+; TABLE-NEXT:    br label %[[LOOP:.*]]
+; TABLE:       [[LOOP]]:
+; TABLE-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; TABLE-NEXT:    [[CRC2:%.*]] = phi i4 [ [[CHECKSUM]], %[[ENTRY]] ], [ [[TBL_LD:%.*]], %[[LOOP]] ]
+; TABLE-NEXT:    [[IV_BITS:%.*]] = shl i8 [[IV]], 3
+; TABLE-NEXT:    [[DATA_INDEXER:%.*]] = shl i8 [[MSG]], [[IV_BITS]]
+; TABLE-NEXT:    [[CRC_INDEXER_CAST:%.*]] = zext i4 [[CRC2]] to i8
+; TABLE-NEXT:    [[CRC_DATA_INDEXER:%.*]] = xor i8 [[DATA_INDEXER]], [[CRC_INDEXER_CAST]]
+; TABLE-NEXT:    [[INDEXER_HI:%.*]] = shl i8 [[CRC_DATA_INDEXER]], 4
+; TABLE-NEXT:    [[INDEXER_EXT:%.*]] = zext i8 [[INDEXER_HI]] to i64
+; TABLE-NEXT:    [[TBL_PTRADD:%.*]] = getelementptr inbounds i4, ptr @.crctable.15, i64 [[INDEXER_EXT]]
+; TABLE-NEXT:    [[TBL_LD]] = load i4, ptr [[TBL_PTRADD]], align 1
+; TABLE-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
+; TABLE-NEXT:    [[EXIT_COND1:%.*]] = icmp ne i8 [[IV]], 0
+; TABLE-NEXT:    br i1 [[EXIT_COND1]], label %[[LOOP]], label %[[EXIT:.*]]
+; TABLE:       [[EXIT]]:
+; TABLE-NEXT:    [[CRC_NEXT_LCSSA:%.*]] = phi i4 [ [[TBL_LD]], %[[LOOP]] ]
+; TABLE-NEXT:    ret i4 [[CRC_NEXT_LCSSA]]
+;
+; CLMUL-LABEL: define i4 @crc4.be.tc8(
+; CLMUL-SAME: i8 [[MSG:%.*]], i4 [[CHECKSUM:%.*]]) {
+; CLMUL-NEXT:  [[ENTRY:.*:]]
+; CLMUL-NEXT:    [[CRC_CAST:%.*]] = zext i4 [[CHECKSUM]] to i12
+; CLMUL-NEXT:    [[DATA_CAST:%.*]] = zext i8 [[MSG]] to i12
+; CLMUL-NEXT:    [[XOR_CRC_DATA1:%.*]] = xor i12 [[CRC_CAST]], [[DATA_CAST]]
+; CLMUL-NEXT:    [[CRC_ALIGN_TC:%.*]] = shl i12 [[XOR_CRC_DATA1]], 4
+; CLMUL-NEXT:    [[CRC_TCBITS:%.*]] = and i12 [[CRC_ALIGN_TC]], 255
+; CLMUL-NEXT:    [[TCBITS_CAST:%.*]] = zext i12 [[CRC_TCBITS]] to i16
+; CLMUL-NEXT:    [[CLMUL_MU:%.*]] = call i16 @llvm.clmul.i16(i16 [[TCBITS_CAST]], i16 309)
+; CLMUL-NEXT:    [[QUOT_LSHR:%.*]] = lshr i16 [[CLMUL_MU]], 8
+; CLMUL-NEXT:    [[QUOT_CAST:%.*]] = trunc i16 [[QUOT_LSHR]] to i12
+; CLMUL-NEXT:    [[CLMUL_GP:%.*]] = call i12 @llvm.clmul.i12(i12 [[QUOT_CAST]], i12 19)
+; CLMUL-NEXT:    [[CRC_RECAST:%.*]] = zext i4 [[CHECKSUM]] to i12
+; CLMUL-NEXT:    [[CRC_SHL2:%.*]] = shl i12 [[CRC_RECAST]], 8
+; CLMUL-NEXT:    [[XOR_CRC_MULT:%.*]] = xor i12 [[CRC_SHL2]], [[CLMUL_GP]]
+; CLMUL-NEXT:    [[CRC_NEXT3:%.*]] = trunc i12 [[XOR_CRC_MULT]] to i4
+; CLMUL-NEXT:    br label %[[LOOP:.*]]
+; CLMUL:       [[LOOP]]:
+; CLMUL-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT:.*]]
+; CLMUL:       [[EXIT]]:
+; CLMUL-NEXT:    [[CRC_NEXT_LCSSA:%.*]] = phi i4 [ [[CRC_NEXT3]], %[[LOOP]] ]
+; CLMUL-NEXT:    ret i4 [[CRC_NEXT_LCSSA]]
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
+  %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ]
+  %crc = phi i4 [ %checksum, %entry ], [ %crc.next, %loop ]
+  %data.trunc = trunc i8 %data to i4
+  %xor.crc.data = xor i4 %crc, %data.trunc
+  %check.sb = icmp sge i4 %xor.crc.data, 0
+  %crc.shl = shl i4 %crc, 1
+  %crc.xor = xor i4 %crc.shl, 3
+  %crc.next = select i1 %check.sb, i4 %crc.shl, i4 %crc.xor
+  %iv.next = add nuw nsw i8 %iv, 1
+  %data.next = shl i8 %data, 1
+  %exit.cond = icmp samesign ult i8 %iv, 7
+  br i1 %exit.cond, label %loop, label %exit
+
+exit:
+  ret i4 %crc.next
+}
+
 define i32 @crc32.le.tc4.data32(i32 %checksum, i32 %msg) optsize {
 ; TABLE-LABEL: define i32 @crc32.le.tc4.data32(
 ; TABLE-SAME: i32 [[CHECKSUM:%.*]], i32 [[MSG:%.*]]) #[[ATTR0:[0-9]+]] {

``````````

</details>


https://github.com/llvm/llvm-project/pull/213040


More information about the llvm-commits mailing list