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