https://github.com/luxufan created 
https://github.com/llvm/llvm-project/pull/126336

This commit enhances the ThinLTO pipeline to support the elimination of unused 
Run-Time Type Information (RTTI) data when the `-fno-split-lto-unit` flag is 
used. Previously, dead RTTI data was not effectively removed, leading to larger 
binary sizes.

>From 83b532a3382a07e472558b8813d43a6f51801423 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Thu, 23 Jan 2025 09:28:15 +0800
Subject: [PATCH] [ThinLTO] Support dead RTTI data elimination under
 -fno-split-lto-unit

This commit enhances the ThinLTO pipeline to support the elimination of
unused Run-Time Type Information (RTTI) data when the
`-fno-split-lto-unit` flag is used. Previously, dead RTTI data was not
effectively removed, leading to larger binary sizes.
---
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  1 +
 clang/test/CodeGenCXX/typeid-type-test.cpp    | 32 +++++++++
 .../include/llvm/Analysis/TypeMetadataUtils.h |  2 +
 llvm/include/llvm/AsmParser/LLParser.h        |  1 +
 llvm/include/llvm/AsmParser/LLToken.h         |  1 +
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |  1 +
 llvm/include/llvm/IR/ModuleSummaryIndex.h     | 22 +++++++
 llvm/include/llvm/LTO/LTO.h                   | 12 +++-
 llvm/include/llvm/Support/LibCXXABI.h         | 49 ++++++++++++++
 .../llvm/Transforms/IPO/DeadRTTIElimination.h | 21 ++++++
 llvm/lib/Analysis/ModuleSummaryAnalysis.cpp   | 66 ++++++++++++++++++-
 llvm/lib/Analysis/TypeMetadataUtils.cpp       | 61 +++++++++++++++++
 llvm/lib/AsmParser/LLLexer.cpp                |  1 +
 llvm/lib/AsmParser/LLParser.cpp               | 30 +++++++++
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     | 13 ++++
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     | 15 +++++
 llvm/lib/IR/AsmWriter.cpp                     | 12 ++++
 llvm/lib/LTO/LTO.cpp                          | 34 +++++++---
 llvm/lib/LTO/LTOBackend.cpp                   | 16 ++++-
 llvm/lib/Support/CMakeLists.txt               |  1 +
 llvm/lib/Support/LibCXXABI.cpp                | 25 +++++++
 llvm/lib/Transforms/IPO/CMakeLists.txt        |  1 +
 .../Transforms/IPO/DeadRTTIElimination.cpp    | 46 +++++++++++++
 llvm/test/Assembler/thinlto-rtti-summary.ll   | 20 ++++++
 llvm/test/ThinLTO/X86/rtti-clean.ll           | 34 ++++++++++
 llvm/test/ThinLTO/X86/rtti-dont-clean.ll      | 46 +++++++++++++
 26 files changed, 547 insertions(+), 16 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/typeid-type-test.cpp
 create mode 100644 llvm/include/llvm/Support/LibCXXABI.h
 create mode 100644 llvm/include/llvm/Transforms/IPO/DeadRTTIElimination.h
 create mode 100644 llvm/lib/Support/LibCXXABI.cpp
 create mode 100644 llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp
 create mode 100644 llvm/test/Assembler/thinlto-rtti-summary.ll
 create mode 100644 llvm/test/ThinLTO/X86/rtti-clean.ll
 create mode 100644 llvm/test/ThinLTO/X86/rtti-dont-clean.ll

diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp 
b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 7c463f51f63dc5c..090eb4c16ce0b47 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1592,6 +1592,7 @@ llvm::Value *ItaniumCXXABI::EmitTypeid(CodeGenFunction 
&CGF,
       cast<CXXRecordDecl>(SrcRecordTy->castAs<RecordType>()->getDecl());
   llvm::Value *Value = CGF.GetVTablePtr(ThisPtr, CGM.GlobalsInt8PtrTy,
                                         ClassDecl);
+  CGF.EmitTypeMetadataCodeForVCall(ClassDecl, Value, SourceLocation());
 
   if (CGM.getItaniumVTableContext().isRelativeLayout()) {
     // Load the type info.
diff --git a/clang/test/CodeGenCXX/typeid-type-test.cpp 
b/clang/test/CodeGenCXX/typeid-type-test.cpp
new file mode 100644
index 000000000000000..9408d87495c608b
--- /dev/null
+++ b/clang/test/CodeGenCXX/typeid-type-test.cpp
@@ -0,0 +1,32 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 5
+// RUN: %clang_cc1 -I%S -triple x86_64-unknown-linux -flto 
-fwhole-program-vtables -fvisibility=hidden -emit-llvm -o - %s | FileCheck %s
+
+#include <typeinfo>
+
+namespace Test1 {
+struct A { virtual void f(); };
+
+// CHECK-LABEL: define hidden noundef nonnull align 8 dereferenceable(16) ptr 
@_ZN5Test19gettypeidEPNS_1AE(
+// CHECK-SAME: ptr noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[A]], ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = icmp eq ptr [[TMP0]], null
+// CHECK-NEXT:    br i1 [[TMP1]], label %[[TYPEID_BAD_TYPEID:.*]], label 
%[[TYPEID_END:.*]]
+// CHECK:       [[TYPEID_BAD_TYPEID]]:
+// CHECK-NEXT:    call void @__cxa_bad_typeid() #[[ATTR3:[0-9]+]]
+// CHECK-NEXT:    unreachable
+// CHECK:       [[TYPEID_END]]:
+// CHECK-NEXT:    [[VTABLE:%.*]] = load ptr, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = call i1 @llvm.type.test(ptr [[VTABLE]], 
metadata !"_ZTSN5Test11AE")
+// CHECK-NEXT:    call void @llvm.assume(i1 [[TMP2]])
+// CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds ptr, ptr [[VTABLE]], 
i64 -1
+// CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[TMP3]], align 8
+// CHECK-NEXT:    ret ptr [[TMP4]]
+//
+const std::type_info &gettypeid(A *a) {
+  return typeid(*a);
+}
+
+}
diff --git a/llvm/include/llvm/Analysis/TypeMetadataUtils.h 
b/llvm/include/llvm/Analysis/TypeMetadataUtils.h
index bdb477b54b532d0..87da08dba34c745 100644
--- a/llvm/include/llvm/Analysis/TypeMetadataUtils.h
+++ b/llvm/include/llvm/Analysis/TypeMetadataUtils.h
@@ -51,6 +51,8 @@ void findDevirtualizableCallsForTypeTest(
     SmallVectorImpl<CallInst *> &Assumes, const CallInst *CI,
     DominatorTree &DT);
 
+bool hasTypeIdLoadForTypeTest(const CallInst *CI);
+
 /// Given a call to the intrinsic \@llvm.type.checked.load, find all
 /// devirtualizable call sites based on the call and return them in 
DevirtCalls.
 void findDevirtualizableCallsForTypeCheckedLoad(
diff --git a/llvm/include/llvm/AsmParser/LLParser.h 
b/llvm/include/llvm/AsmParser/LLParser.h
index c01de4a289a69a0..b5dbfdd24657d6c 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -427,6 +427,7 @@ namespace llvm {
     bool parseTypeIdEntry(unsigned ID);
     bool parseTypeIdSummary(TypeIdSummary &TIS);
     bool parseTypeIdCompatibleVtableEntry(unsigned ID);
+    bool parseTypeIdMayBeAccessed(unsigned ID);
     bool parseTypeTestResolution(TypeTestResolution &TTRes);
     bool parseOptionalWpdResolutions(
         std::map<uint64_t, WholeProgramDevirtResolution> &WPDResMap);
diff --git a/llvm/include/llvm/AsmParser/LLToken.h 
b/llvm/include/llvm/AsmParser/LLToken.h
index 7b47bc88ddb25ff..c2bdab430b68947 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -422,6 +422,7 @@ enum Kind {
   kw_args,
   kw_typeid,
   kw_typeidCompatibleVTable,
+  kw_typeidMayBeAccessed,
   kw_summary,
   kw_typeTestRes,
   kw_kind,
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h 
b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 9eb38c3e4482910..41cb7a5922088c4 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -335,6 +335,7 @@ enum GlobalValueSummarySymtabCodes {
   // CallStackRadixTreeBuilder class in ProfileData/MemProf.h for format.
   // [n x entry]
   FS_CONTEXT_RADIX_TREE_ARRAY = 32,
+  FS_RTTI = 33,
 };
 
 enum MetadataCodes {
diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h 
b/llvm/include/llvm/IR/ModuleSummaryIndex.h
index 3c586a1dd21d823..717bb37685f529a 100644
--- a/llvm/include/llvm/IR/ModuleSummaryIndex.h
+++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h
@@ -643,6 +643,19 @@ class GlobalValueSummary {
   /// Return the list of values referenced by this global value definition.
   ArrayRef<ValueInfo> refs() const { return RefEdgeList; }
 
+  /// Erase all reference whose name is equal to Name.
+  bool eraseRef(StringRef Name) {
+    bool Erased = false;
+    erase_if(RefEdgeList, [&](ValueInfo VI) {
+      if (VI.name() == Name) {
+        Erased = true;
+        return true;
+      }
+      return false;
+    });
+    return Erased;
+  }
+
   /// If this is an alias summary, returns the summary of the aliased object (a
   /// global variable or function), otherwise returns itself.
   GlobalValueSummary *getBaseObject();
@@ -1365,6 +1378,9 @@ class ModuleSummaryIndex {
   std::map<StringRef, TypeIdCompatibleVtableInfo, std::less<>>
       TypeIdCompatibleVtableMap;
 
+  /// Type identifiers that may be accessed at run time.
+  SmallVector<StringRef, 0> TypeIdMayBeAccessed;
+
   /// Mapping from original ID to GUID. If original ID can map to multiple
   /// GUIDs, it will be mapped to 0.
   DenseMap<GlobalValue::GUID, GlobalValue::GUID> OidGuidMap;
@@ -1875,6 +1891,12 @@ class ModuleSummaryIndex {
     return I->second;
   }
 
+  void addTypeIdAccessed(StringRef TypeId) {
+    TypeIdMayBeAccessed.push_back(TypeId);
+  }
+
+  const auto &getTypeIdAccessed() const { return TypeIdMayBeAccessed; }
+
   /// Collect for the given module the list of functions it defines
   /// (GUID -> Summary).
   void collectDefinedFunctionsForModule(StringRef ModulePath,
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index 242a05f7d32c025..085e6eaddc4904e 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -386,6 +386,8 @@ class LTO {
 private:
   Config Conf;
 
+  std::string TargetTriple;
+
   struct RegularLTOState {
     RegularLTOState(unsigned ParallelCodeGenParallelismLevel,
                     const Config &Conf);
@@ -520,11 +522,17 @@ class LTO {
                    const SymbolResolution *&ResI, const SymbolResolution 
*ResE);
 
   Error runRegularLTO(AddStreamFn AddStream);
-  Error runThinLTO(AddStreamFn AddStream, FileCache Cache,
-                   const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols);
+  Error
+  runThinLTO(AddStreamFn AddStream, FileCache Cache,
+             const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols,
+             function_ref<PrevailingType(GlobalValue::GUID)> isPrevailing);
 
   Error checkPartiallySplit();
 
+  std::string & getTargetTriple() { return TargetTriple; }
+
+  void setTargetTriple(std::string TT) { TargetTriple = std::move(TT); }
+
   mutable bool CalledGetMaxTasks = false;
 
   // LTO mode when using Unified LTO.
diff --git a/llvm/include/llvm/Support/LibCXXABI.h 
b/llvm/include/llvm/Support/LibCXXABI.h
new file mode 100644
index 000000000000000..37f4b43f9584563
--- /dev/null
+++ b/llvm/include/llvm/Support/LibCXXABI.h
@@ -0,0 +1,49 @@
+#ifndef LLVM_SUPPORT_LIBCXXABI_H
+#define LLVM_SUPPORT_LIBCXXABI_H
+
+#include "llvm/IR/DataLayout.h"
+#include "llvm/TargetParser/Triple.h"
+
+namespace llvm {
+
+class CXXABI {
+
+  virtual const char * getVTablePrefix() = 0;
+  virtual const char * getTypeNamePrefix() = 0;
+  virtual const char * getTypeInfoPrefix() = 0;
+
+public:
+  static std::unique_ptr<CXXABI> Create(Triple &TT);
+  virtual ~CXXABI() {}
+  virtual int64_t
+  getOffsetFromTypeInfoSlotToAddressPoint(const DataLayout &DT) = 0;
+
+  bool isVTable(StringRef Name) { return Name.starts_with(getVTablePrefix()); }
+  bool isTypeName(StringRef Name) {
+    return Name.starts_with(getTypeNamePrefix());
+  }
+  bool isTypeInfo(StringRef Name) {
+    return Name.starts_with(getTypeInfoPrefix());
+  }
+
+  std::string getTypeNameFromTypeInfo(StringRef TypeInfo);
+  std::string getTypeInfoFromVTable(StringRef VTable);
+};
+
+class Itanium final : public CXXABI {
+
+  const char * getVTablePrefix() override { return "_ZTV"; }
+  const char * getTypeNamePrefix() override { return "_ZTS"; }
+  const char * getTypeInfoPrefix() override { return "_ZTI"; }
+
+public:
+  virtual ~Itanium() {}
+
+  int64_t
+  getOffsetFromTypeInfoSlotToAddressPoint(const DataLayout &DL) override {
+    return -2 * static_cast<int64_t>(DL.getPointerSize());
+  }
+};
+
+} // namespace llvm
+#endif
diff --git a/llvm/include/llvm/Transforms/IPO/DeadRTTIElimination.h 
b/llvm/include/llvm/Transforms/IPO/DeadRTTIElimination.h
new file mode 100644
index 000000000000000..906abf3d1a9ed27
--- /dev/null
+++ b/llvm/include/llvm/Transforms/IPO/DeadRTTIElimination.h
@@ -0,0 +1,21 @@
+#ifndef LLVM_TRANSFORMS_IPO_DEADRTTIELIMINATION_H
+#define LLVM_TRANSFORMS_IPO_DEADRTTIELIMINATION_H
+
+#include "llvm/IR/ModuleSummaryIndex.h"
+#include "llvm/Support/LibCXXABI.h"
+#include "llvm/TargetParser/Triple.h"
+
+namespace llvm {
+class DeadRTTIElimIndex {
+  ModuleSummaryIndex &ExportSummary;
+  std::unique_ptr<CXXABI> ABI;
+
+public:
+  DeadRTTIElimIndex(ModuleSummaryIndex &ExportSummary, Triple &TT)
+      : ExportSummary(ExportSummary), ABI(CXXABI::Create(TT)) {}
+
+  void run();
+};
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_SCALAR_DEADRTTIELIMINATION_H
diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp 
b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index 611d4bfbc69e8fe..ec0aa81d05f8a04 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -52,6 +52,7 @@
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LibCXXABI.h"
 #include <cassert>
 #include <cstdint>
 #include <vector>
@@ -202,7 +203,7 @@ static void addVCallToSet(
 /// If this intrinsic call requires that we add information to the function
 /// summary, do so via the non-constant reference arguments.
 static void addIntrinsicToSummary(
-    const CallInst *CI,
+    ModuleSummaryIndex &Index, const CallInst *CI,
     SetVector<GlobalValue::GUID, std::vector<GlobalValue::GUID>> &TypeTests,
     SetVector<FunctionSummary::VFuncId, std::vector<FunctionSummary::VFuncId>>
         &TypeTestAssumeVCalls,
@@ -241,6 +242,10 @@ static void addIntrinsicToSummary(
       addVCallToSet(Call, Guid, TypeTestAssumeVCalls,
                     TypeTestAssumeConstVCalls);
 
+    if (Triple(CI->getModule()->getTargetTriple()).getOS() == Triple::Linux &&
+        hasTypeIdLoadForTypeTest(CI))
+      Index.addTypeIdAccessed(TypeId->getString());
+
     break;
   }
 
@@ -431,7 +436,7 @@ static void computeFunctionSummary(
       if (CalledFunction) {
         if (CI && CalledFunction->isIntrinsic()) {
           addIntrinsicToSummary(
-              CI, TypeTests, TypeTestAssumeVCalls, TypeCheckedLoadVCalls,
+              Index, CI, TypeTests, TypeTestAssumeVCalls, 
TypeCheckedLoadVCalls,
               TypeTestAssumeConstVCalls, TypeCheckedLoadConstVCalls, DT);
           continue;
         }
@@ -911,6 +916,61 @@ static void setLiveRoot(ModuleSummaryIndex &Index, 
StringRef Name) {
       Summary->setLive(true);
 }
 
+static bool hasNonVTableUsers(const User *U, CXXABI *ABI) {
+  LLVM_DEBUG(dbgs() << "Check if " << *U << "has vtable users\n");
+  if (isa<Instruction>(U)) {
+    // If the type info is used in dynamic_cast or exception handling,
+    // its user must be the instruction.
+    return true;
+  }
+
+  // The virtual table type is either a struct of arrays. For example:
+  // @vtable = constant { [3 x ptr] } { [3 x ptr] [ ptr null, ptr @rtti, ptr 
@vf] }
+  //
+  // In this case, the user of @rtti is an anonymous ConstantArray.
+  // Therefore, if the user of the type information is anonymous,
+  // we need to perform a depth-first search (DFS) to locate its named users.
+  //
+  // And we also need to iterate its users if the current user is the type
+  // info global variable itself.
+  StringRef Name = U->getName();
+  if (Name.empty() || ABI->isTypeInfo(Name)) {
+    for (const User *It : U->users())
+      if (hasNonVTableUsers(It, ABI))
+        return true;
+    return false;
+  }
+
+  if (!ABI->isVTable(Name))
+    return true;
+
+  return false;
+}
+
+static void analyzeRTTIVars(ModuleSummaryIndex &Index, const Module &M) {
+  Triple TT(M.getTargetTriple());
+
+  std::unique_ptr<CXXABI> ABI = CXXABI::Create(TT);
+  if (!ABI)
+    return;
+
+  for (const GlobalVariable &GV : M.globals()) {
+    if (!ABI->isTypeInfo(GV.getName()))
+      continue;
+
+    if (hasNonVTableUsers(&GV, ABI.get())) {
+      std::string TypeName =
+          ABI->getTypeNameFromTypeInfo(GV.getName());
+      const GlobalVariable *TypeNameGV = M.getNamedGlobal(TypeName);
+      if (TypeNameGV)
+        Index.addTypeIdAccessed(TypeNameGV->getName());
+      else
+        Index.addTypeIdAccessed(Index.saveString(TypeName));
+      break;
+    }
+  }
+}
+
 ModuleSummaryIndex llvm::buildModuleSummaryIndex(
     const Module &M,
     std::function<BlockFrequencyInfo *(const Function &F)> GetBFICallback,
@@ -1019,6 +1079,8 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
           mdconst::extract_or_null<ConstantInt>(M.getModuleFlag("ThinLTO")))
     IsThinLTO = MD->getZExtValue();
 
+  analyzeRTTIVars(Index, M);
+
   // Compute summaries for all functions defined in module, and save in the
   // index.
   for (const auto &F : M) {
diff --git a/llvm/lib/Analysis/TypeMetadataUtils.cpp 
b/llvm/lib/Analysis/TypeMetadataUtils.cpp
index 9ec0785eb5034d6..9271f9c3d5b0baa 100644
--- a/llvm/lib/Analysis/TypeMetadataUtils.cpp
+++ b/llvm/lib/Analysis/TypeMetadataUtils.cpp
@@ -17,6 +17,7 @@
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Module.h"
+#include "llvm/Support/LibCXXABI.h"
 
 using namespace llvm;
 
@@ -50,6 +51,42 @@ findCallsAtConstantOffset(SmallVectorImpl<DevirtCallSite> 
&DevirtCalls,
   }
 }
 
+static bool hasTypeIdLoadAtConstantOffset(const Module *M, Value *VPtr,
+                                          int64_t Offset, const CallInst *CI,
+                                          CXXABI *ABI) {
+  Triple TT(M->getTargetTriple());
+  bool HasTypeIdLoad = false;
+  for (const Use &U : VPtr->uses()) {
+    Value *User = U.getUser();
+    if (isa<BitCastInst>(User)) {
+      HasTypeIdLoad |= hasTypeIdLoadAtConstantOffset(M, User, Offset, CI, ABI);
+    } else if (isa<LoadInst>(User)) {
+      if (Offset ==
+          ABI->getOffsetFromTypeInfoSlotToAddressPoint(M->getDataLayout()))
+        return true;
+    } else if (auto GEP = dyn_cast<GetElementPtrInst>(User)) {
+      // Take into account the GEP offset.
+      if (VPtr == GEP->getPointerOperand() && GEP->hasAllConstantIndices()) {
+        SmallVector<Value *, 8> Indices(drop_begin(GEP->operands()));
+        int64_t GEPOffset = M->getDataLayout().getIndexedOffsetInType(
+            GEP->getSourceElementType(), Indices);
+        HasTypeIdLoad |=
+            hasTypeIdLoadAtConstantOffset(M, User, Offset + GEPOffset, CI, 
ABI);
+      }
+    } else if (auto *Call = dyn_cast<CallInst>(User)) {
+      if (Call->getIntrinsicID() == llvm::Intrinsic::load_relative) {
+        if (auto *LoadOffset = dyn_cast<ConstantInt>(Call->getOperand(1))) {
+          HasTypeIdLoad |=
+              hasTypeIdLoadAtConstantOffset(M, User, Offset, CI, ABI);
+        }
+      }
+    } else {
+      HasTypeIdLoad = true;
+    }
+  }
+  return HasTypeIdLoad;
+}
+
 // Search for virtual calls that load from VPtr and add them to DevirtCalls.
 static void findLoadCallsAtConstantOffset(
     const Module *M, SmallVectorImpl<DevirtCallSite> &DevirtCalls, Value *VPtr,
@@ -103,6 +140,30 @@ void llvm::findDevirtualizableCallsForTypeTest(
         M, DevirtCalls, CI->getArgOperand(0)->stripPointerCasts(), 0, CI, DT);
 }
 
+bool llvm::hasTypeIdLoadForTypeTest(const CallInst *CI) {
+  assert(CI->getCalledFunction()->getIntrinsicID() == Intrinsic::type_test ||
+         CI->getCalledFunction()->getIntrinsicID() ==
+             Intrinsic::public_type_test);
+  Triple TT(CI->getModule()->getTargetTriple());
+  std::unique_ptr<CXXABI> ABI = CXXABI::Create(TT);
+  if (!ABI)
+    return false;
+  SmallVector<CallInst *, 1> Assumes;
+
+  const Module *M = CI->getParent()->getParent()->getParent();
+
+  // Find llvm.assume intrinsics for this llvm.type.test call.
+  for (const Use &CIU : CI->uses())
+    if (auto *Assume = dyn_cast<AssumeInst>(CIU.getUser()))
+      Assumes.push_back(Assume);
+
+  if (!Assumes.empty())
+    return hasTypeIdLoadAtConstantOffset(
+        M, CI->getArgOperand(0)->stripPointerCasts(), 0, CI, ABI.get());
+
+  return false;
+}
+
 void llvm::findDevirtualizableCallsForTypeCheckedLoad(
     SmallVectorImpl<DevirtCallSite> &DevirtCalls,
     SmallVectorImpl<Instruction *> &LoadedPtrs,
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 5ea507c009bdc61..b6d09f0992c7994 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -826,6 +826,7 @@ lltok::Kind LLLexer::LexIdentifier() {
   KEYWORD(args);
   KEYWORD(typeid);
   KEYWORD(typeidCompatibleVTable);
+  KEYWORD(typeidMayBeAccessed);
   KEYWORD(summary);
   KEYWORD(typeTestRes);
   KEYWORD(kind);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index be6166f0c41694f..e33ef2e13755c54 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -1118,6 +1118,9 @@ bool LLParser::parseSummaryEntry() {
   case lltok::kw_typeidCompatibleVTable:
     result = parseTypeIdCompatibleVtableEntry(SummaryID);
     break;
+  case lltok::kw_typeidMayBeAccessed:
+    result = parseTypeIdMayBeAccessed(SummaryID);
+    break;
   case lltok::kw_flags:
     result = parseSummaryIndexFlags();
     break;
@@ -8918,6 +8921,33 @@ bool LLParser::parseTypeIdSummary(TypeIdSummary &TIS) {
 static ValueInfo EmptyVI =
     ValueInfo(false, (GlobalValueSummaryMapTy::value_type *)-8);
 
+bool LLParser::parseTypeIdMayBeAccessed(unsigned ID) {
+  assert(Lex.getKind() == lltok::kw_typeidMayBeAccessed);
+  Lex.Lex();
+
+  std::string Name;
+  if (parseToken(lltok::colon, "expected ':' here") ||
+      parseToken(lltok::lparen, "expected '(' here") ||
+      parseToken(lltok::kw_name, "expected 'name' here") ||
+      parseToken(lltok::colon, "expected ':' here") ||
+      parseStringConstant(Name))
+    return true;
+
+  Index->addTypeIdAccessed(Index->saveString(Name));
+
+  while (Lex.getKind() != lltok::rparen) {
+    if (parseToken(lltok::comma, "expected ',' here") ||
+        parseStringConstant(Name))
+      return true;
+    Index->addTypeIdAccessed(Index->saveString(Name));
+  }
+
+  if (parseToken(lltok::rparen, "expected ')' here"))
+    return true;
+
+  return false;
+}
+
 /// TypeIdCompatibleVtableEntry
 ///   ::= 'typeidCompatibleVTable' ':' '(' 'name' ':' STRINGCONSTANT ','
 ///   TypeIdCompatibleVtableInfo
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp 
b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 56f5ff4b20e5dbf..434b3ef8a586702 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -1015,6 +1015,7 @@ class ModuleSummaryIndexBitcodeReader : public 
BitcodeReaderBase {
   void parseTypeIdCompatibleVtableSummaryRecord(ArrayRef<uint64_t> Record);
   void parseTypeIdCompatibleVtableInfo(ArrayRef<uint64_t> Record, size_t &Slot,
                                        TypeIdCompatibleVtableInfo &TypeId);
+  void parseTypeIdAccessed(ArrayRef<uint64_t> Record);
   std::vector<FunctionSummary::ParamAccess>
   parseParamAccesses(ArrayRef<uint64_t> Record);
   SmallVector<unsigned> parseAllocInfoContext(ArrayRef<uint64_t> Record,
@@ -7575,6 +7576,14 @@ void 
ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableInfo(
   TypeId.push_back({Offset, Callee});
 }
 
+void ModuleSummaryIndexBitcodeReader::parseTypeIdAccessed(
+    ArrayRef<uint64_t> Record) {
+  for (unsigned I = 0; I < Record.size(); I += 2) {
+    TheIndex.addTypeIdAccessed(
+        {Strtab.data() + Record[I], static_cast<size_t>(Record[I + 1])});
+  }
+}
+
 void ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableSummaryRecord(
     ArrayRef<uint64_t> Record) {
   size_t Slot = 0;
@@ -8071,6 +8080,10 @@ Error 
ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) {
       parseTypeIdCompatibleVtableSummaryRecord(Record);
       break;
 
+    case bitc::FS_RTTI:
+      parseTypeIdAccessed(Record);
+      break;
+
     case bitc::FS_BLOCK_COUNT:
       TheIndex.addBlockCount(Record[0]);
       break;
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp 
b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 31c96400dd0fe51..4b1fc6cf8a95584 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -4601,6 +4601,12 @@ void 
ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() {
   Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8));
   unsigned RadixAbbrev = Stream.EmitAbbrev(std::move(Abbv));
 
+  Abbv = std::make_shared<BitCodeAbbrev>();
+  Abbv->Add(BitCodeAbbrevOp(bitc::FS_RTTI));
+  Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
+  Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8));
+  unsigned RTTIAccessedAbbrev = Stream.EmitAbbrev(std::move(Abbv));
+
   // First walk through all the functions and collect the allocation contexts 
in
   // their associated summaries, for use in constructing a radix tree of
   // contexts. Note that we need to do this in the same order as the functions
@@ -4690,6 +4696,15 @@ void 
ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() {
     NameVals.clear();
   }
 
+  if (!Index->getTypeIdAccessed().empty()) {
+    for (auto TypeId : Index->getTypeIdAccessed()) {
+      NameVals.push_back(StrtabBuilder.add(TypeId));
+      NameVals.push_back(TypeId.size());
+    }
+    Stream.EmitRecord(bitc::FS_RTTI, NameVals, RTTIAccessedAbbrev);
+    NameVals.clear();
+  }
+
   if (Index->getBlockCount())
     Stream.EmitRecord(bitc::FS_BLOCK_COUNT,
                       ArrayRef<uint64_t>{Index->getBlockCount()});
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index a37a8901489cf70..e4d242792247a5a 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -3128,6 +3128,18 @@ void AssemblyWriter::printModuleSummaryIndex() {
     Out << ") ; guid = " << GUID << "\n";
   }
 
+  // Print the TypeIdMayBeAccessed entries.
+  if (!TheIndex->getTypeIdAccessed().empty()) {
+    Out << "^" << NumSlots << " = typeidMayBeAccessed: (name: ";
+    FieldSeparator FS;
+    for (auto TypeId : TheIndex->getTypeIdAccessed()) {
+      Out << FS;
+      Out << "\"" << TypeId << "\"";
+    }
+    Out << ")\n";
+    ++NumSlots;
+  }
+
   // Don't emit flags when it's not really needed (value is zero by default).
   if (TheIndex->getFlags()) {
     Out << "^" << NumSlots << " = flags: " << TheIndex->getFlags() << "\n";
diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp
index 0f53c6085121719..49a0c31af5e99c7 100644
--- a/llvm/lib/LTO/LTO.cpp
+++ b/llvm/lib/LTO/LTO.cpp
@@ -55,6 +55,7 @@
 #include "llvm/Transforms/IPO.h"
 #include "llvm/Transforms/IPO/MemProfContextDisambiguation.h"
 #include "llvm/Transforms/IPO/WholeProgramDevirt.h"
+#include "llvm/Transforms/IPO/DeadRTTIElimination.h"
 #include "llvm/Transforms/Utils/FunctionImportUtils.h"
 #include "llvm/Transforms/Utils/SplitModule.h"
 
@@ -729,6 +730,9 @@ Error LTO::add(std::unique_ptr<InputFile> Input,
                ArrayRef<SymbolResolution> Res) {
   assert(!CalledGetMaxTasks);
 
+  if (getTargetTriple().empty())
+    setTargetTriple(Input->getTargetTriple().str());
+
   if (Conf.ResolutionFile)
     writeToResolutionFile(*Conf.ResolutionFile, Input.get(), Res);
 
@@ -1187,8 +1191,10 @@ Error LTO::run(AddStreamFn AddStream, FileCache Cache) {
       return PrevailingType::Unknown;
     return It->second;
   };
-  computeDeadSymbolsWithConstProp(ThinLTO.CombinedIndex, GUIDPreservedSymbols,
-                                  isPrevailing, Conf.OptLevel > 0);
+
+  if (!RegularLTO.ModsWithSummaries.empty())
+    computeDeadSymbolsWithConstProp(ThinLTO.CombinedIndex, 
GUIDPreservedSymbols,
+                                    isPrevailing, Conf.OptLevel > 0);
 
   // Setup output file to emit statistics.
   auto StatsFileOrErr = setupStatsFile(Conf.StatsFile);
@@ -1208,7 +1214,7 @@ Error LTO::run(AddStreamFn AddStream, FileCache Cache) {
   if (!Result)
     // This will reset the GlobalResolutions optional once done with it to
     // reduce peak memory before importing.
-    Result = runThinLTO(AddStream, Cache, GUIDPreservedSymbols);
+    Result = runThinLTO(AddStream, Cache, GUIDPreservedSymbols, isPrevailing);
 
   if (StatsFile)
     PrintStatisticsJSON(StatsFile->os());
@@ -1839,8 +1845,10 @@ ThinBackend lto::createWriteIndexesThinBackend(
   return ThinBackend(Func, Parallelism);
 }
 
-Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
-                      const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) 
{
+Error LTO::runThinLTO(
+    AddStreamFn AddStream, FileCache Cache,
+    const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols,
+    function_ref<PrevailingType(GlobalValue::GUID)> IsPrevailing) {
   LLVM_DEBUG(dbgs() << "Running ThinLTO\n");
   ThinLTO.CombinedIndex.releaseTemporaryMemory();
   timeTraceProfilerBegin("ThinLink", StringRef(""));
@@ -1856,10 +1864,6 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache 
Cache,
     return Error::success();
   }
 
-  if (Conf.CombinedIndexHook &&
-      !Conf.CombinedIndexHook(ThinLTO.CombinedIndex, GUIDPreservedSymbols))
-    return Error::success();
-
   // Collect for each module the list of function it defines (GUID ->
   // Summary).
   DenseMap<StringRef, GVSummaryMapTy> ModuleToDefinedGVSummaries(
@@ -1920,6 +1924,18 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache 
Cache,
       ThinLTO.CombinedIndex, WholeProgramVisibilityEnabledInLTO,
       DynamicExportSymbols, VisibleToRegularObjSymbols);
 
+  Triple TT(getTargetTriple());
+  DeadRTTIElimIndex(ThinLTO.CombinedIndex, TT).run();
+
+  if (!ThinLTO.CombinedIndex.withGlobalValueDeadStripping())
+    computeDeadSymbolsWithConstProp(ThinLTO.CombinedIndex, 
GUIDPreservedSymbols,
+                                    IsPrevailing, Conf.OptLevel > 0);
+
+
+  if (Conf.CombinedIndexHook &&
+      !Conf.CombinedIndexHook(ThinLTO.CombinedIndex, GUIDPreservedSymbols))
+    return Error::success();
+
   // Perform index-based WPD. This will return immediately if there are
   // no index entries in the typeIdMetadata map (e.g. if we are instead
   // performing IR-based WPD in hybrid regular/thin LTO mode).
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index 8a2dddce4892c1a..d0566f0d008d340 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -33,6 +33,7 @@
 #include "llvm/Passes/StandardInstrumentations.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LibCXXABI.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/Program.h"
@@ -573,14 +574,23 @@ static void dropDeadSymbols(Module &Mod, const 
GVSummaryMapTy &DefinedGlobals,
         convertToDeclaration(GV);
       }
 
+  Triple TT(Mod.getTargetTriple());
+  std::unique_ptr<CXXABI> ABI = CXXABI::Create(TT);
+
   // Now that all dead bodies have been dropped, delete the actual objects
   // themselves when possible.
   for (GlobalValue *GV : DeadGVs) {
     GV->removeDeadConstantUsers();
-    // Might reference something defined in native object (i.e. dropped a
-    // non-prevailing IR def, but we need to keep the declaration).
-    if (GV->use_empty())
+    if (ABI && (ABI->isTypeInfo(GV->getName()) ||
+                            ABI->isTypeName(GV->getName()))) {
+      GV->replaceAllUsesWith(
+          ConstantPointerNull::get(PointerType::get(Mod.getContext(), 0)));
+      GV->eraseFromParent();
+    } else if (GV->use_empty()) {
+      // Might reference something defined in native object (i.e. dropped a
+      // non-prevailing IR def, but we need to keep the declaration).
       GV->eraseFromParent();
+    }
   }
 }
 
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 2ecaea4b02bf618..50eb6ac3a507307 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -206,6 +206,7 @@ add_llvm_component_library(LLVMSupport
   KnownBits.cpp
   LEB128.cpp
   LineIterator.cpp
+  LibCXXABI.cpp
   Locale.cpp
   LockFileManager.cpp
   ManagedStatic.cpp
diff --git a/llvm/lib/Support/LibCXXABI.cpp b/llvm/lib/Support/LibCXXABI.cpp
new file mode 100644
index 000000000000000..f3b38b94d35c2ed
--- /dev/null
+++ b/llvm/lib/Support/LibCXXABI.cpp
@@ -0,0 +1,25 @@
+#include "llvm/Support/LibCXXABI.h"
+
+namespace llvm {
+
+std::unique_ptr<CXXABI> CXXABI::Create(Triple &TT) {
+  if (TT.getOS() == Triple::Linux)
+    return std::make_unique<Itanium>();
+
+  return nullptr;
+}
+
+std::string CXXABI::getTypeNameFromTypeInfo(StringRef TypeInfo) {
+  assert(TypeInfo.starts_with(getTypeInfoPrefix()) &&
+         "TypeInfo is not starts with the correct type infor prefix");
+  TypeInfo.consume_front(getTypeInfoPrefix());
+  return getTypeNamePrefix() + TypeInfo.str();
+}
+
+std::string CXXABI::getTypeInfoFromVTable(StringRef VTable) {
+  assert(VTable.starts_with(getVTablePrefix()) &&
+         "TypeInfo is not starts with the correct type infor prefix");
+  VTable.consume_front(getVTablePrefix());
+  return getTypeInfoPrefix() + VTable.str();
+}
+} // namespace llvm
diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt 
b/llvm/lib/Transforms/IPO/CMakeLists.txt
index 15cb57399d2460c..6627ca72b3d9567 100644
--- a/llvm/lib/Transforms/IPO/CMakeLists.txt
+++ b/llvm/lib/Transforms/IPO/CMakeLists.txt
@@ -10,6 +10,7 @@ add_llvm_component_library(LLVMipo
   ConstantMerge.cpp
   CrossDSOCFI.cpp
   DeadArgumentElimination.cpp
+  DeadRTTIElimination.cpp
   ElimAvailExtern.cpp
   EmbedBitcodePass.cpp
   ExpandVariadics.cpp
diff --git a/llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp 
b/llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp
new file mode 100644
index 000000000000000..bef2f52cd4d3715
--- /dev/null
+++ b/llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp
@@ -0,0 +1,46 @@
+#include "llvm/Transforms/IPO/DeadRTTIElimination.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/LibCXXABI.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "dre"
+
+STATISTIC(NumDeadTypeInfo, "Number of dead type info global variable");
+
+void DeadRTTIElimIndex::run() {
+  if (!ABI)
+    return;
+
+  DenseSet<StringRef> TypeIdSlotMayLiveVTables;
+
+  const auto &UsedTypeIds = ExportSummary.getTypeIdAccessed();
+  for (StringRef TypeId : UsedTypeIds) {
+    auto Info = ExportSummary.getTypeIdCompatibleVtableSummary(TypeId);
+
+    if (!Info.has_value())
+      continue;
+
+    for (auto CompatibleVTable : *Info)
+      TypeIdSlotMayLiveVTables.insert(CompatibleVTable.VTableVI.name());
+  }
+
+  for (auto &VI : ExportSummary) {
+    StringRef GVSName = VI.second.U.Name;
+    if (!ABI->isVTable(GVSName) ||
+        TypeIdSlotMayLiveVTables.contains(GVSName) ||
+        VI.second.SummaryList.empty())
+      continue;
+
+    auto *GVS = dyn_cast<GlobalVarSummary>(VI.second.SummaryList[0].get());
+    if (GVS &&
+        GVS->getVCallVisibility() == llvm::GlobalObject::VCallVisibilityPublic)
+      continue;
+
+    ++NumDeadTypeInfo;
+    for (auto &SL : VI.second.SummaryList)
+      SL->eraseRef(ABI->getTypeInfoFromVTable(GVSName));
+  }
+}
diff --git a/llvm/test/Assembler/thinlto-rtti-summary.ll 
b/llvm/test/Assembler/thinlto-rtti-summary.ll
new file mode 100644
index 000000000000000..3707edc34e7183f
--- /dev/null
+++ b/llvm/test/Assembler/thinlto-rtti-summary.ll
@@ -0,0 +1,20 @@
+; RUN: llvm-as %s -o - | llvm-dis -o %t.ll
+; RUN: grep "^\^" %s >%t2
+; RUN: grep "^\^" %t.ll >%t3
+; Expect that the summary information is the same after round-trip through
+; llvm-as and llvm-dis.
+; RUN: diff -b %t2 %t3
+
+target triple = "aarch64-unknown-linux-gnu"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+@_ZTSxxx = external global ptr
+@_ZTIxxx = external global ptr
+@xxx = constant [1 x ptr] [ptr @_ZTIxxx]
+
+^0 = module: (path: "<stdin>", hash: (0, 0, 0, 0, 0))
+^1 = gv: (name: "_ZTIxxx") ; guid = 2928584540419986814
+^2 = gv: (name: "xxx", summaries: (variable: (module: ^0, flags: (linkage: 
external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, 
canAutoHide: 0, importType: definition), varFlags: (readonly: 1, writeonly: 0, 
constant: 1), refs: (^1)))) ; guid = 5616283335571169781
+^3 = gv: (name: "_ZTSxxx") ; guid = 16805677846636166078
+^4 = typeidMayBeAccessed: (name: "_ZTSxxx")
+^5 = blockcount: 0
diff --git a/llvm/test/ThinLTO/X86/rtti-clean.ll 
b/llvm/test/ThinLTO/X86/rtti-clean.ll
new file mode 100644
index 000000000000000..7d9b1907db35fd6
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/rtti-clean.ll
@@ -0,0 +1,34 @@
+; RUN: opt -thinlto-bc -o %t.o %s
+;
+; RUN: llvm-lto2 run %t.o -o %t2 -save-temps \
+; RUN:           -r=%t.o,main,px \
+; RUN:           -r=%t.o,_ZTSvt,p \
+; RUN:           -r=%t.o,_ZTIvt,p \
+; RUN:           -r=%t.o,_ZTVvt,p \
+; RUN:           -whole-program-visibility
+; RUN: llvm-dis %t2.1.1.promote.bc -o - | FileCheck %s
+;
+
+target datalayout = 
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@_ZTSvt = external constant ptr
+@_ZTIvt = weak_odr constant { ptr } { ptr @_ZTSvt }
+
+%vtTy = type { [3 x ptr] }
+
+; CHECK: @_ZTVvt = weak_odr constant %vtTy { [3 x ptr] [ptr null, ptr null, 
ptr @vf] }, !type !0, !vcall_visitbiliy !1
+@_ZTVvt = weak_odr constant %vtTy { [3 x ptr] [ptr null, ptr @_ZTIvt, ptr @vf] 
}, !type !0, !vcall_visitbiliy !1
+
+define internal void @vf() {
+  ret void
+}
+
+define void @main() {
+  %vfunc = load ptr, ptr @_ZTVvt
+  ret void
+}
+
+!0 = !{i32 16, !"_ZTSvt"}
+!1 = !{i64 1}
+!2 = !{i32 16, !"_ZTSvt1"}
diff --git a/llvm/test/ThinLTO/X86/rtti-dont-clean.ll 
b/llvm/test/ThinLTO/X86/rtti-dont-clean.ll
new file mode 100644
index 000000000000000..11d0d9cf6ad9c43
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/rtti-dont-clean.ll
@@ -0,0 +1,46 @@
+; RUN: opt -thinlto-bc -o %t.o %s
+;
+; RUN: llvm-lto2 run %t.o -o %t2 -save-temps \
+; RUN:           -r=%t.o,main,px \
+; RUN:           -r=%t.o,_ZTSvt,p \
+; RUN:           -r=%t.o,_ZTIvt,p \
+; RUN:           -r=%t.o,_ZTVvt,p \
+; RUN:           -r=%t.o,use,p \
+; RUN:           -whole-program-visibility
+; RUN: llvm-dis %t2.1.1.promote.bc -o - | FileCheck %s
+;
+
+target datalayout = 
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@_ZTSvt = external constant ptr
+@_ZTIvt = weak_odr constant { ptr } { ptr @_ZTSvt }
+
+%vtTy = type { [3 x ptr] }
+
+; CHECK: @_ZTVvt = weak_odr constant %vtTy { [3 x ptr] [ptr null, ptr @_ZTIvt, 
ptr @vf] }, !type !0, !vcall_visitbiliy !1
+@_ZTVvt = weak_odr constant %vtTy { [3 x ptr] [ptr null, ptr @_ZTIvt, ptr @vf] 
}, !type !0, !vcall_visitbiliy !1
+
+define internal void @vf() {
+  ret void
+}
+
+define ptr @use(ptr %p) {
+  %vtable = load ptr, ptr %p
+  %x = call i1 @llvm.type.test(ptr %vtable, metadata !"_ZTSvt")
+  call void @llvm.assume(i1 %x)
+  %rttiptr = getelementptr i8, ptr %vtable, i64 -16
+  %rtti = load ptr, ptr %rttiptr
+  ret ptr %rtti
+}
+
+define void @main() {
+  %vfunc = load ptr, ptr @_ZTVvt
+  ret void
+}
+
+declare void @llvm.assume(i1)
+declare i1 @llvm.type.test(ptr %ptr, metadata %type) nounwind readnone
+!0 = !{i32 16, !"_ZTSvt"}
+!1 = !{i64 1}
+!2 = !{i32 16, !"_ZTSvt1"}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to