[llvm] [GVN] Fix vtable processing of static object (PR #223456)

via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 1 04:03:54 PDT 2026


https://github.com/eleviant updated https://github.com/llvm/llvm-project/pull/223456

>From 028562ce2a24f54b644dc7a038c34466b01aee59 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Mon, 14 Sep 2026 18:44:52 +0200
Subject: [PATCH 1/2] [GVN] Fix vtable processing of static object

Patch fixes crash in GVN pass when compiling this C++ code:

https://godbolt.org/z/sbKMa5K1d
---
 .../lib/Analysis/MemoryDependenceAnalysis.cpp |  3 +-
 llvm/test/Transforms/GVN/strict-vtable.ll     | 38 +++++++++++++++++++
 2 files changed, 40 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/GVN/strict-vtable.ll

diff --git a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
index a5a178c6f2583..730bd1d67f911 100644
--- a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
@@ -323,7 +323,8 @@ MemoryDependenceResults::getInvariantGroupPointerDependency(LoadInst *LI,
 
   for (const Use &Us : LoadOperand->uses()) {
     auto *U = dyn_cast<Instruction>(Us.getUser());
-    if (!U || U == LI || !DT.dominates(U, LI))
+    if (!U || U == LI || U->getFunction() != LI->getFunction() ||
+        !DT.dominates(U, LI))
       continue;
 
     // If we hit load/store with the same invariant.group metadata (and the
diff --git a/llvm/test/Transforms/GVN/strict-vtable.ll b/llvm/test/Transforms/GVN/strict-vtable.ll
new file mode 100644
index 0000000000000..91c6575d7e2fb
--- /dev/null
+++ b/llvm/test/Transforms/GVN/strict-vtable.ll
@@ -0,0 +1,38 @@
+; RUN: opt -passes=gvn -S %s -o /dev/null
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
+
+$_ZTV1i = comdat any
+
+$_ZTI1i = comdat any
+
+$_ZTS1i = comdat any
+
+ at f = dso_local local_unnamed_addr global float 0.000000e+00, align 4
+ at _ZZ1gvE1a = internal global { ptr, ptr } { ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr], [3 x ptr] }, ptr @_ZTV1i, i32 0, i32 0, i32 2), ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr], [3 x ptr] }, ptr @_ZTV1i, i32 0, i32 1, i32 2) }, align 8
+ at _ZTV1i = linkonce_odr dso_local constant { [3 x ptr], [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1i, ptr @_ZN1b1cEv], [3 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1i, ptr @_ZN1d1eEf] }, comdat, align 8
+ at _ZTI1i = linkonce_odr dso_local constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), ptr @_ZTS1i, i32 0, i32 2, ptr @_ZTI1b, i64 2, ptr @_ZTI1d, i64 2050 }, comdat, align 8
+ at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr]
+ at _ZTS1i = linkonce_odr dso_local constant [3 x i8] c"1i\00", comdat, align 1
+ at _ZTI1b = external constant ptr
+ at _ZTI1d = external constant ptr
+
+define dso_local void @_Z1hv() local_unnamed_addr {
+entry:
+  %0 = load float, ptr @f, align 4
+  %vtable = load ptr, ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8), align 8, !invariant.group !0
+  %1 = load ptr, ptr %vtable, align 8, !invariant.load !0
+  tail call void %1(ptr noundef nonnull align 8 dereferenceable(8) getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8), float noundef %0)
+  ret void
+}
+
+define dso_local noundef nonnull align 8 dereferenceable(8) ptr @_Z1gv() local_unnamed_addr {
+entry:
+  ret ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8)
+}
+
+declare noundef i32 @_ZN1b1cEv(ptr noundef nonnull align 8 dereferenceable(8)) unnamed_addr
+
+declare void @_ZN1d1eEf(ptr noundef nonnull align 8 dereferenceable(8), float noundef) unnamed_addr
+
+!0 = !{}

>From e398189d7acbda6c7210cede9b5c8dd98f5fae19 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Thu, 1 Oct 2026 13:03:20 +0200
Subject: [PATCH 2/2] Refactor and minimize test case

---
 .../lib/Analysis/MemoryDependenceAnalysis.cpp |  7 ++--
 llvm/test/Transforms/GVN/strict-vtable.ll     | 41 ++++++++++---------
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
index 730bd1d67f911..d888fff250a0e 100644
--- a/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
+++ b/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp
@@ -304,11 +304,11 @@ MemoryDependenceResults::getInvariantGroupPointerDependency(LoadInst *LI,
   // cast graph down only.
   Value *LoadOperand = LI->getPointerOperand()->stripPointerCasts();
 
-  // It's is not safe to walk the use list of global value, because function
+  // It's is not safe to walk the use list of constant, because function
   // passes aren't allowed to look outside their functions.
   // FIXME: this could be fixed by filtering instructions from outside
   // of current function.
-  if (isa<GlobalValue>(LoadOperand))
+  if (isa<Constant>(LoadOperand))
     return MemDepResult::getUnknown();
 
   Instruction *ClosestDependency = nullptr;
@@ -323,8 +323,7 @@ MemoryDependenceResults::getInvariantGroupPointerDependency(LoadInst *LI,
 
   for (const Use &Us : LoadOperand->uses()) {
     auto *U = dyn_cast<Instruction>(Us.getUser());
-    if (!U || U == LI || U->getFunction() != LI->getFunction() ||
-        !DT.dominates(U, LI))
+    if (!U || U == LI || !DT.dominates(U, LI))
       continue;
 
     // If we hit load/store with the same invariant.group metadata (and the
diff --git a/llvm/test/Transforms/GVN/strict-vtable.ll b/llvm/test/Transforms/GVN/strict-vtable.ll
index 91c6575d7e2fb..9c0bbb7f66c5f 100644
--- a/llvm/test/Transforms/GVN/strict-vtable.ll
+++ b/llvm/test/Transforms/GVN/strict-vtable.ll
@@ -1,38 +1,39 @@
-; RUN: opt -passes=gvn -S %s -o /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=gvn -S %s -o - | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
 
-$_ZTV1i = comdat any
-
-$_ZTI1i = comdat any
-
-$_ZTS1i = comdat any
-
 @f = dso_local local_unnamed_addr global float 0.000000e+00, align 4
- at _ZZ1gvE1a = internal global { ptr, ptr } { ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr], [3 x ptr] }, ptr @_ZTV1i, i32 0, i32 0, i32 2), ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr], [3 x ptr] }, ptr @_ZTV1i, i32 0, i32 1, i32 2) }, align 8
- at _ZTV1i = linkonce_odr dso_local constant { [3 x ptr], [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1i, ptr @_ZN1b1cEv], [3 x ptr] [ptr inttoptr (i64 -8 to ptr), ptr @_ZTI1i, ptr @_ZN1d1eEf] }, comdat, align 8
- at _ZTI1i = linkonce_odr dso_local constant { ptr, ptr, i32, i32, ptr, i64, ptr, i64 } { ptr getelementptr inbounds (ptr, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 2), ptr @_ZTS1i, i32 0, i32 2, ptr @_ZTI1b, i64 2, ptr @_ZTI1d, i64 2050 }, comdat, align 8
- at _ZTVN10__cxxabiv121__vmi_class_type_infoE = external global [0 x ptr]
- at _ZTS1i = linkonce_odr dso_local constant [3 x i8] c"1i\00", comdat, align 1
- at _ZTI1b = external constant ptr
- at _ZTI1d = external constant ptr
+
+ at _ZZ1gvE1a = internal global { ptr, ptr } zeroinitializer
 
 define dso_local void @_Z1hv() local_unnamed_addr {
+; CHECK-LABEL: define dso_local void @_Z1hv() local_unnamed_addr {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr @f, align 4
+; CHECK-NEXT:    [[VTABLE:%.*]] = load ptr, ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8), align 8, !invariant.group [[META0:![0-9]+]]
+; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[VTABLE]], align 8, !invariant.load [[META0]]
+; CHECK-NEXT:    tail call void [[TMP1]](ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8), float noundef [[TMP0]])
+; CHECK-NEXT:    ret void
+;
 entry:
   %0 = load float, ptr @f, align 4
   %vtable = load ptr, ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8), align 8, !invariant.group !0
   %1 = load ptr, ptr %vtable, align 8, !invariant.load !0
-  tail call void %1(ptr noundef nonnull align 8 dereferenceable(8) getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8), float noundef %0)
+  tail call void %1(ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8), float noundef %0)
   ret void
 }
 
-define dso_local noundef nonnull align 8 dereferenceable(8) ptr @_Z1gv() local_unnamed_addr {
+define dso_local ptr @_Z1gv() local_unnamed_addr {
+; CHECK-LABEL: define dso_local ptr @_Z1gv() local_unnamed_addr {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    ret ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8)
+;
 entry:
   ret ptr getelementptr inbounds nuw (i8, ptr @_ZZ1gvE1a, i64 8)
 }
 
-declare noundef i32 @_ZN1b1cEv(ptr noundef nonnull align 8 dereferenceable(8)) unnamed_addr
-
-declare void @_ZN1d1eEf(ptr noundef nonnull align 8 dereferenceable(8), float noundef) unnamed_addr
-
 !0 = !{}
+;.
+; CHECK: [[META0]] = !{}
+;.



More information about the llvm-commits mailing list