[clang] [llvm] [ThinLTO] Support dead RTTI data elimination under -fno-split-lto-unit (PR #126336)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 11 23:05:48 PST 2025
https://github.com/luxufan updated https://github.com/llvm/llvm-project/pull/126336
>From 83b532a3382a07e472558b8813d43a6f51801423 Mon Sep 17 00:00:00 2001
From: luxufan <luxufan981014 at gmail.com>
Date: Thu, 23 Jan 2025 09:28:15 +0800
Subject: [PATCH 1/9] [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"
+
+ at _ZTSxxx = external global ptr
+ at _ZTIxxx = external global ptr
+ at 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"
+
+ at _ZTSvt = external constant ptr
+ at _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
+ at _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"
+
+ at _ZTSvt = external constant ptr
+ at _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
+ at _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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 10:31:29 +0800
Subject: [PATCH 2/9] 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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 11:09:01 +0800
Subject: [PATCH 3/9] 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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 11:25:23 +0800
Subject: [PATCH 4/9] 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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 13:06:56 +0800
Subject: [PATCH 5/9] 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"
+
+ at _ZTSvt = external constant ptr
+ at _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
+ at _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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 13:55:51 +0800
Subject: [PATCH 6/9] 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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 14:09:18 +0800
Subject: [PATCH 7/9] 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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 14:25:31 +0800
Subject: [PATCH 8/9] 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 <luxufan981014 at gmail.com>
Date: Wed, 12 Feb 2025 15:05:25 +0800
Subject: [PATCH 9/9] 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"; }
More information about the llvm-commits
mailing list