[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