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

>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 01/13] [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 7c463f51f63dc..090eb4c16ce0b 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 0000000000000..9408d87495c60
--- /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 bdb477b54b532..87da08dba34c7 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 c01de4a289a69..b5dbfdd24657d 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 7b47bc88ddb25..c2bdab430b689 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 9eb38c3e44829..41cb7a5922088 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 3c586a1dd21d8..717bb37685f52 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 242a05f7d32c0..085e6eaddc490 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 0000000000000..37f4b43f95845
--- /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 0000000000000..906abf3d1a9ed
--- /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 611d4bfbc69e8..ec0aa81d05f8a 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 9ec0785eb5034..9271f9c3d5b0b 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 5ea507c009bdc..b6d09f0992c79 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 be6166f0c4169..e33ef2e13755c 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 56f5ff4b20e5d..434b3ef8a5867 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 31c96400dd0fe..4b1fc6cf8a955 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 a37a8901489cf..e4d242792247a 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 0f53c60851217..49a0c31af5e99 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 8a2dddce4892c..d0566f0d008d3 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 2ecaea4b02bf6..50eb6ac3a5073 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 0000000000000..f3b38b94d35c2
--- /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 15cb57399d246..6627ca72b3d95 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 0000000000000..bef2f52cd4d37
--- /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 0000000000000..3707edc34e718
--- /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 0000000000000..7d9b1907db35f
--- /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 0000000000000..11d0d9cf6ad9c
--- /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"}

>From 0d5c7e493e9f912c0aece774024711601e2af6d8 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 10:31:29 +0800
Subject: [PATCH 02/13] use getModule

---
 llvm/lib/Analysis/TypeMetadataUtils.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/TypeMetadataUtils.cpp 
b/llvm/lib/Analysis/TypeMetadataUtils.cpp
index 9271f9c3d5b0b..e60b439a16201 100644
--- a/llvm/lib/Analysis/TypeMetadataUtils.cpp
+++ b/llvm/lib/Analysis/TypeMetadataUtils.cpp
@@ -150,7 +150,7 @@ bool llvm::hasTypeIdLoadForTypeTest(const CallInst *CI) {
     return false;
   SmallVector<CallInst *, 1> Assumes;
 
-  const Module *M = CI->getParent()->getParent()->getParent();
+  const Module *M = CI->getModule();
 
   // Find llvm.assume intrinsics for this llvm.type.test call.
   for (const Use &CIU : CI->uses())

>From 9dea1da60c4bebe3b64fecbe7755eb8a63edf881 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 11:09:01 +0800
Subject: [PATCH 03/13] add comment

---
 llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp 
b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index ec0aa81d05f8a..f203e34885ed2 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -916,6 +916,8 @@ static void setLiveRoot(ModuleSummaryIndex &Index, 
StringRef Name) {
       Summary->setLive(true);
 }
 
+// Return true if the User U is reachable from a non-vtable user
+// through the use-def chain.
 static bool hasNonVTableUsers(const User *U, CXXABI *ABI) {
   LLVM_DEBUG(dbgs() << "Check if " << *U << "has vtable users\n");
   if (isa<Instruction>(U)) {

>From 8a91bb3cad99f0c88fc2528fe31034c4d0059611 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 11:25:23 +0800
Subject: [PATCH 04/13] add comment

---
 llvm/include/llvm/Analysis/TypeMetadataUtils.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/include/llvm/Analysis/TypeMetadataUtils.h 
b/llvm/include/llvm/Analysis/TypeMetadataUtils.h
index 87da08dba34c7..c553de833a203 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);
 
+/// Given a call to the intrinsic \@llvm.type.test, return true if a type id
+/// load exists that associated with this intrinsic call.
 bool hasTypeIdLoadForTypeTest(const CallInst *CI);
 
 /// Given a call to the intrinsic \@llvm.type.checked.load, find all

>From e51483879773ad7bfcbb1a195342760434d12937 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 13:06:56 +0800
Subject: [PATCH 05/13] don't perform optimization if there is no type metadata

---
 .../Transforms/IPO/DeadRTTIElimination.cpp    |  3 ++
 .../test/ThinLTO/X86/rtti-no-type-metadata.ll | 32 +++++++++++++++++++
 2 files changed, 35 insertions(+)
 create mode 100644 llvm/test/ThinLTO/X86/rtti-no-type-metadata.ll

diff --git a/llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp 
b/llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp
index bef2f52cd4d37..2c54098200fa7 100644
--- a/llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp
+++ b/llvm/lib/Transforms/IPO/DeadRTTIElimination.cpp
@@ -14,6 +14,9 @@ void DeadRTTIElimIndex::run() {
   if (!ABI)
     return;
 
+  if (ExportSummary.typeIdCompatibleVtableMap().empty())
+    return;
+
   DenseSet<StringRef> TypeIdSlotMayLiveVTables;
 
   const auto &UsedTypeIds = ExportSummary.getTypeIdAccessed();
diff --git a/llvm/test/ThinLTO/X86/rtti-no-type-metadata.ll 
b/llvm/test/ThinLTO/X86/rtti-no-type-metadata.ll
new file mode 100644
index 0000000000000..9c68a559280f0
--- /dev/null
+++ b/llvm/test/ThinLTO/X86/rtti-no-type-metadata.ll
@@ -0,0 +1,32 @@
+; 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 @_ZTIvt, 
ptr @vf] }, !vcall_visitbiliy !0
+@_ZTVvt = weak_odr constant %vtTy { [3 x ptr] [ptr null, ptr @_ZTIvt, ptr @vf] 
}, !vcall_visitbiliy !0
+
+define internal void @vf() {
+  ret void
+}
+
+define void @main() {
+  %vfunc = load ptr, ptr @_ZTVvt
+  ret void
+}
+
+!0 = !{i64 1}

>From 843d6c5c9f6310c2124e216884eef5f757a9fd50 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 13:55:51 +0800
Subject: [PATCH 06/13] add comment

---
 llvm/lib/LTO/LTOBackend.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index d0566f0d008d3..3ce60a7be9610 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -581,6 +581,12 @@ static void dropDeadSymbols(Module &Mod, const 
GVSummaryMapTy &DefinedGlobals,
   // themselves when possible.
   for (GlobalValue *GV : DeadGVs) {
     GV->removeDeadConstantUsers();
+
+    // The RTTI data consists of both type information and the type name 
string.
+    // Although they are considered dead, there are still users that reference 
them.
+    // For example, the type information might be used by a vtable, and the 
type name
+    // string might be used by the type info.
+    // Therefore, we need to replace these uses to null pointer before erasing 
them.
     if (ABI && (ABI->isTypeInfo(GV->getName()) ||
                             ABI->isTypeName(GV->getName()))) {
       GV->replaceAllUsesWith(

>From 7d9f3477d66eabe279e878b41948584d834534ea Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 14:09:18 +0800
Subject: [PATCH 07/13] Use SetVector for element uniqueness

---
 llvm/include/llvm/IR/ModuleSummaryIndex.h | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h 
b/llvm/include/llvm/IR/ModuleSummaryIndex.h
index 717bb37685f52..8650c6a124dbb 100644
--- a/llvm/include/llvm/IR/ModuleSummaryIndex.h
+++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h
@@ -20,6 +20,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
@@ -1379,7 +1380,7 @@ class ModuleSummaryIndex {
       TypeIdCompatibleVtableMap;
 
   /// Type identifiers that may be accessed at run time.
-  SmallVector<StringRef, 0> TypeIdMayBeAccessed;
+  SetVector<StringRef> TypeIdMayBeAccessed;
 
   /// Mapping from original ID to GUID. If original ID can map to multiple
   /// GUIDs, it will be mapped to 0.
@@ -1892,7 +1893,7 @@ class ModuleSummaryIndex {
   }
 
   void addTypeIdAccessed(StringRef TypeId) {
-    TypeIdMayBeAccessed.push_back(TypeId);
+    TypeIdMayBeAccessed.insert(TypeId);
   }
 
   const auto &getTypeIdAccessed() const { return TypeIdMayBeAccessed; }

>From 2e8ae2257d0e21a96bff4d4c9ab6d92c598a3299 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 14:25:31 +0800
Subject: [PATCH 08/13] add header file comment

---
 llvm/include/llvm/Support/LibCXXABI.h | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/llvm/include/llvm/Support/LibCXXABI.h 
b/llvm/include/llvm/Support/LibCXXABI.h
index 37f4b43f95845..e9fb80746d696 100644
--- a/llvm/include/llvm/Support/LibCXXABI.h
+++ b/llvm/include/llvm/Support/LibCXXABI.h
@@ -1,3 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// This file provides utility functions for interacting with the libc++abi
+/// runtime.
+///
+/// This header defines helper functions used for handling libc++abi-specific
+/// runtime operations, such as judging if a symbol name is the name of vtable,
+/// type information or type name string.
+///
+/// The utilities provided here are useful for transformations that require
+/// analyzing or modifying libc++abi-specific constructs.
+//
+//===----------------------------------------------------------------------===//
+//
 #ifndef LLVM_SUPPORT_LIBCXXABI_H
 #define LLVM_SUPPORT_LIBCXXABI_H
 

>From 774d2685427f7c36787473db3c0b7d393caf8d70 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Wed, 12 Feb 2025 15:05:25 +0800
Subject: [PATCH 09/13] add comment

---
 llvm/include/llvm/Support/LibCXXABI.h | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/llvm/include/llvm/Support/LibCXXABI.h 
b/llvm/include/llvm/Support/LibCXXABI.h
index e9fb80746d696..c9f0aa99070fe 100644
--- a/llvm/include/llvm/Support/LibCXXABI.h
+++ b/llvm/include/llvm/Support/LibCXXABI.h
@@ -27,6 +27,16 @@
 
 namespace llvm {
 
+
+/// Abstract interface for handling C++ ABI (Application Binary Interface) 
specifics.
+///
+/// This class provides an abstraction for interacting with different C++ ABIs,
+/// particularly in the context of name mangling and vtable layout.
+/// This allows the CXX ABI-aware optimizations to correctly identify and 
transform
+/// RTTI data and vtables.
+///
+/// Note this is an abstract class that should be subclassed to provide
+/// implementation details for a specific C++ ABI such as Itanium or MSVC.
 class CXXABI {
 
   virtual const char * getVTablePrefix() = 0;
@@ -36,6 +46,9 @@ class CXXABI {
 public:
   static std::unique_ptr<CXXABI> Create(Triple &TT);
   virtual ~CXXABI() {}
+
+  /// Return the offset from the type info slot to its address point
+  /// in the vtable.
   virtual int64_t
   getOffsetFromTypeInfoSlotToAddressPoint(const DataLayout &DT) = 0;
 
@@ -47,10 +60,20 @@ class CXXABI {
     return Name.starts_with(getTypeInfoPrefix());
   }
 
+  /// Return the name of type name string from the given name of the
+  /// type info.
   std::string getTypeNameFromTypeInfo(StringRef TypeInfo);
+
+  /// Return the name of the type info from the given name of the vtable.
   std::string getTypeInfoFromVTable(StringRef VTable);
 };
 
+/// Implements C++ ABI support for the Itanium ABI.
+///
+/// This class provides functionality specific to the Itanium C++ ABI.
+/// It extends the `CXXABI` interface to implement ABI-specific operations.
+///
+/// See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#rtti
 class Itanium final : public CXXABI {
 
   const char * getVTablePrefix() override { return "_ZTV"; }

>From b0b4238c2818c6f83cefa4ebb1c29274bbd066f3 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Fri, 14 Feb 2025 10:52:47 +0800
Subject: [PATCH 10/13] bug fix

---
 llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp 
b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index f203e34885ed2..6e69651f1df13 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -968,7 +968,6 @@ static void analyzeRTTIVars(ModuleSummaryIndex &Index, 
const Module &M) {
         Index.addTypeIdAccessed(TypeNameGV->getName());
       else
         Index.addTypeIdAccessed(Index.saveString(TypeName));
-      break;
     }
   }
 }

>From 03034c995f119865586fe3d43b85551f32c98539 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Fri, 14 Feb 2025 11:32:25 +0800
Subject: [PATCH 11/13] add test case

---
 llvm/test/ThinLTO/X86/rtti-dont-clean.ll | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/llvm/test/ThinLTO/X86/rtti-dont-clean.ll 
b/llvm/test/ThinLTO/X86/rtti-dont-clean.ll
index 11d0d9cf6ad9c..7b2069cb06a37 100644
--- a/llvm/test/ThinLTO/X86/rtti-dont-clean.ll
+++ b/llvm/test/ThinLTO/X86/rtti-dont-clean.ll
@@ -5,7 +5,12 @@
 ; RUN:           -r=%t.o,_ZTSvt,p \
 ; RUN:           -r=%t.o,_ZTIvt,p \
 ; RUN:           -r=%t.o,_ZTVvt,p \
+; RUN:           -r=%t.o,_ZTSvt1,p \
+; RUN:           -r=%t.o,_ZTIvt1,p \
+; RUN:           -r=%t.o,_ZTVvt1,p \
 ; RUN:           -r=%t.o,use,p \
+; RUN:           -r=%t.o,dyncast,p \
+; RUN:           -r=%t.o,__dynamic_cast,px \
 ; RUN:           -whole-program-visibility
 ; RUN: llvm-dis %t2.1.1.promote.bc -o - | FileCheck %s
 ;
@@ -16,10 +21,15 @@ target triple = "x86_64-unknown-linux-gnu"
 @_ZTSvt = external constant ptr
 @_ZTIvt = weak_odr constant { ptr } { ptr @_ZTSvt }
 
+@_ZTSvt1 = external constant ptr
+@_ZTIvt1 = weak_odr constant { ptr } { ptr @_ZTSvt1 }
+
 %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
+; CHECK: @_ZTVvt1 = weak_odr constant %vtTy { [3 x ptr] [ptr null, ptr 
@_ZTIvt1, ptr @vf] }, !type !2, !vcall_visitbiliy !1
+@_ZTVvt1 = weak_odr constant %vtTy { [3 x ptr] [ptr null, ptr @_ZTIvt1, ptr 
@vf] }, !type !2, !vcall_visitbiliy !1
 
 define internal void @vf() {
   ret void
@@ -34,11 +44,19 @@ define ptr @use(ptr %p) {
   ret ptr %rtti
 }
 
+define ptr @dyncast(ptr %p) {
+  %r = call ptr @__dynamic_cast(ptr %p, ptr @_ZTIvt1, ptr @_ZTIvt1, i64 0)
+  ret ptr %r
+}
+
+; Make symbol _ZTVvt and _ZTVvt1 alive.
 define void @main() {
   %vfunc = load ptr, ptr @_ZTVvt
+  %vfunc1 = load ptr, ptr @_ZTVvt1
   ret void
 }
 
+declare ptr @__dynamic_cast(ptr, ptr, ptr, i64)
 declare void @llvm.assume(i1)
 declare i1 @llvm.type.test(ptr %ptr, metadata %type) nounwind readnone
 !0 = !{i32 16, !"_ZTSvt"}

>From 69d3fdb1c89a63f6d907e3503be7638a5250814c Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Fri, 14 Feb 2025 13:46:49 +0800
Subject: [PATCH 12/13] Replace isVTable with checking the type metadata

---
 llvm/lib/Analysis/ModuleSummaryAnalysis.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp 
b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index 6e69651f1df13..14b6808356e18 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -943,7 +943,8 @@ static bool hasNonVTableUsers(const User *U, CXXABI *ABI) {
     return false;
   }
 
-  if (!ABI->isVTable(Name))
+  auto *GV = dyn_cast<GlobalVariable>(U);
+  if (!GV || GV->getMetadata(LLVMContext::MD_type))
     return true;
 
   return false;

>From e6a36067e25a4312fa939954d005b7215bd8fd35 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981...@gmail.com>
Date: Fri, 14 Feb 2025 14:57:17 +0800
Subject: [PATCH 13/13] remove unused triple

---
 llvm/lib/Analysis/TypeMetadataUtils.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Analysis/TypeMetadataUtils.cpp 
b/llvm/lib/Analysis/TypeMetadataUtils.cpp
index e60b439a16201..afd1a51148da8 100644
--- a/llvm/lib/Analysis/TypeMetadataUtils.cpp
+++ b/llvm/lib/Analysis/TypeMetadataUtils.cpp
@@ -54,7 +54,6 @@ 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();

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

Reply via email to