[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