diff --git a/clang/docs/ThinLTO.rst b/clang/docs/ThinLTO.rst --- a/clang/docs/ThinLTO.rst +++ b/clang/docs/ThinLTO.rst @@ -213,7 +213,7 @@ `_ when configuring the bootstrap compiler build: - * ``-DLLVM_ENABLE_LTO=thin`` + * ``-DLLVM_ENABLE_LTO=Thin`` * ``-DCMAKE_C_COMPILER=/path/to/host/clang`` * ``-DCMAKE_CXX_COMPILER=/path/to/host/clang++`` * ``-DCMAKE_RANLIB=/path/to/host/llvm-ranlib`` @@ -221,7 +221,7 @@ Or, on Windows: - * ``-DLLVM_ENABLE_LTO=thin`` + * ``-DLLVM_ENABLE_LTO=Thin`` * ``-DCMAKE_C_COMPILER=/path/to/host/clang-cl.exe`` * ``-DCMAKE_CXX_COMPILER=/path/to/host/clang-cl.exe`` * ``-DCMAKE_LINKER=/path/to/host/lld-link.exe`` diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h --- a/llvm/include/llvm/IR/ModuleSummaryIndex.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -591,9 +591,11 @@ this->NoUnwind &= RHS.NoUnwind; this->MayThrow &= RHS.MayThrow; this->HasUnknownCall &= RHS.HasUnknownCall; + // this->MustBeUnreachable &= RHS.MustBeUnreachable; return *this; } + // This is used to print human-readable information. bool anyFlagSet() { return this->ReadNone | this->ReadOnly | this->NoRecurse | this->ReturnDoesNotAlias | this->NoInline | this->AlwaysInline | @@ -669,7 +671,7 @@ std::vector(), std::vector(), std::vector(), - std::vector()); + std::vector(), false); } /// A dummy node to reference external functions that aren't in the index @@ -697,6 +699,13 @@ using ParamAccessesTy = std::vector; std::unique_ptr ParamAccesses; + // If true, the function must be unreachable. + // Note, a function might still be unreachable (not detected) + // even if this bit is off. + // + // For example, the destructor of a abstract class is unreachable. + bool mustBeUnreachable = false; + public: FunctionSummary(GVFlags Flags, unsigned NumInsts, FFlags FunFlags, uint64_t EntryCount, std::vector Refs, @@ -706,10 +715,11 @@ std::vector TypeCheckedLoadVCalls, std::vector TypeTestAssumeConstVCalls, std::vector TypeCheckedLoadConstVCalls, - std::vector Params) + std::vector Params, bool mustBeUnreachable) : GlobalValueSummary(FunctionKind, Flags, std::move(Refs)), InstCount(NumInsts), FunFlags(FunFlags), EntryCount(EntryCount), - CallGraphEdgeList(std::move(CGEdges)) { + CallGraphEdgeList(std::move(CGEdges)), + mustBeUnreachable(mustBeUnreachable) { if (!TypeTests.empty() || !TypeTestAssumeVCalls.empty() || !TypeCheckedLoadVCalls.empty() || !TypeTestAssumeConstVCalls.empty() || !TypeCheckedLoadConstVCalls.empty()) @@ -732,6 +742,8 @@ /// Get function summary flags. FFlags fflags() const { return FunFlags; } + bool isUnreachableFunction() const { return mustBeUnreachable; } + void setNoRecurse() { FunFlags.NoRecurse = true; } void setNoUnwind() { FunFlags.NoUnwind = true; } @@ -1334,6 +1346,7 @@ /// Return a ValueInfo for \p GV and mark it as belonging to GV. ValueInfo getOrInsertValueInfo(const GlobalValue *GV) { assert(HaveGVs); + errs() << "\tGV->GUID is " << GV->getGUID() << "\n"; auto VP = getOrInsertValuePtr(GV->getGUID()); VP->second.U.GV = GV; return ValueInfo(HaveGVs, VP); @@ -1369,6 +1382,8 @@ std::unique_ptr Summary) { if (const FunctionSummary *FS = dyn_cast(Summary.get())) HasParamAccess |= !FS->paramAccesses().empty(); + errs() << "\tGoing to add original name " << Summary->getOriginalName() + << " to GUID " << VI.getGUID() << "\n"; addOriginalName(VI.getGUID(), Summary->getOriginalName()); // Here we have a notionally const VI, but the value it points to is owned // by the non-const *this. @@ -1379,12 +1394,17 @@ /// Add an original name for the value of the given GUID. void addOriginalName(GlobalValue::GUID ValueGUID, GlobalValue::GUID OrigGUID) { - if (OrigGUID == 0 || ValueGUID == OrigGUID) + if (OrigGUID == 0 || ValueGUID == OrigGUID) { + errs() << "\t\tOrigGUID is zero or the same as ValueGUID [ " << OrigGUID + << ", " << ValueGUID << "\n"; return; + } if (OidGuidMap.count(OrigGUID) && OidGuidMap[OrigGUID] != ValueGUID) OidGuidMap[OrigGUID] = 0; else OidGuidMap[OrigGUID] = ValueGUID; + errs() << "\t\tMap origGUID to new guid " << OrigGUID << ", " << ValueGUID + << "\n"; } /// Find the summary for ValueInfo \p VI in module \p ModuleId, or nullptr if diff --git a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h --- a/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndexYAML.h @@ -234,7 +234,7 @@ std::move(FSum.TypeCheckedLoadVCalls), std::move(FSum.TypeTestAssumeConstVCalls), std::move(FSum.TypeCheckedLoadConstVCalls), - ArrayRef{})); + ArrayRef{}, false)); } } static void output(IO &io, GlobalValueSummaryMapTy &V) { diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -234,6 +234,24 @@ return false; } +// Returns true if `F` must be an unreachable function. +static bool mustBeUnreachableFunction(const Function &F) { + if (!F.empty()) { + const BasicBlock &entryBlock = F.getEntryBlock(); + if (!entryBlock.empty()) { + const Instruction *inst = &(*entryBlock.rbegin()); + if (inst->getOpcode() == Instruction::Unreachable) { + // TODO(mingmingl): delete + errs() << "unreachable function: "; + F.dump(); + errs() << "\n"; + return true; + } + } + } + return false; +} + static void computeFunctionSummary( ModuleSummaryIndex &Index, const Module &M, const Function &F, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, DominatorTree &DT, @@ -497,9 +515,13 @@ CallGraphEdges.takeVector(), TypeTests.takeVector(), TypeTestAssumeVCalls.takeVector(), TypeCheckedLoadVCalls.takeVector(), TypeTestAssumeConstVCalls.takeVector(), - TypeCheckedLoadConstVCalls.takeVector(), std::move(ParamAccesses)); + TypeCheckedLoadConstVCalls.takeVector(), std::move(ParamAccesses), + mustBeUnreachableFunction(F)); if (NonRenamableLocal) CantBePromoted.insert(F.getGUID()); + errs() << "Going to add F " << F.getName() + << " to global value summary and its unreachable mark is " + << FuncSummary->isUnreachableFunction() << "\n"; Index.addGlobalValueSummary(F, std::move(FuncSummary)); } @@ -745,7 +767,7 @@ ArrayRef{}, ArrayRef{}, ArrayRef{}, - ArrayRef{}); + ArrayRef{}, false); Index.addGlobalValueSummary(*GV, std::move(Summary)); } else { std::unique_ptr Summary = diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -8396,7 +8396,7 @@ std::move(TypeIdInfo.TypeCheckedLoadVCalls), std::move(TypeIdInfo.TypeTestAssumeConstVCalls), std::move(TypeIdInfo.TypeCheckedLoadConstVCalls), - std::move(ParamAccesses)); + std::move(ParamAccesses), false); FS->setModulePath(ModulePath); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -6199,14 +6199,20 @@ // numrefs x valueid, // n x (valueid, relblockfreq)] case bitc::FS_PERMODULE: + dbgs() << "Mingming FS_PERMODULE\n"; + [[clang::fallthrough]]; case bitc::FS_PERMODULE_RELBF: + dbgs() << "Mingming FS_PERMODULE_RELBF\n"; + [[clang::fallthrough]]; case bitc::FS_PERMODULE_PROFILE: { + dbgs() << "Mingming FS_PERMODULE_PROFILE\n"; unsigned ValueID = Record[0]; uint64_t RawFlags = Record[1]; unsigned InstCount = Record[2]; uint64_t RawFunFlags = 0; unsigned NumRefs = Record[3]; unsigned NumRORefs = 0, NumWORefs = 0; + bool isUnreachableFunc = false; int RefListStartIndex = 4; if (Version >= 4) { RawFunFlags = Record[3]; @@ -6217,7 +6223,8 @@ RefListStartIndex = 6; if (Version >= 7) { NumWORefs = Record[6]; - RefListStartIndex = 7; + isUnreachableFunc = (Record[7] == 1) ? true : false; + RefListStartIndex = 8; } } } @@ -6246,7 +6253,7 @@ std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), std::move(PendingTypeCheckedLoadConstVCalls), - std::move(PendingParamAccesses)); + std::move(PendingParamAccesses), isUnreachableFunc); auto VIAndOriginalGUID = getValueInfoFromValueId(ValueID); FS->setModulePath(getThisModule()->first()); FS->setOriginalName(VIAndOriginalGUID.second); @@ -6308,6 +6315,8 @@ // numrefs, numrefs x valueid, // n x (valueid, offset)] case bitc::FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS: { + dbgs() << "Mingming FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS" + << "\n"; unsigned ValueID = Record[0]; uint64_t RawFlags = Record[1]; GlobalVarSummary::GVarFlags GVF = getDecodedGVarFlags(Record[2]); @@ -6347,7 +6356,9 @@ unsigned NumRefs = Record[4]; unsigned NumRORefs = 0, NumWORefs = 0; int RefListStartIndex = 5; + bool isUnreachableFunction = false; + dbgs() << "Mingming Version is " << Version << "\n"; if (Version >= 4) { RawFunFlags = Record[4]; RefListStartIndex = 6; @@ -6360,9 +6371,10 @@ EntryCount = Record[5]; RefListStartIndex = 8; if (Version >= 7) { - RefListStartIndex = 9; + RefListStartIndex = 10; NumWORefs = Record[8]; - NumRORefsOffset = 2; + isUnreachableFunction = (Record[9] == 1) ? true : false; + NumRORefsOffset = 3; } } NumRORefs = Record[RefListStartIndex - NumRORefsOffset]; @@ -6370,6 +6382,8 @@ NumRefs = Record[NumRefsIndex]; } + dbgs() << "Mingming func is unreachable function " + << isUnreachableFunction << "\n"; auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); int CallGraphEdgeStartIndex = RefListStartIndex + NumRefs; assert(Record.size() >= RefListStartIndex + NumRefs && @@ -6389,7 +6403,7 @@ std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), std::move(PendingTypeCheckedLoadConstVCalls), - std::move(PendingParamAccesses)); + std::move(PendingParamAccesses), isUnreachableFunction); LastSeenSummary = FS.get(); LastSeenGUID = VI.getGUID(); FS->setModulePath(ModuleIdMap[ModuleId]); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -63,6 +63,7 @@ #include "llvm/Support/AtomicOrdering.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" @@ -3826,14 +3827,27 @@ return {VE.getValueID(VI.getValue())}; }); + dbgs() << "Mingming function name is "; + F.dump(); + dbgs() << "\n"; auto SpecialRefCnts = FS->specialRefCounts(); + dbgs() << "Mingming EncodedGVSummary index is " << NameVals.size() << "\n"; NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); + dbgs() << "Mingming instCount index is " << NameVals.size() << "\n"; NameVals.push_back(FS->instCount()); + dbgs() << "Mingming fflags index is " << NameVals.size() << "\n"; NameVals.push_back(getEncodedFFlags(FS->fflags())); + dbgs() << "Mingming refCount index is " << NameVals.size() << "\n"; NameVals.push_back(FS->refs().size()); + dbgs() << "Mingming rorefcnt index is " << NameVals.size() << "\n"; NameVals.push_back(SpecialRefCnts.first); // rorefcnt + dbgs() << "Mingming worefcnt index is " << NameVals.size() << "\n"; NameVals.push_back(SpecialRefCnts.second); // worefcnt + dbgs() << "Mingming isUnreachable function index is " << NameVals.size() + << " and value is " << FS->isUnreachableFunction() << "\n"; + NameVals.push_back(FS->isUnreachableFunction() ? 1 : 0); + for (auto &RI : FS->refs()) NameVals.push_back(VE.getValueID(RI.getValue())); diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -3154,6 +3154,7 @@ void AssemblyWriter::printFunctionSummary(const FunctionSummary *FS) { Out << ", insts: " << FS->instCount(); + Out << ", unreachable: " << FS->isUnreachableFunction(); if (FS->fflags().anyFlagSet()) Out << ", " << FS->fflags(); diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -565,7 +565,8 @@ bool tryFindVirtualCallTargets(std::vector &TargetsForSlot, const std::set &TypeMemberInfos, - uint64_t ByteOffset); + uint64_t ByteOffset, + ModuleSummaryIndex *ExportSummary); void applySingleImplDevirt(VTableSlotInfo &SlotInfo, Constant *TheFn, bool &IsExported); @@ -646,6 +647,10 @@ runForTesting(Module &M, function_ref AARGetter, function_ref OREGetter, function_ref LookupDomTree); + + // Returns true if the function is unreachable from all summaries. + static bool mustBeUnreachableFunction(Function *TheFn, + ModuleSummaryIndex *exportSummary); }; struct DevirtIndex { @@ -933,6 +938,50 @@ return Changed; } +bool DevirtModule::mustBeUnreachableFunction( + Function *TheFn, ModuleSummaryIndex *ExportSummary) { + assert(((TheFn != nullptr) && (ExportSummary != nullptr)) && + "TheFn and ExportSummary must not be nullptr"); + errs() << "Func GUID is " << TheFn->getGUID() << "\n"; + + errs() << "Func current identifier is " << TheFn->getGlobalIdentifier() + << "\n"; + const std::string rewrittenFuncGlobalIdentifier = + GlobalValue::getGlobalIdentifier(TheFn->getName(), + GlobalValue::ExternalLinkage, + TheFn->getParent()->getSourceFileName()); + errs() << "rewritten identifier is " << rewrittenFuncGlobalIdentifier << "\n"; + errs() << "GUID before is " + << GlobalValue::getGUID(TheFn->getGlobalIdentifier()) + << " and after is " + << GlobalValue::getGUID(rewrittenFuncGlobalIdentifier) << "\n"; + + if (ValueInfo TheFnVI = ExportSummary->getValueInfo( + GlobalValue::getGUID(rewrittenFuncGlobalIdentifier))) { + bool AllSummariesAreFunctionSummary = true; + bool AllFunctionSummariesIndicateUnreachable = true; + for (auto &Summary : TheFnVI.getSummaryList()) { + if (auto *FS = dyn_cast(Summary.get())) { + if (!FS->isUnreachableFunction()) { + AllFunctionSummariesIndicateUnreachable = false; + break; + } + } else { + AllSummariesAreFunctionSummary = false; + break; + } + } + if (AllSummariesAreFunctionSummary && + AllFunctionSummariesIndicateUnreachable) { + return true; + } + } else { + errs() << "No value info for func: "; + TheFn->dump(); + } + return false; +} + void DevirtModule::buildTypeIdentifierMap( std::vector &Bits, DenseMap> &TypeIdMap) { @@ -969,7 +1018,10 @@ bool DevirtModule::tryFindVirtualCallTargets( std::vector &TargetsForSlot, - const std::set &TypeMemberInfos, uint64_t ByteOffset) { + const std::set &TypeMemberInfos, uint64_t ByteOffset, + ModuleSummaryIndex *ExportSummary) { + errs() << "DevirtModule::tryFindVirtualCallTargets\n"; + ExportSummary->dump(); for (const TypeMemberInfo &TM : TypeMemberInfos) { if (!TM.Bits->GV->isConstant()) return false; @@ -997,6 +1049,15 @@ if (Fn->getName() == "__cxa_pure_virtual") continue; + if (mustBeUnreachableFunction(Fn, ExportSummary)) { + errs() << "Function " << Fn->getName() << " is unreachable\n"; + continue; + } + if (Fn->getName() == "_ZN4BaseD0Ev") { + errs() << "not skipping skip _ZN4BaseD0Ev\n"; + // continue; + } + TargetsForSlot.push_back({Fn, &TM}); } @@ -1142,12 +1203,26 @@ ModuleSummaryIndex *ExportSummary, MutableArrayRef TargetsForSlot, VTableSlotInfo &SlotInfo, WholeProgramDevirtResolution *Res) { + ExportSummary->dump(); // See if the program contains a single implementation of this virtual // function. Function *TheFn = TargetsForSlot[0].Fn; - for (auto &&Target : TargetsForSlot) - if (TheFn != Target.Fn) + errs() << "First func: "; + TheFn->dump(); + errs() << "\n"; + for (auto &&Target : TargetsForSlot) { + errs() << "Target func: "; + Target.Fn->dump(); + errs() << "\n"; + if (mustBeUnreachableFunction(Target.Fn, ExportSummary)) { + errs() << "\t\t\t???Found an unreachable function " + << Target.Fn->getName() << "\n"; + continue; + } + if (TheFn != Target.Fn) { return false; + } + } // If so, update each call site to call that implementation directly. if (RemarksEnabled) @@ -1162,6 +1237,7 @@ // to make it visible to thin LTO objects. We can only get here during the // ThinLTO export phase. if (TheFn->hasLocalLinkage()) { + errs() << TheFn->getName() << " has local linkage\n"; std::string NewName = (TheFn->getName() + ".llvm.merged").str(); // Since we are renaming the function, any comdats with the same name must @@ -2015,6 +2091,7 @@ } bool DevirtModule::run() { + errs() << "devirt module::run\n"; // If only some of the modules were split, we cannot correctly perform // this transformation. We already checked for the presense of type tests // with partially split modules during the thin link, and would have emitted @@ -2137,7 +2214,7 @@ cast(S.first.TypeID)->getString()) .WPDRes[S.first.ByteOffset]; if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos, - S.first.ByteOffset)) { + S.first.ByteOffset, ExportSummary)) { if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) { DidVirtualConstProp |=