[GlobalDCE] In VFE, replace the whole 'sub' expression of unused relative-pointer...
authorKuba Mracek <mracek@apple.com>
Wed, 6 Oct 2021 22:55:00 +0000 (15:55 -0700)
committerKuba Mracek <mracek@apple.com>
Wed, 6 Oct 2021 22:55:55 +0000 (15:55 -0700)
Differential Revision: https://reviews.llvm.org/D109114

llvm/include/llvm/Analysis/TypeMetadataUtils.h
llvm/lib/Analysis/TypeMetadataUtils.cpp
llvm/lib/Transforms/IPO/GlobalDCE.cpp
llvm/test/Transforms/GlobalDCE/call-with-ptrtoint.ll [new file with mode: 0644]
llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-bad.ll
llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers-gep.ll
llvm/test/Transforms/GlobalDCE/virtual-functions-relative-pointers.ll

index 6ca1124..074c409 100644 (file)
@@ -22,6 +22,7 @@ namespace llvm {
 class CallBase;
 class CallInst;
 class Constant;
+class Function;
 class DominatorTree;
 class Instruction;
 class Module;
@@ -56,25 +57,30 @@ void findDevirtualizableCallsForTypeCheckedLoad(
     SmallVectorImpl<Instruction *> &Preds, bool &HasNonCallUses,
     const CallInst *CI, DominatorTree &DT);
 
-// Processes a Constant recursively looking into elements of arrays, structs and
-// expressions to find a trivial pointer element that is located at the given
-// offset (relative to the beginning of the whole outer Constant).
-//
-// Used for example from GlobalDCE to find an entry in a C++ vtable that matches
-// a vcall offset.
-//
-// To support Swift vtables, getPointerAtOffset can see through "relative
-// pointers", i.e. (sub-)expressions of the form of:
-//
-//   @symbol = ... {
-//     i32 trunc (i64 sub (
-//       i64 ptrtoint (<type> @target to i64), i64 ptrtoint (... @symbol to i64)
-//     ) to i32)
-//   }
-//
-// For such (sub-)expressions, getPointerAtOffset returns the @target pointer.
+/// Processes a Constant recursively looking into elements of arrays, structs
+/// and expressions to find a trivial pointer element that is located at the
+/// given offset (relative to the beginning of the whole outer Constant).
+///
+/// Used for example from GlobalDCE to find an entry in a C++ vtable that
+/// matches a vcall offset.
+///
+/// To support Swift vtables, getPointerAtOffset can see through "relative
+/// pointers", i.e. (sub-)expressions of the form of:
+///
+/// @symbol = ... {
+///   i32 trunc (i64 sub (
+///     i64 ptrtoint (<type> @target to i64), i64 ptrtoint (... @symbol to i64)
+///   ) to i32)
+/// }
+///
+/// For such (sub-)expressions, getPointerAtOffset returns the @target pointer.
 Constant *getPointerAtOffset(Constant *I, uint64_t Offset, Module &M,
                              Constant *TopLevelGlobal = nullptr);
-}
+
+/// Finds the same "relative pointer" pattern as described above, where the
+/// target is `F`, and replaces the entire pattern with a constant zero.
+void replaceRelativePointerUsersWithZero(Function *F);
+
+} // namespace llvm
 
 #endif
index 65fa2bb..80051fd 100644 (file)
@@ -197,3 +197,20 @@ Constant *llvm::getPointerAtOffset(Constant *I, uint64_t Offset, Module &M,
   }
   return nullptr;
 }
+
+void llvm::replaceRelativePointerUsersWithZero(Function *F) {
+  for (auto *U : F->users()) {
+    auto *PtrExpr = dyn_cast<ConstantExpr>(U);
+    if (!PtrExpr || PtrExpr->getOpcode() != Instruction::PtrToInt)
+      continue;
+
+    for (auto *PtrToIntUser : PtrExpr->users()) {
+      auto *SubExpr = dyn_cast<ConstantExpr>(PtrToIntUser);
+      if (!SubExpr || SubExpr->getOpcode() != Instruction::Sub)
+        continue;
+
+      SubExpr->replaceNonMetadataUsesWith(
+          ConstantInt::get(SubExpr->getType(), 0));
+    }
+  }
+}
index 018a594..9977fa7 100644 (file)
@@ -416,6 +416,16 @@ PreservedAnalyses GlobalDCEPass::run(Module &M, ModuleAnalysisManager &MAM) {
       // virtual function pointers with null, allowing us to remove the
       // function itself.
       ++NumVFuncs;
+
+      // Detect vfuncs that are referenced as "relative pointers" which are used
+      // in Swift vtables, i.e. entries in the form of:
+      //
+      //   i32 trunc (i64 sub (i64 ptrtoint @f, i64 ptrtoint ...)) to i32)
+      //
+      // In this case, replace the whole "sub" expression with constant 0 to
+      // avoid leaving a weird sub(0, symbol) expression behind.
+      replaceRelativePointerUsersWithZero(F);
+
       F->replaceNonMetadataUsesWith(ConstantPointerNull::get(F->getType()));
     }
     EraseUnusedGlobalValue(F);
diff --git a/llvm/test/Transforms/GlobalDCE/call-with-ptrtoint.ll b/llvm/test/Transforms/GlobalDCE/call-with-ptrtoint.ll
new file mode 100644 (file)
index 0000000..bff769b
--- /dev/null
@@ -0,0 +1,21 @@
+; RUN: opt < %s -globaldce -S
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+declare { i8*, i1 } @llvm.type.checked.load(i8*, i32, metadata)
+
+define internal void @foo() {
+  call void @bar_with_fptr_argument(i64 ptrtoint (void ()* @baz to i64))
+  ret void
+}
+
+define internal void @bar_with_fptr_argument(i64 %0) {
+  ret void
+}
+
+define internal void @baz() {
+  ret void
+}
+
+!999 = !{i32 1, !"Virtual Function Elim", i32 1}
+!llvm.module.flags = !{!999}
index 9562d3d..c386e6c 100644 (file)
@@ -14,11 +14,7 @@ declare { i8*, i1 } @llvm.type.checked.load(i8*, i32, metadata)
 !0 = !{i64 0, !"vfunc1.type"}
 !1 = !{i64 4, !"vfunc2.type"}
 
-; CHECK:      @vtable = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [
-; CHECK-SAME:   i32 trunc (i64 sub (i64 0, i64 ptrtoint ({ [3 x i32] }* @vtable to i64)) to i32),
-; CHECK-SAME:   i32 trunc (i64 sub (i64 0, i64 ptrtoint ({ [3 x i32] }* @vtable to i64)) to i32),
-; CHECK-SAME:   i32 trunc (i64 sub (i64 0, i64 ptrtoint (void ()* @weird_ref_2 to i64)) to i32)
-; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2
+; CHECK:      @vtable = internal unnamed_addr constant { [3 x i32] } zeroinitializer, align 8, !type !0, !type !1, !vcall_visibility !2
 
 define internal void @vfunc1() { ret void }
 define internal void @vfunc2() { ret void }
index e49dc78..7ec29cc 100644 (file)
@@ -16,7 +16,7 @@ declare { i8*, i1 } @llvm.type.checked.load(i8*, i32, metadata)
 
 ; CHECK:      @vtable = internal unnamed_addr constant { [4 x i32] } { [4 x i32] [
 ; CHECK-SAME:   i32 trunc (i64 sub (i64 ptrtoint (void ()* @vfunc1_live              to i64), i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @vtable, i32 0, i32 0, i32 2) to i64)) to i32),
-; CHECK-SAME:   i32 trunc (i64 sub (i64 0,                                                    i64 ptrtoint (i32* getelementptr inbounds ({ [4 x i32] }, { [4 x i32] }* @vtable, i32 0, i32 0, i32 2) to i64)) to i32)
+; CHECK-SAME:   i32 0
 ; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2
 
 ; (1) vfunc1_live is referenced from @main, stays alive
index c2bbf00..685f375 100644 (file)
@@ -14,7 +14,7 @@ declare { i8*, i1 } @llvm.type.checked.load(i8*, i32, metadata)
 
 ; CHECK:      @vtable = internal unnamed_addr constant { [2 x i32] } { [2 x i32] [
 ; CHECK-SAME:   i32 trunc (i64 sub (i64 ptrtoint (void ()* @vfunc1_live              to i64), i64 ptrtoint ({ [2 x i32] }* @vtable to i64)) to i32),
-; CHECK-SAME:   i32 trunc (i64 sub (i64 0,                                                    i64 ptrtoint ({ [2 x i32] }* @vtable to i64)) to i32)
+; CHECK-SAME:   i32 0
 ; CHECK-SAME: ] }, align 8, !type !0, !type !1, !vcall_visibility !2
 
 ; (1) vfunc1_live is referenced from @main, stays alive