Skip to content

Commit 86dbcaf

Browse files
committed
[llvm] Teach GlobalDCE about dso_local_equivalent
This way, C++ relative-vtables can also participate in GlobalDCE. This depends on some TypeMetadataUtils.cpp bits in D134320, but that dependency can be removed and included here if necessary. Differential Revision: https://reviews.llvm.org/D135928
1 parent ab9b2fe commit 86dbcaf

File tree

5 files changed

+87
-14
lines changed

5 files changed

+87
-14
lines changed

llvm/include/llvm/Analysis/TypeMetadataUtils.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ Constant *getPointerAtOffset(Constant *I, uint64_t Offset, Module &M,
7878
Constant *TopLevelGlobal = nullptr);
7979

8080
/// Finds the same "relative pointer" pattern as described above, where the
81-
/// target is `F`, and replaces the entire pattern with a constant zero.
82-
void replaceRelativePointerUsersWithZero(Function *F);
81+
/// target is `C`, and replaces the entire pattern with a constant zero.
82+
void replaceRelativePointerUsersWithZero(Constant *C);
8383

8484
} // namespace llvm
8585

llvm/lib/Analysis/TypeMetadataUtils.cpp

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -213,19 +213,26 @@ Constant *llvm::getPointerAtOffset(Constant *I, uint64_t Offset, Module &M,
213213
return nullptr;
214214
}
215215

216-
void llvm::replaceRelativePointerUsersWithZero(Function *F) {
217-
for (auto *U : F->users()) {
218-
auto *PtrExpr = dyn_cast<ConstantExpr>(U);
219-
if (!PtrExpr || PtrExpr->getOpcode() != Instruction::PtrToInt)
220-
continue;
216+
static void replaceRelativePointerUserWithZero(User *U) {
217+
auto *PtrExpr = dyn_cast<ConstantExpr>(U);
218+
if (!PtrExpr || PtrExpr->getOpcode() != Instruction::PtrToInt)
219+
return;
221220

222-
for (auto *PtrToIntUser : PtrExpr->users()) {
223-
auto *SubExpr = dyn_cast<ConstantExpr>(PtrToIntUser);
224-
if (!SubExpr || SubExpr->getOpcode() != Instruction::Sub)
225-
continue;
221+
for (auto *PtrToIntUser : PtrExpr->users()) {
222+
auto *SubExpr = dyn_cast<ConstantExpr>(PtrToIntUser);
223+
if (!SubExpr || SubExpr->getOpcode() != Instruction::Sub)
224+
return;
226225

227-
SubExpr->replaceNonMetadataUsesWith(
228-
ConstantInt::get(SubExpr->getType(), 0));
229-
}
226+
SubExpr->replaceNonMetadataUsesWith(
227+
ConstantInt::get(SubExpr->getType(), 0));
228+
}
229+
}
230+
231+
void llvm::replaceRelativePointerUsersWithZero(Constant *C) {
232+
for (auto *U : C->users()) {
233+
if (auto *Equiv = dyn_cast<DSOLocalEquivalent>(U))
234+
replaceRelativePointerUsersWithZero(Equiv);
235+
else
236+
replaceRelativePointerUserWithZero(U);
230237
}
231238
}

llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-bad.ll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,32 @@ declare { ptr, i1 } @llvm.type.checked.load(ptr, i32, metadata)
1616

1717
; CHECK: @vtable = internal unnamed_addr constant { [3 x i32] } zeroinitializer, align 8, !type !0, !type !1, !vcall_visibility !2
1818

19+
@vtable2 = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [
20+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc3 to i64), i64 ptrtoint (ptr @vtable2 to i64)) to i32),
21+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc4 to i64), i64 ptrtoint (ptr @vtable2 to i64)) to i32),
22+
23+
; a "bad" relative pointer because it's base is not the @vtable symbol
24+
i32 trunc (i64 sub (i64 ptrtoint (ptr @weird_ref_3 to i64), i64 ptrtoint (ptr @weird_ref_4 to i64)) to i32)
25+
]}, align 4, !type !3, !type !4, !vcall_visibility !{i64 2}
26+
!3 = !{i64 0, !"vfunc3.type"}
27+
!4 = !{i64 4, !"vfunc4.type"}
28+
29+
; CHECK: @vtable2 = internal unnamed_addr constant { [3 x i32] } zeroinitializer, align 4, !type !3, !type !4, !vcall_visibility !2
30+
1931
define internal void @vfunc1() { ret void }
2032
define internal void @vfunc2() { ret void }
2133
define internal void @weird_ref_1() { ret void }
2234
define internal void @weird_ref_2() { ret void }
35+
declare void @vfunc3()
36+
declare void @vfunc4()
37+
declare void @weird_ref_3()
38+
declare void @weird_ref_4()
2339

2440
define void @main() {
2541
%1 = ptrtoint ptr @vtable to i64 ; to keep @vtable alive
2642
call void @weird_ref_2()
43+
%2 = ptrtoint ptr @vtable2 to i64 ; to keep @vtable2 alive
44+
call void @weird_ref_4()
2745
ret void
2846
}
2947

llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-gep.ll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,20 @@ declare { ptr, i1 } @llvm.type.checked.load(ptr, i32, metadata)
1919
; CHECK-SAME: i32 0
2020
; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2
2121

22+
@vtable2 = internal unnamed_addr constant { [4 x i32] } { [4 x i32] [
23+
i32 42,
24+
i32 1337,
25+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc3_live_extern to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32] }, ptr @vtable2, i32 0, i32 0, i32 2) to i64)) to i32),
26+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc4_dead_extern to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32] }, ptr @vtable2, i32 0, i32 0, i32 2) to i64)) to i32)
27+
]}, align 4, !type !3, !type !4, !vcall_visibility !{i64 2}
28+
!3 = !{i64 8, !"vfunc3.type"}
29+
!4 = !{i64 12, !"vfunc4.type"}
30+
31+
; CHECK: @vtable2 = internal unnamed_addr constant { [4 x i32] } { [4 x i32] [
32+
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc3_live_extern to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [4 x i32] }, ptr @vtable2, i32 0, i32 0, i32 2) to i64)) to i32),
33+
; CHECK-SAME: i32 0
34+
; CHECK-SAME: ] }, align 4, !type !3, !type !4, !vcall_visibility !2
35+
2236
; (1) vfunc1_live is referenced from @main, stays alive
2337
define internal void @vfunc1_live() {
2438
; CHECK: define internal void @vfunc1_live(
@@ -31,9 +45,19 @@ define internal void @vfunc2_dead() {
3145
ret void
3246
}
3347

48+
; (3) vfunc3_live_extern is referenced from @main, stays alive
49+
; CHECK: declare void @vfunc3_live_extern
50+
declare void @vfunc3_live_extern()
51+
52+
; (4) vfunc4_dead_extern is never referenced, gets removed and vtable slot is null'd
53+
; CHECK-NOT: declare void @vfunc4_dead_extern
54+
declare void @vfunc4_dead_extern()
55+
3456
define void @main() {
3557
%1 = ptrtoint ptr @vtable to i64 ; to keep @vtable alive
3658
%2 = tail call { ptr, i1 } @llvm.type.checked.load(ptr null, i32 0, metadata !"vfunc1.type")
59+
%3 = ptrtoint ptr @vtable2 to i64 ; to keep @vtable2 alive
60+
%4 = tail call { ptr, i1 } @llvm.type.checked.load(ptr null, i32 0, metadata !"vfunc3.type")
3761
ret void
3862
}
3963

llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers.ll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@ declare { ptr, i1 } @llvm.type.checked.load(ptr, i32, metadata)
1717
; CHECK-SAME: i32 0
1818
; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2
1919

20+
; Similar to above, but the vtable is more aligned to how C++ relative vtables look.
21+
; That is, the functions may not be dso-local.
22+
@vtable2 = internal unnamed_addr constant { [2 x i32] } { [2 x i32] [
23+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc3_live_extern to i64), i64 ptrtoint (ptr @vtable2 to i64)) to i32),
24+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc4_dead_extern to i64), i64 ptrtoint (ptr @vtable2 to i64)) to i32)
25+
]}, align 4, !type !3, !type !4, !vcall_visibility !{i64 2}
26+
!3 = !{i64 0, !"vfunc3.type"}
27+
!4 = !{i64 4, !"vfunc4.type"}
28+
29+
; CHECK: @vtable2 = internal unnamed_addr constant { [2 x i32] } { [2 x i32] [
30+
; CHECK-SAME: i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vfunc3_live_extern to i64), i64 ptrtoint (ptr @vtable2 to i64)) to i32),
31+
; CHECK-SAME: i32 0
32+
; CHECK-SAME: ] }, align 4, !type !3, !type !4, !vcall_visibility !2
33+
2034
; (1) vfunc1_live is referenced from @main, stays alive
2135
define internal void @vfunc1_live() {
2236
; CHECK: define internal void @vfunc1_live(
@@ -29,9 +43,19 @@ define internal void @vfunc2_dead() {
2943
ret void
3044
}
3145

46+
; (3) vfunc3_live_extern is referenced from @main, stays alive
47+
; CHECK: declare void @vfunc3_live_extern
48+
declare void @vfunc3_live_extern()
49+
50+
; (4) vfunc4_dead_extern is never referenced, gets removed and vtable slot is null'd
51+
; CHECK-NOT: declare void @vfunc4_dead_extern
52+
declare void @vfunc4_dead_extern()
53+
3254
define void @main() {
3355
%1 = ptrtoint ptr @vtable to i64 ; to keep @vtable alive
3456
%2 = tail call { ptr, i1 } @llvm.type.checked.load(ptr null, i32 0, metadata !"vfunc1.type")
57+
%3 = ptrtoint ptr @vtable2 to i64 ; to keep @vtable2 alive
58+
%4 = tail call { ptr, i1 } @llvm.type.checked.load(ptr null, i32 0, metadata !"vfunc3.type")
3559
ret void
3660
}
3761

0 commit comments

Comments
 (0)