[llvm] 5f88e49 - [MemDep] Treat partial-overlap lifetime.start as a clobber (#211167)

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 22 14:54:31 PDT 2026


Author: Usama Hameed
Date: 2026-07-22T14:54:26-07:00
New Revision: 5f88e49e8c24c5b799872b4331d35b5406d3c213

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

LOG: [MemDep] Treat partial-overlap lifetime.start as a clobber (#211167)

When getSimplePointerDependencyFrom scans backward and reaches a
@llvm.lifetime.start, it only treated the marker as a barrier when the
query location MustAliased the lifetime's argument, and otherwise fell
through to `continue`. For a load through a GEP into part of an alloca
the alias result is PartialAlias, so the scan walked past lifetime.start
as if it weren't there, and GVN Load PRE would hoist the load above it.

Inspect the full alias result instead of just isMustAlias: MustAlias
still returns Def and NoAlias still continues, but any partial/may
overlap now returns a Clobber so lifetime.start acts as a barrier and
the access cannot be moved above it.

Fixes #194940

Added: 
    llvm/test/Transforms/GVN/pre-no-hoist-across-lifetime.ll

Modified: 
    llvm/lib/Analysis/MemoryDependenceAnalysis.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
index 6fda89af4867c..0ccaadaa1dfb7 100644
--- a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
@@ -464,9 +464,13 @@ MemDepResult MemoryDependenceResults::getSimplePointerDependencyFrom(
       switch (ID) {
       case Intrinsic::lifetime_start: {
         MemoryLocation ArgLoc = MemoryLocation::getAfter(II->getArgOperand(0));
-        if (BatchAA.isMustAlias(ArgLoc, MemLoc))
+        AliasResult R = BatchAA.alias(ArgLoc, MemLoc);
+        if (R == AliasResult::MustAlias)
           return MemDepResult::getDef(II);
-        continue;
+        if (R == AliasResult::NoAlias)
+          continue;
+        // A partial overlap must act as a barrier.
+        return MemDepResult::getClobber(II);
       }
       case Intrinsic::masked_load:
       case Intrinsic::masked_store: {

diff  --git a/llvm/test/Transforms/GVN/pre-no-hoist-across-lifetime.ll b/llvm/test/Transforms/GVN/pre-no-hoist-across-lifetime.ll
new file mode 100644
index 0000000000000..013b2f358519d
--- /dev/null
+++ b/llvm/test/Transforms/GVN/pre-no-hoist-across-lifetime.ll
@@ -0,0 +1,92 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=gvn -S | FileCheck %s
+
+; Regression test for
+; https://github.com/llvm/llvm-project/issues/194940
+; GVN Load PRE must not hoist a load across @llvm.lifetime.start of the
+; alloca it accesses (partial overlap).
+
+%Struct = type { [80 x i8], i16 }
+
+declare void @opaque(ptr)
+
+; The load of %s through a GEP must NOT be hoisted into %cold, which runs
+; before the lifetime.start in %merge; the load has to stay after it.
+define void @dont_hoist_across_lifetime_start(ptr %out, i1 %cond) {
+; CHECK-LABEL: define void @dont_hoist_across_lifetime_start(
+; CHECK-SAME: ptr [[OUT:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[S:%.*]] = alloca [[STRUCT:%.*]], align 8
+; CHECK-NEXT:    br i1 [[COND]], label %[[MERGE:.*]], label %[[COLD:.*]]
+; CHECK:       [[COLD]]:
+; CHECK-NEXT:    store ptr null, ptr inttoptr (i64 8 to ptr), align 8
+; CHECK-NEXT:    br label %[[MERGE]]
+; CHECK:       [[MERGE]]:
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[S]])
+; CHECK-NEXT:    [[S_GEP:%.*]] = getelementptr inbounds i8, ptr [[S]], i64 80
+; CHECK-NEXT:    [[V:%.*]] = load i16, ptr [[S_GEP]], align 8
+; CHECK-NEXT:    store i16 [[V]], ptr [[OUT]], align 2
+; CHECK-NEXT:    call void @opaque(ptr [[S]])
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[S]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %s = alloca %Struct, align 8
+  br i1 %cond, label %merge, label %cold
+
+cold:
+  ; Keep %cold from being eliminated — the side effect matters so that PRE
+  ; has a non-trivial predecessor to hoist into.
+  store ptr null, ptr inttoptr (i64 8 to ptr), align 8
+  br label %merge
+
+merge:
+  call void @llvm.lifetime.start.p0(ptr %s)
+  %s.gep = getelementptr inbounds i8, ptr %s, i64 80
+  %v = load i16, ptr %s.gep, align 8
+  store i16 %v, ptr %out, align 2
+  call void @opaque(ptr %s)
+  call void @llvm.lifetime.end.p0(ptr %s)
+  ret void
+}
+
+; Sanity check: for a load that is dominated by lifetime.start on every
+; incoming edge, GVN can still eliminate the redundant loads across the join
+; (nothing about the fix should block that).
+define i16 @eliminate_redundant_load_after_lifetime_start(i1 %cond) {
+; CHECK-LABEL: define i16 @eliminate_redundant_load_after_lifetime_start(
+; CHECK-SAME: i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[S:%.*]] = alloca [[STRUCT:%.*]], align 8
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[S]])
+; CHECK-NEXT:    [[S_GEP:%.*]] = getelementptr inbounds i8, ptr [[S]], i64 80
+; CHECK-NEXT:    store i16 42, ptr [[S_GEP]], align 8
+; CHECK-NEXT:    br i1 [[COND]], label %[[A:.*]], label %[[B:.*]]
+; CHECK:       [[A]]:
+; CHECK-NEXT:    br label %[[JOIN:.*]]
+; CHECK:       [[B]]:
+; CHECK-NEXT:    br label %[[JOIN]]
+; CHECK:       [[JOIN]]:
+; CHECK-NEXT:    call void @llvm.lifetime.end.p0(ptr [[S]])
+; CHECK-NEXT:    ret i16 42
+;
+entry:
+  %s = alloca %Struct, align 8
+  call void @llvm.lifetime.start.p0(ptr %s)
+  %s.gep = getelementptr inbounds i8, ptr %s, i64 80
+  store i16 42, ptr %s.gep, align 8
+  br i1 %cond, label %a, label %b
+
+a:
+  %va = load i16, ptr %s.gep, align 8
+  br label %join
+
+b:
+  %vb = load i16, ptr %s.gep, align 8
+  br label %join
+
+join:
+  %v = phi i16 [ %va, %a ], [ %vb, %b ]
+  call void @llvm.lifetime.end.p0(ptr %s)
+  ret i16 %v
+}


        


More information about the llvm-commits mailing list