[llvm] [MemCpyOpt] Don't perform stack-move opt for out-of-bounds copy (PR #217607)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 20 05:56:50 PDT 2026
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/217607
There can be a copy with an out of bounds offset in dead code. If the perform the stack-move optimization based on that, we'll end up merging an alloca into an out-of-bounds offset of another alloca, which will result in UB (when used in non-dead code).
>From 999e3a2673aa1dc1b85f89f06abd4a37ce789ecf Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Thu, 20 Aug 2026 14:45:37 +0200
Subject: [PATCH 1/2] Add miscompiled test case
---
.../Transforms/MemCpyOpt/stack-move-offset.ll | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll b/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll
index ee15babdd3896..9e51ea17d958e 100644
--- a/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll
+++ b/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll
@@ -282,3 +282,35 @@ define void @no_optimize_clobbering_store_to_src_offset(ptr noalias %dst) {
ret void
}
+
+; https://github.com/llvm/llvm-project/issues/216566
+; There could be a copy using an out of bounds offset in dead code. Don't
+; optimize such cases.
+define i32 @out_of_bounds_offset() {
+; CHECK-LABEL: define i32 @out_of_bounds_offset() {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[A2:%.*]] = alloca i8, align 1
+; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[A2]], i64 123
+; CHECK-NEXT: br i1 true, label [[IF_THEN1:%.*]], label [[IF_THEN2:%.*]]
+; CHECK: if.then1:
+; CHECK-NEXT: store i8 0, ptr [[TMP0]], align 1
+; CHECK-NEXT: ret i32 0
+; CHECK: if.then2:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A2]], i64 123
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %a1 = alloca i8, align 1
+ %a2 = alloca i8, align 1
+ br i1 true, label %if.then1, label %if.then2
+
+if.then1:
+ store i8 0, ptr %a1
+ ret i32 0
+
+if.then2:
+ %gep = getelementptr i8, ptr %a2, i64 123
+ %v = load i8, ptr %gep
+ store i8 %v, ptr %a1
+ ret i32 0
+}
>From ed4853da49f1fb1df06c68c39bf3ecb0dcdd7a38 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Thu, 20 Aug 2026 14:52:43 +0200
Subject: [PATCH 2/2] Check that the offset is in bounds
---
llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp | 7 +++++++
llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll | 8 +++++---
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
index 92e1d9cf21240..a8b6bc7b275b2 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -1572,6 +1572,13 @@ bool MemCpyOptPass::performStackMoveOptzn(Instruction *Load, Instruction *Store,
return false;
}
+ if (*SrcOffset) {
+ // Make sure that the copied offset is actually part of the alloca. There
+ // might be an out-of-bounds copy in dead code.
+ if (!Size.isFixed() || *SrcOffset + Size > *SrcSize)
+ return false;
+ }
+
// Check if it will be legal to combine allocas without breaking dominator.
bool MoveSrc = !DT->dominates(SrcAlloca, DestAlloca);
if (MoveSrc) {
diff --git a/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll b/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll
index 9e51ea17d958e..80cb21dee3d1e 100644
--- a/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll
+++ b/llvm/test/Transforms/MemCpyOpt/stack-move-offset.ll
@@ -290,13 +290,15 @@ define i32 @out_of_bounds_offset() {
; CHECK-LABEL: define i32 @out_of_bounds_offset() {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[A2:%.*]] = alloca i8, align 1
-; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[A2]], i64 123
+; CHECK-NEXT: [[A3:%.*]] = alloca i8, align 1
; CHECK-NEXT: br i1 true, label [[IF_THEN1:%.*]], label [[IF_THEN2:%.*]]
; CHECK: if.then1:
-; CHECK-NEXT: store i8 0, ptr [[TMP0]], align 1
+; CHECK-NEXT: store i8 0, ptr [[A2]], align 1
; CHECK-NEXT: ret i32 0
; CHECK: if.then2:
-; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A2]], i64 123
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A3]], i64 123
+; CHECK-NEXT: [[V:%.*]] = load i8, ptr [[GEP]], align 1
+; CHECK-NEXT: store i8 [[V]], ptr [[A2]], align 1
; CHECK-NEXT: ret i32 0
;
entry:
More information about the llvm-commits
mailing list