[llvm] cf28b9f - [LoopIdiom] Fix 64-to-32-bit stride truncation in strlen idiom recognition (#227406)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 00:35:13 PDT 2026


Author: Szymon Sobieszek
Date: 2026-09-30T09:35:06+02:00
New Revision: cf28b9f7fa4c4dec8f209b7059586df6234c7a25

URL: https://github.com/llvm/llvm-project/commit/cf28b9f7fa4c4dec8f209b7059586df6234c7a25
DIFF: https://github.com/llvm/llvm-project/commit/cf28b9f7fa4c4dec8f209b7059586df6234c7a25.diff

LOG: [LoopIdiom] Fix 64-to-32-bit stride truncation in strlen idiom recognition (#227406)

This patch fixes a silent miscompile where loops with massive strides
(e.g., 0x100000001 or 0x2000000000000001) were incorrectly optimized
into strlen calls.

Previously, getZExtValue() was truncated to a 32-bit unsigned integer,
causing some massive strides to appear as a stride of 1. Furthermore,
the OpWidth check relied on multiplication (StepSize * 8), which is
vulnerable to 64-bit integer overflow.

By upgrading StepSize to uint64_t and using division (OpWidth / 8), we
ensure the optimization is safely aborted for massive strides without
risking arithmetic overflow.

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
    llvm/test/Transforms/LoopIdiom/strlen.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
index 8c098e0d68d72..bbb0bd370899e 100644
--- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp
@@ -2095,15 +2095,15 @@ class StrlenVerifier {
 
     LLVM_DEBUG(dbgs() << "pointer load scev: " << *LoadEv << "\n");
 
-    unsigned StepSize = Step->getZExtValue();
+    uint64_t StepSize = Step->getZExtValue();
 
     // Verify that StepSize is consistent with platform char width.
     OpWidth = OperandType->getIntegerBitWidth();
     unsigned WcharSize = TLI->getWCharSize(*LoopLoad->getModule());
-    if (OpWidth != StepSize * 8)
-      return false;
     if (OpWidth != 8 && OpWidth != 16 && OpWidth != 32)
       return false;
+    if (StepSize != OpWidth / 8)
+      return false;
     if (OpWidth >= 16)
       if (OpWidth != WcharSize * 8)
         return false;

diff  --git a/llvm/test/Transforms/LoopIdiom/strlen.ll b/llvm/test/Transforms/LoopIdiom/strlen.ll
index eaafe6b162f28..31b797430aafc 100644
--- a/llvm/test/Transforms/LoopIdiom/strlen.ll
+++ b/llvm/test/Transforms/LoopIdiom/strlen.ll
@@ -617,23 +617,20 @@ define i64 @valid_basic_strlen_with_dbg(ptr %str) {
 ; CHECK-LABEL: define i64 @valid_basic_strlen_with_dbg(
 ; CHECK-SAME: ptr [[STR:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[STRLEN:%.*]] = call i64 @strlen(ptr [[STR]]), !dbg [[DBGLOC1:![0-9]+]]
+; CHECK-NEXT:    [[STRLEN:%.*]] = call i64 @strlen(ptr [[STR]]), !dbg [[DBG4:![0-9]+]]
 ; CHECK-NEXT:    [[SCEVGEP:%.*]] = getelementptr i8, ptr [[STR]], i64 [[STRLEN]]
 ; CHECK-NEXT:    br label %[[WHILE_COND:.*]]
 ; CHECK:       [[WHILE_COND]]:
 ; CHECK-NEXT:    [[STR_ADDR_0:%.*]] = phi ptr [ [[STR]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ]
-; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[STR_ADDR_0]], align 1, !dbg [[DBGLOC2:![0-9]+]]
-; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 0, !dbg [[DBGLOC2]]
-; CHECK-NEXT:    [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 1, !dbg [[DBGLOC2]]
-; CHECK-NEXT:    br i1 true, label %[[WHILE_END:.*]], label %[[WHILE_COND]], !dbg [[DBGLOC1]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[STR_ADDR_0]], align 1, !dbg [[DBG8:![0-9]+]]
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp eq i8 [[TMP0]], 0, !dbg [[DBG8]]
+; CHECK-NEXT:    [[INCDEC_PTR]] = getelementptr i8, ptr [[STR_ADDR_0]], i64 1, !dbg [[DBG8]]
+; CHECK-NEXT:    br i1 true, label %[[WHILE_END:.*]], label %[[WHILE_COND]], !dbg [[DBG4]]
 ; CHECK:       [[WHILE_END]]:
-; CHECK-NEXT:    [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[SCEVGEP]] to i64, !dbg [[DBGLOC2]]
-; CHECK-NEXT:    [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64, !dbg [[DBGLOC2]]
-; CHECK-NEXT:    [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !dbg [[DBGLOC2]]
-; CHECK-NEXT:    ret i64 [[SUB_PTR_SUB]], !dbg [[DBGLOC2]]
-;
-; CHECK: [[DBGLOC1]] = !DILocation(line: 3, column: 3
-; CHECK: [[DBGLOC2]] = !DILocation(line: 5, column: 3
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[SCEVGEP]] to i64, !dbg [[DBG8]]
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[STR]] to i64, !dbg [[DBG8]]
+; CHECK-NEXT:    [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]], !dbg [[DBG8]]
+; CHECK-NEXT:    ret i64 [[SUB_PTR_SUB]], !dbg [[DBG8]]
 ;
 entry:
   br label %while.cond
@@ -652,6 +649,43 @@ while.end:
   ret i64 %sub.ptr.sub, !dbg !8
 }
 
+; Test a stride of 0x100000001 (4294967297).
+; The loop should not be transformed into a strlen call.
+define i64 @large_stride(ptr %src) {
+; CHECK-LABEL: define i64 @large_stride(
+; CHECK-SAME: ptr [[SRC:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[WHILE_COND:.*]]
+; CHECK:       [[WHILE_COND]]:
+; CHECK-NEXT:    [[P_0:%.*]] = phi ptr [ [[SRC]], %[[ENTRY]] ], [ [[INCDEC_PTR:%.*]], %[[WHILE_COND]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[P_0]], align 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[TMP0]], 0
+; CHECK-NEXT:    [[INCDEC_PTR]] = getelementptr inbounds i8, ptr [[P_0]], i64 4294967297
+; CHECK-NEXT:    br i1 [[CMP]], label %[[WHILE_COND]], label %[[WHILE_END:.*]]
+; CHECK:       [[WHILE_END]]:
+; CHECK-NEXT:    [[P_0_LCSSA:%.*]] = phi ptr [ [[P_0]], %[[WHILE_COND]] ]
+; CHECK-NEXT:    [[SUB_PTR_LHS_CAST:%.*]] = ptrtoint ptr [[P_0_LCSSA]] to i64
+; CHECK-NEXT:    [[SUB_PTR_RHS_CAST:%.*]] = ptrtoint ptr [[SRC]] to i64
+; CHECK-NEXT:    [[SUB_PTR_SUB:%.*]] = sub i64 [[SUB_PTR_LHS_CAST]], [[SUB_PTR_RHS_CAST]]
+; CHECK-NEXT:    ret i64 [[SUB_PTR_SUB]]
+;
+entry:
+  br label %while.cond
+
+while.cond:
+  %p.0 = phi ptr [ %src, %entry ], [ %incdec.ptr, %while.cond ]
+  %0 = load i8, ptr %p.0, align 1
+  %cmp = icmp ne i8 %0, 0
+  %incdec.ptr = getelementptr inbounds i8, ptr %p.0, i64 4294967297
+  br i1 %cmp, label %while.cond, label %while.end
+
+while.end:
+  %sub.ptr.lhs.cast = ptrtoint ptr %p.0 to i64
+  %sub.ptr.rhs.cast = ptrtoint ptr %src to i64
+  %sub.ptr.sub = sub i64 %sub.ptr.lhs.cast, %sub.ptr.rhs.cast
+  ret i64 %sub.ptr.sub
+}
+
 !llvm.module.flags = !{!0}
 !llvm.dbg.cu = !{!1}
 
@@ -664,3 +698,13 @@ while.end:
 !6 = distinct !DISubprogram(name: "foo", scope: !2, file: !2, line: 2, type: !7, virtualIndex: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !1)
 !7 = !DISubroutineType(types: !3)
 !8 = !DILocation(line: 5, column: 3, scope: !5)
+;.
+; CHECK: [[META1:![0-9]+]] = distinct !DICompileUnit(language: DW_LANG_C99, file: [[META2:![0-9]+]], producer: "{{.*}}clang version {{.*}}", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: [[META3:![0-9]+]], retainedTypes: [[META3]])
+; CHECK: [[META2]] = !DIFile(filename: "{{.*}}strlen.c", directory: {{.*}})
+; CHECK: [[META3]] = !{}
+; CHECK: [[DBG4]] = !DILocation(line: 3, column: 3, scope: [[META5:![0-9]+]])
+; CHECK: [[META5]] = distinct !DILexicalBlock(scope: [[META6:![0-9]+]], file: [[META2]], line: 2, column: 21)
+; CHECK: [[META6]] = distinct !DISubprogram(name: "foo", scope: [[META2]], file: [[META2]], line: 2, type: [[META7:![0-9]+]], virtualIndex: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: [[META1]])
+; CHECK: [[META7]] = !DISubroutineType(types: [[META3]])
+; CHECK: [[DBG8]] = !DILocation(line: 5, column: 3, scope: [[META5]])
+;.


        


More information about the llvm-commits mailing list