Changeset View
Changeset View
Standalone View
Standalone View
llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
Show First 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | ClThreshold("wholeprogramdevirt-branch-funnel-threshold", cl::Hidden, | ||||
cl::desc("Maximum number of call targets per " | cl::desc("Maximum number of call targets per " | ||||
"call site to enable branch funnels")); | "call site to enable branch funnels")); | ||||
static cl::opt<bool> | static cl::opt<bool> | ||||
PrintSummaryDevirt("wholeprogramdevirt-print-index-based", cl::Hidden, | PrintSummaryDevirt("wholeprogramdevirt-print-index-based", cl::Hidden, | ||||
cl::init(false), cl::ZeroOrMore, | cl::init(false), cl::ZeroOrMore, | ||||
cl::desc("Print index-based devirtualization messages")); | cl::desc("Print index-based devirtualization messages")); | ||||
/// Provide a way to force enable whole program visibility in tests. | |||||
/// This is needed to support legacy tests that don't contain | |||||
/// !vcall_visibility metadata (the mere presense of type tests | |||||
/// previously implied hidden visibility). | |||||
cl::opt<bool> | |||||
WholeProgramVisibility("whole-program-visibility", cl::init(false), | |||||
cl::Hidden, cl::ZeroOrMore, | |||||
cl::desc("Enable whole program visibility")); | |||||
evgeny777: Is this tested? | |||||
Added a test of it to llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll. tejohnson: Added a test of it to llvm/test/ThinLTO/X86/devirt_vcall_vis_public.ll. | |||||
/// Provide a way to force disable whole program for debugging or workarounds, | |||||
/// when enabled via the linker. | |||||
cl::opt<bool> DisableWholeProgramVisibility( | |||||
"disable-whole-program-visibility", cl::init(false), cl::Hidden, | |||||
cl::ZeroOrMore, | |||||
cl::desc("Disable whole program visibility (overrides enabling options)")); | |||||
// Find the minimum offset that we may store a value of size Size bits at. If | // Find the minimum offset that we may store a value of size Size bits at. If | ||||
// IsAfter is set, look for an offset before the object, otherwise look for an | // IsAfter is set, look for an offset before the object, otherwise look for an | ||||
// offset after the object. | // offset after the object. | ||||
uint64_t | uint64_t | ||||
wholeprogramdevirt::findLowestOffset(ArrayRef<VirtualCallTarget> Targets, | wholeprogramdevirt::findLowestOffset(ArrayRef<VirtualCallTarget> Targets, | ||||
bool IsAfter, uint64_t Size) { | bool IsAfter, uint64_t Size) { | ||||
// Find a minimum offset taking into account only vtable sizes. | // Find a minimum offset taking into account only vtable sizes. | ||||
uint64_t MinByte = 0; | uint64_t MinByte = 0; | ||||
▲ Show 20 Lines • Show All 552 Lines • ▼ Show 20 Lines | PreservedAnalyses WholeProgramDevirtPass::run(Module &M, | ||||
}; | }; | ||||
if (!DevirtModule(M, AARGetter, OREGetter, LookupDomTree, ExportSummary, | if (!DevirtModule(M, AARGetter, OREGetter, LookupDomTree, ExportSummary, | ||||
ImportSummary) | ImportSummary) | ||||
.run()) | .run()) | ||||
return PreservedAnalyses::all(); | return PreservedAnalyses::all(); | ||||
return PreservedAnalyses::none(); | return PreservedAnalyses::none(); | ||||
} | } | ||||
// Enable whole program visibility if enabled by client (e.g. linker) or | |||||
// internal option, and not force disabled. | |||||
static bool hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO) { | |||||
return (WholeProgramVisibilityEnabledInLTO || WholeProgramVisibility) && | |||||
!DisableWholeProgramVisibility; | |||||
} | |||||
namespace llvm { | namespace llvm { | ||||
/// If whole program visibility asserted, then upgrade all public vcall | |||||
/// visibility metadata on vtable definitions to linkage unit visibility in | |||||
/// Module IR (for regular or hybrid LTO). | |||||
void updateVCallVisibilityInModule(Module &M, | |||||
bool WholeProgramVisibilityEnabledInLTO) { | |||||
if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) | |||||
return; | |||||
for (GlobalVariable &GV : M.globals()) | |||||
// Add linkage unit visibility to any variable with type metadata, which are | |||||
// the vtable definitions. We won't have an existing vcall_visibility | |||||
// metadata on vtable definitions with public visibility. | |||||
if (GV.hasMetadata(LLVMContext::MD_type) && | |||||
GV.getVCallVisibility() == GlobalObject::VCallVisibilityPublic) | |||||
GV.setVCallVisibilityMetadata(GlobalObject::VCallVisibilityLinkageUnit); | |||||
} | |||||
/// If whole program visibility asserted, then upgrade all public vcall | |||||
/// visibility metadata on vtable definition summaries to linkage unit | |||||
/// visibility in Module summary index (for ThinLTO). | |||||
void updateVCallVisibilityInIndex(ModuleSummaryIndex &Index, | |||||
bool WholeProgramVisibilityEnabledInLTO) { | |||||
if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) | |||||
return; | |||||
for (auto &P : Index) { | |||||
for (auto &S : P.second.SummaryList) { | |||||
auto *GVar = dyn_cast<GlobalVarSummary>(S.get()); | |||||
if (!GVar || GVar->vTableFuncs().empty() || | |||||
GVar->getVCallVisibility() != GlobalObject::VCallVisibilityPublic) | |||||
continue; | |||||
GVar->setVCallVisibility(GlobalObject::VCallVisibilityLinkageUnit); | |||||
} | |||||
} | |||||
} | |||||
void runWholeProgramDevirtOnIndex( | void runWholeProgramDevirtOnIndex( | ||||
ModuleSummaryIndex &Summary, std::set<GlobalValue::GUID> &ExportedGUIDs, | ModuleSummaryIndex &Summary, std::set<GlobalValue::GUID> &ExportedGUIDs, | ||||
std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) { | std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) { | ||||
DevirtIndex(Summary, ExportedGUIDs, LocalWPDTargetsMap).run(); | DevirtIndex(Summary, ExportedGUIDs, LocalWPDTargetsMap).run(); | ||||
} | } | ||||
void updateIndexWPDForExports( | void updateIndexWPDForExports( | ||||
ModuleSummaryIndex &Summary, | ModuleSummaryIndex &Summary, | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | |||||
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) { | ||||
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 | |||||
// with public LTO visibility. | |||||
if (TM.Bits->GV->getVCallVisibility() == | |||||
GlobalObject::VCallVisibilityPublic) | |||||
return false; | |||||
Constant *Ptr = getPointerAtOffset(TM.Bits->GV->getInitializer(), | Constant *Ptr = getPointerAtOffset(TM.Bits->GV->getInitializer(), | ||||
TM.Offset + ByteOffset, M); | TM.Offset + ByteOffset, M); | ||||
if (!Ptr) | if (!Ptr) | ||||
return false; | return false; | ||||
auto Fn = dyn_cast<Function>(Ptr->stripPointerCasts()); | auto Fn = dyn_cast<Function>(Ptr->stripPointerCasts()); | ||||
if (!Fn) | if (!Fn) | ||||
return false; | return false; | ||||
Show All 29 Lines | for (const TypeIdOffsetVtableInfo &P : TIdInfo) { | ||||
const GlobalVarSummary *VS = nullptr; | const GlobalVarSummary *VS = nullptr; | ||||
bool LocalFound = false; | bool LocalFound = false; | ||||
for (auto &S : P.VTableVI.getSummaryList()) { | for (auto &S : P.VTableVI.getSummaryList()) { | ||||
if (GlobalValue::isLocalLinkage(S->linkage())) { | if (GlobalValue::isLocalLinkage(S->linkage())) { | ||||
if (LocalFound) | if (LocalFound) | ||||
return false; | return false; | ||||
LocalFound = true; | LocalFound = true; | ||||
} | } | ||||
if (!GlobalValue::isAvailableExternallyLinkage(S->linkage())) | if (!GlobalValue::isAvailableExternallyLinkage(S->linkage())) { | ||||
VS = cast<GlobalVarSummary>(S->getBaseObject()); | VS = cast<GlobalVarSummary>(S->getBaseObject()); | ||||
// We cannot perform whole program devirtualization analysis on a vtable | |||||
// with public LTO visibility. | |||||
if (VS->getVCallVisibility() == GlobalObject::VCallVisibilityPublic) | |||||
return false; | |||||
} | |||||
} | } | ||||
if (!VS->isLive()) | if (!VS->isLive()) | ||||
continue; | continue; | ||||
for (auto VTP : VS->vTableFuncs()) { | for (auto VTP : VS->vTableFuncs()) { | ||||
if (VTP.VTableOffset != P.AddressPointOffset + ByteOffset) | if (VTP.VTableOffset != P.AddressPointOffset + ByteOffset) | ||||
continue; | continue; | ||||
TargetsForSlot.push_back(VTP.FuncVI); | TargetsForSlot.push_back(VTP.FuncVI); | ||||
▲ Show 20 Lines • Show All 927 Lines • ▼ Show 20 Lines | if (TypeCheckedLoadFunc) | ||||
scanTypeCheckedLoadUsers(TypeCheckedLoadFunc); | scanTypeCheckedLoadUsers(TypeCheckedLoadFunc); | ||||
if (ImportSummary) { | if (ImportSummary) { | ||||
for (auto &S : CallSlots) | for (auto &S : CallSlots) | ||||
importResolution(S.first, S.second); | importResolution(S.first, S.second); | ||||
removeRedundantTypeTests(); | removeRedundantTypeTests(); | ||||
// We have lowered or deleted the type instrinsics, so we will no | |||||
// longer have enough information to reason about the liveness of virtual | |||||
// function pointers in GlobalDCE. | |||||
for (GlobalVariable &GV : M.globals()) | |||||
GV.eraseMetadata(LLVMContext::MD_vcall_visibility); | |||||
// The rest of the code is only necessary when exporting or during regular | // The rest of the code is only necessary when exporting or during regular | ||||
// LTO, so we are done. | // LTO, so we are done. | ||||
return true; | return true; | ||||
} | } | ||||
// Rebuild type metadata into a map for easy lookup. | // Rebuild type metadata into a map for easy lookup. | ||||
std::vector<VTableBits> Bits; | std::vector<VTableBits> Bits; | ||||
DenseMap<Metadata *, std::set<TypeMemberInfo>> TypeIdMap; | DenseMap<Metadata *, std::set<TypeMemberInfo>> TypeIdMap; | ||||
▲ Show 20 Lines • Show All 107 Lines • ▼ Show 20 Lines | bool DevirtModule::run() { | ||||
removeRedundantTypeTests(); | removeRedundantTypeTests(); | ||||
// Rebuild each global we touched as part of virtual constant propagation to | // Rebuild each global we touched as part of virtual constant propagation to | ||||
// include the before and after bytes. | // include the before and after bytes. | ||||
if (DidVirtualConstProp) | if (DidVirtualConstProp) | ||||
for (VTableBits &B : Bits) | for (VTableBits &B : Bits) | ||||
rebuildGlobal(B); | rebuildGlobal(B); | ||||
// We have lowered or deleted the type checked load intrinsics, so we no | // We have lowered or deleted the type instrinsics, so we will no | ||||
// longer have enough information to reason about the liveness of virtual | // longer have enough information to reason about the liveness of virtual | ||||
// function pointers in GlobalDCE. | // function pointers in GlobalDCE. | ||||
for (GlobalVariable &GV : M.globals()) | for (GlobalVariable &GV : M.globals()) | ||||
GV.eraseMetadata(LLVMContext::MD_vcall_visibility); | GV.eraseMetadata(LLVMContext::MD_vcall_visibility); | ||||
return true; | return true; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 74 Lines • Show Last 20 Lines |
Is this tested?