Changeset View
Changeset View
Standalone View
Standalone View
llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
Show First 20 Lines • Show All 559 Lines • ▼ Show 20 Lines | struct DevirtModule { | ||||
void scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc); | void scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc); | ||||
void buildTypeIdentifierMap( | void buildTypeIdentifierMap( | ||||
std::vector<VTableBits> &Bits, | std::vector<VTableBits> &Bits, | ||||
DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap); | DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap); | ||||
bool | bool | ||||
tryFindVirtualCallTargets(std::vector<VirtualCallTarget> &TargetsForSlot, | tryFindVirtualCallTargets(std::vector<VirtualCallTarget> &TargetsForSlot, | ||||
const std::set<TypeMemberInfo> &TypeMemberInfos, | const std::set<TypeMemberInfo> &TypeMemberInfos, | ||||
uint64_t ByteOffset); | uint64_t ByteOffset, | ||||
ModuleSummaryIndex *ExportSummary); | |||||
void applySingleImplDevirt(VTableSlotInfo &SlotInfo, Constant *TheFn, | void applySingleImplDevirt(VTableSlotInfo &SlotInfo, Constant *TheFn, | ||||
bool &IsExported); | bool &IsExported); | ||||
bool trySingleImplDevirt(ModuleSummaryIndex *ExportSummary, | bool trySingleImplDevirt(ModuleSummaryIndex *ExportSummary, | ||||
MutableArrayRef<VirtualCallTarget> TargetsForSlot, | MutableArrayRef<VirtualCallTarget> TargetsForSlot, | ||||
VTableSlotInfo &SlotInfo, | VTableSlotInfo &SlotInfo, | ||||
WholeProgramDevirtResolution *Res); | WholeProgramDevirtResolution *Res); | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | struct DevirtModule { | ||||
bool run(); | bool run(); | ||||
// Lower the module using the action and summary passed as command line | // Lower the module using the action and summary passed as command line | ||||
// arguments. For testing purposes only. | // arguments. For testing purposes only. | ||||
static bool | static bool | ||||
runForTesting(Module &M, function_ref<AAResults &(Function &)> AARGetter, | runForTesting(Module &M, function_ref<AAResults &(Function &)> AARGetter, | ||||
function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter, | function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter, | ||||
function_ref<DominatorTree &(Function &)> LookupDomTree); | function_ref<DominatorTree &(Function &)> LookupDomTree); | ||||
// Returns true if the function is unreachable from all summaries. | |||||
static bool mustBeUnreachableFunction(Function *TheFn, | |||||
ModuleSummaryIndex *exportSummary); | |||||
}; | }; | ||||
struct DevirtIndex { | struct DevirtIndex { | ||||
ModuleSummaryIndex &ExportSummary; | ModuleSummaryIndex &ExportSummary; | ||||
// The set in which to record GUIDs exported from their module by | // The set in which to record GUIDs exported from their module by | ||||
// devirtualization, used by client to ensure they are not internalized. | // devirtualization, used by client to ensure they are not internalized. | ||||
std::set<GlobalValue::GUID> &ExportedGUIDs; | std::set<GlobalValue::GUID> &ExportedGUIDs; | ||||
// A map in which to record the information necessary to locate the WPD | // A map in which to record the information necessary to locate the WPD | ||||
▲ Show 20 Lines • Show All 271 Lines • ▼ Show 20 Lines | if (StringRef(ClWriteSummary).endswith(".bc")) { | ||||
yaml::Output Out(OS); | yaml::Output Out(OS); | ||||
Out << *Summary; | Out << *Summary; | ||||
} | } | ||||
} | } | ||||
return Changed; | 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<FunctionSummary>(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( | void DevirtModule::buildTypeIdentifierMap( | ||||
std::vector<VTableBits> &Bits, | std::vector<VTableBits> &Bits, | ||||
DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap) { | DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap) { | ||||
DenseMap<GlobalVariable *, VTableBits *> GVToBits; | DenseMap<GlobalVariable *, VTableBits *> GVToBits; | ||||
Bits.reserve(M.getGlobalList().size()); | Bits.reserve(M.getGlobalList().size()); | ||||
SmallVector<MDNode *, 2> Types; | SmallVector<MDNode *, 2> Types; | ||||
for (GlobalVariable &GV : M.globals()) { | for (GlobalVariable &GV : M.globals()) { | ||||
Types.clear(); | Types.clear(); | ||||
Show All 20 Lines | for (MDNode *Type : Types) { | ||||
TypeIdMap[TypeID].insert({BitsPtr, Offset}); | TypeIdMap[TypeID].insert({BitsPtr, Offset}); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool DevirtModule::tryFindVirtualCallTargets( | bool DevirtModule::tryFindVirtualCallTargets( | ||||
std::vector<VirtualCallTarget> &TargetsForSlot, | std::vector<VirtualCallTarget> &TargetsForSlot, | ||||
const std::set<TypeMemberInfo> &TypeMemberInfos, uint64_t ByteOffset) { | const std::set<TypeMemberInfo> &TypeMemberInfos, uint64_t ByteOffset, | ||||
ModuleSummaryIndex *ExportSummary) { | |||||
errs() << "DevirtModule::tryFindVirtualCallTargets\n"; | |||||
ExportSummary->dump(); | |||||
for (const TypeMemberInfo &TM : TypeMemberInfos) { | for (const TypeMemberInfo &TM : TypeMemberInfos) { | ||||
if (!TM.Bits->GV->isConstant()) | if (!TM.Bits->GV->isConstant()) | ||||
return false; | return false; | ||||
// We cannot perform whole program devirtualization analysis on a vtable | // We cannot perform whole program devirtualization analysis on a vtable | ||||
// with public LTO visibility. | // with public LTO visibility. | ||||
if (TM.Bits->GV->getVCallVisibility() == | if (TM.Bits->GV->getVCallVisibility() == | ||||
GlobalObject::VCallVisibilityPublic) | GlobalObject::VCallVisibilityPublic) | ||||
Show All 11 Lines | for (const TypeMemberInfo &TM : TypeMemberInfos) { | ||||
if (FunctionsToSkip.match(Fn->getName())) | if (FunctionsToSkip.match(Fn->getName())) | ||||
return false; | return false; | ||||
// We can disregard __cxa_pure_virtual as a possible call target, as | // We can disregard __cxa_pure_virtual as a possible call target, as | ||||
// calls to pure virtuals are UB. | // calls to pure virtuals are UB. | ||||
if (Fn->getName() == "__cxa_pure_virtual") | if (Fn->getName() == "__cxa_pure_virtual") | ||||
continue; | 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}); | TargetsForSlot.push_back({Fn, &TM}); | ||||
} | } | ||||
// Give up if we couldn't find any targets. | // Give up if we couldn't find any targets. | ||||
return !TargetsForSlot.empty(); | return !TargetsForSlot.empty(); | ||||
} | } | ||||
bool DevirtIndex::tryFindVirtualCallTargets( | bool DevirtIndex::tryFindVirtualCallTargets( | ||||
▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | for (auto &P : SlotInfo.ConstCSInfo) | ||||
AddCalls(P.second); | AddCalls(P.second); | ||||
return IsExported; | return IsExported; | ||||
} | } | ||||
bool DevirtModule::trySingleImplDevirt( | bool DevirtModule::trySingleImplDevirt( | ||||
ModuleSummaryIndex *ExportSummary, | ModuleSummaryIndex *ExportSummary, | ||||
MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo, | MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo, | ||||
WholeProgramDevirtResolution *Res) { | WholeProgramDevirtResolution *Res) { | ||||
ExportSummary->dump(); | |||||
// See if the program contains a single implementation of this virtual | // See if the program contains a single implementation of this virtual | ||||
// function. | // function. | ||||
Function *TheFn = TargetsForSlot[0].Fn; | Function *TheFn = TargetsForSlot[0].Fn; | ||||
for (auto &&Target : TargetsForSlot) | errs() << "First func: "; | ||||
if (TheFn != Target.Fn) | 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; | return false; | ||||
} | |||||
} | |||||
// If so, update each call site to call that implementation directly. | // If so, update each call site to call that implementation directly. | ||||
if (RemarksEnabled) | if (RemarksEnabled) | ||||
TargetsForSlot[0].WasDevirt = true; | TargetsForSlot[0].WasDevirt = true; | ||||
bool IsExported = false; | bool IsExported = false; | ||||
applySingleImplDevirt(SlotInfo, TheFn, IsExported); | applySingleImplDevirt(SlotInfo, TheFn, IsExported); | ||||
if (!IsExported) | if (!IsExported) | ||||
return false; | return false; | ||||
// If the only implementation has local linkage, we must promote to external | // If the only implementation has local linkage, we must promote to external | ||||
// to make it visible to thin LTO objects. We can only get here during the | // to make it visible to thin LTO objects. We can only get here during the | ||||
// ThinLTO export phase. | // ThinLTO export phase. | ||||
if (TheFn->hasLocalLinkage()) { | if (TheFn->hasLocalLinkage()) { | ||||
errs() << TheFn->getName() << " has local linkage\n"; | |||||
std::string NewName = (TheFn->getName() + ".llvm.merged").str(); | std::string NewName = (TheFn->getName() + ".llvm.merged").str(); | ||||
// Since we are renaming the function, any comdats with the same name must | // Since we are renaming the function, any comdats with the same name must | ||||
// also be renamed. This is required when targeting COFF, as the comdat name | // also be renamed. This is required when targeting COFF, as the comdat name | ||||
// must match one of the names of the symbols in the comdat. | // must match one of the names of the symbols in the comdat. | ||||
if (Comdat *C = TheFn->getComdat()) { | if (Comdat *C = TheFn->getComdat()) { | ||||
if (C->getName() == TheFn->getName()) { | if (C->getName() == TheFn->getName()) { | ||||
Comdat *NewC = M.getOrInsertComdat(NewName); | Comdat *NewC = M.getOrInsertComdat(NewName); | ||||
▲ Show 20 Lines • Show All 837 Lines • ▼ Show 20 Lines | for (auto &&U : NumUnsafeUsesForTypeTest) { | ||||
if (U.second == 0) { | if (U.second == 0) { | ||||
U.first->replaceAllUsesWith(True); | U.first->replaceAllUsesWith(True); | ||||
U.first->eraseFromParent(); | U.first->eraseFromParent(); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool DevirtModule::run() { | bool DevirtModule::run() { | ||||
errs() << "devirt module::run\n"; | |||||
// If only some of the modules were split, we cannot correctly perform | // If only some of the modules were split, we cannot correctly perform | ||||
// this transformation. We already checked for the presense of type tests | // this transformation. We already checked for the presense of type tests | ||||
// with partially split modules during the thin link, and would have emitted | // with partially split modules during the thin link, and would have emitted | ||||
// an error if any were found, so here we can simply return. | // an error if any were found, so here we can simply return. | ||||
if ((ExportSummary && ExportSummary->partiallySplitLTOUnits()) || | if ((ExportSummary && ExportSummary->partiallySplitLTOUnits()) || | ||||
(ImportSummary && ImportSummary->partiallySplitLTOUnits())) | (ImportSummary && ImportSummary->partiallySplitLTOUnits())) | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 106 Lines • ▼ Show 20 Lines | if (ExportSummary && isa<MDString>(S.first.TypeID) && | ||||
// a global's type metadata, the TypeIdMap entry set will be empty, and | // a global's type metadata, the TypeIdMap entry set will be empty, and | ||||
// we don't want to create an entry (with the default Unknown type | // we don't want to create an entry (with the default Unknown type | ||||
// resolution), which can prevent detection of the Unsat. | // resolution), which can prevent detection of the Unsat. | ||||
Res = &ExportSummary | Res = &ExportSummary | ||||
->getOrInsertTypeIdSummary( | ->getOrInsertTypeIdSummary( | ||||
cast<MDString>(S.first.TypeID)->getString()) | cast<MDString>(S.first.TypeID)->getString()) | ||||
.WPDRes[S.first.ByteOffset]; | .WPDRes[S.first.ByteOffset]; | ||||
if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos, | if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos, | ||||
S.first.ByteOffset)) { | S.first.ByteOffset, ExportSummary)) { | ||||
if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) { | if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) { | ||||
DidVirtualConstProp |= | DidVirtualConstProp |= | ||||
tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first); | tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first); | ||||
tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first); | tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 126 Lines • Show Last 20 Lines |