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 @@ -6246,7 +6246,7 @@ std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), std::move(PendingTypeCheckedLoadConstVCalls), - std::move(PendingParamAccesses)); + std::move(PendingParamAccesses), false); auto VIAndOriginalGUID = getValueInfoFromValueId(ValueID); FS->setModulePath(getThisModule()->first()); FS->setOriginalName(VIAndOriginalGUID.second); @@ -6389,7 +6389,7 @@ std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), std::move(PendingTypeCheckedLoadConstVCalls), - std::move(PendingParamAccesses)); + std::move(PendingParamAccesses), false); LastSeenSummary = FS.get(); LastSeenGUID = VI.getGUID(); FS->setModulePath(ModuleIdMap[ModuleId]); 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 |=