summaryrefslogtreecommitdiff
path: root/lib/Transforms/IPO/WholeProgramDevirt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Transforms/IPO/WholeProgramDevirt.cpp')
-rw-r--r--lib/Transforms/IPO/WholeProgramDevirt.cpp202
1 files changed, 154 insertions, 48 deletions
diff --git a/lib/Transforms/IPO/WholeProgramDevirt.cpp b/lib/Transforms/IPO/WholeProgramDevirt.cpp
index 00769cd63229..ec56f0cde25d 100644
--- a/lib/Transforms/IPO/WholeProgramDevirt.cpp
+++ b/lib/Transforms/IPO/WholeProgramDevirt.cpp
@@ -51,14 +51,13 @@
#include "llvm/ADT/iterator_range.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/BasicAliasAnalysis.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/TypeMetadataUtils.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
-#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/DerivedTypes.h"
-#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalVariable.h"
@@ -275,18 +274,39 @@ struct VirtualCallSite {
// of that field for details.
unsigned *NumUnsafeUses;
- void emitRemark(const Twine &OptName, const Twine &TargetName) {
+ void
+ emitRemark(const StringRef OptName, const StringRef TargetName,
+ function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter) {
Function *F = CS.getCaller();
- emitOptimizationRemark(
- F->getContext(), DEBUG_TYPE, *F,
- CS.getInstruction()->getDebugLoc(),
- OptName + ": devirtualized a call to " + TargetName);
+ DebugLoc DLoc = CS->getDebugLoc();
+ BasicBlock *Block = CS.getParent();
+
+ // In the new pass manager, we can request the optimization
+ // remark emitter pass on a per-function-basis, which the
+ // OREGetter will do for us.
+ // In the old pass manager, this is harder, so we just build
+ // a optimization remark emitter on the fly, when we need it.
+ std::unique_ptr<OptimizationRemarkEmitter> OwnedORE;
+ OptimizationRemarkEmitter *ORE;
+ if (OREGetter)
+ ORE = &OREGetter(F);
+ else {
+ OwnedORE = make_unique<OptimizationRemarkEmitter>(F);
+ ORE = OwnedORE.get();
+ }
+
+ using namespace ore;
+ ORE->emit(OptimizationRemark(DEBUG_TYPE, OptName, DLoc, Block)
+ << NV("Optimization", OptName) << ": devirtualized a call to "
+ << NV("FunctionName", TargetName));
}
- void replaceAndErase(const Twine &OptName, const Twine &TargetName,
- bool RemarksEnabled, Value *New) {
+ void replaceAndErase(
+ const StringRef OptName, const StringRef TargetName, bool RemarksEnabled,
+ function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
+ Value *New) {
if (RemarksEnabled)
- emitRemark(OptName, TargetName);
+ emitRemark(OptName, TargetName, OREGetter);
CS->replaceAllUsesWith(New);
if (auto II = dyn_cast<InvokeInst>(CS.getInstruction())) {
BranchInst::Create(II->getNormalDest(), CS.getInstruction());
@@ -383,6 +403,7 @@ struct DevirtModule {
IntegerType *IntPtrTy;
bool RemarksEnabled;
+ function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter;
MapVector<VTableSlot, VTableSlotInfo> CallSlots;
@@ -397,6 +418,7 @@ struct DevirtModule {
std::map<CallInst *, unsigned> NumUnsafeUsesForTypeTest;
DevirtModule(Module &M, function_ref<AAResults &(Function &)> AARGetter,
+ function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter,
ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary)
: M(M), AARGetter(AARGetter), ExportSummary(ExportSummary),
@@ -405,7 +427,7 @@ struct DevirtModule {
Int32Ty(Type::getInt32Ty(M.getContext())),
Int64Ty(Type::getInt64Ty(M.getContext())),
IntPtrTy(M.getDataLayout().getIntPtrType(M.getContext(), 0)),
- RemarksEnabled(areRemarksEnabled()) {
+ RemarksEnabled(areRemarksEnabled()), OREGetter(OREGetter) {
assert(!(ExportSummary && ImportSummary));
}
@@ -444,16 +466,23 @@ struct DevirtModule {
std::string getGlobalName(VTableSlot Slot, ArrayRef<uint64_t> Args,
StringRef Name);
+ bool shouldExportConstantsAsAbsoluteSymbols();
+
// This function is called during the export phase to create a symbol
// definition containing information about the given vtable slot and list of
// arguments.
void exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
Constant *C);
+ void exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name,
+ uint32_t Const, uint32_t &Storage);
// This function is called during the import phase to create a reference to
// the symbol definition created during the export phase.
Constant *importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
- StringRef Name, unsigned AbsWidth = 0);
+ StringRef Name);
+ Constant *importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
+ StringRef Name, IntegerType *IntTy,
+ uint32_t Storage);
void applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, bool IsOne,
Constant *UniqueMemberAddr);
@@ -482,8 +511,9 @@ struct DevirtModule {
// Lower the module using the action and summary passed as command line
// arguments. For testing purposes only.
- static bool runForTesting(Module &M,
- function_ref<AAResults &(Function &)> AARGetter);
+ static bool runForTesting(
+ Module &M, function_ref<AAResults &(Function &)> AARGetter,
+ function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter);
};
struct WholeProgramDevirt : public ModulePass {
@@ -508,9 +538,14 @@ struct WholeProgramDevirt : public ModulePass {
bool runOnModule(Module &M) override {
if (skipModule(M))
return false;
+
+ auto OREGetter = function_ref<OptimizationRemarkEmitter &(Function *)>();
+
if (UseCommandLine)
- return DevirtModule::runForTesting(M, LegacyAARGetter(*this));
- return DevirtModule(M, LegacyAARGetter(*this), ExportSummary, ImportSummary)
+ return DevirtModule::runForTesting(M, LegacyAARGetter(*this), OREGetter);
+
+ return DevirtModule(M, LegacyAARGetter(*this), OREGetter, ExportSummary,
+ ImportSummary)
.run();
}
@@ -542,13 +577,17 @@ PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
auto AARGetter = [&](Function &F) -> AAResults & {
return FAM.getResult<AAManager>(F);
};
- if (!DevirtModule(M, AARGetter, nullptr, nullptr).run())
+ auto OREGetter = [&](Function *F) -> OptimizationRemarkEmitter & {
+ return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F);
+ };
+ if (!DevirtModule(M, AARGetter, OREGetter, nullptr, nullptr).run())
return PreservedAnalyses::all();
return PreservedAnalyses::none();
}
bool DevirtModule::runForTesting(
- Module &M, function_ref<AAResults &(Function &)> AARGetter) {
+ Module &M, function_ref<AAResults &(Function &)> AARGetter,
+ function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter) {
ModuleSummaryIndex Summary;
// Handle the command-line summary arguments. This code is for testing
@@ -566,7 +605,7 @@ bool DevirtModule::runForTesting(
bool Changed =
DevirtModule(
- M, AARGetter,
+ M, AARGetter, OREGetter,
ClSummaryAction == PassSummaryAction::Export ? &Summary : nullptr,
ClSummaryAction == PassSummaryAction::Import ? &Summary : nullptr)
.run();
@@ -684,7 +723,7 @@ void DevirtModule::applySingleImplDevirt(VTableSlotInfo &SlotInfo,
auto Apply = [&](CallSiteInfo &CSInfo) {
for (auto &&VCallSite : CSInfo.CallSites) {
if (RemarksEnabled)
- VCallSite.emitRemark("single-impl", TheFn->getName());
+ VCallSite.emitRemark("single-impl", TheFn->getName(), OREGetter);
VCallSite.CS.setCalledFunction(ConstantExpr::getBitCast(
TheFn, VCallSite.CS.getCalledValue()->getType()));
// This use is no longer unsafe.
@@ -724,9 +763,24 @@ bool DevirtModule::trySingleImplDevirt(
// to make it visible to thin LTO objects. We can only get here during the
// ThinLTO export phase.
if (TheFn->hasLocalLinkage()) {
+ std::string NewName = (TheFn->getName() + "$merged").str();
+
+ // 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
+ // must match one of the names of the symbols in the comdat.
+ if (Comdat *C = TheFn->getComdat()) {
+ if (C->getName() == TheFn->getName()) {
+ Comdat *NewC = M.getOrInsertComdat(NewName);
+ NewC->setSelectionKind(C->getSelectionKind());
+ for (GlobalObject &GO : M.global_objects())
+ if (GO.getComdat() == C)
+ GO.setComdat(NewC);
+ }
+ }
+
TheFn->setLinkage(GlobalValue::ExternalLinkage);
TheFn->setVisibility(GlobalValue::HiddenVisibility);
- TheFn->setName(TheFn->getName() + "$merged");
+ TheFn->setName(NewName);
}
Res->TheKind = WholeProgramDevirtResolution::SingleImpl;
@@ -769,7 +823,7 @@ void DevirtModule::applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
uint64_t TheRetVal) {
for (auto Call : CSInfo.CallSites)
Call.replaceAndErase(
- "uniform-ret-val", FnName, RemarksEnabled,
+ "uniform-ret-val", FnName, RemarksEnabled, OREGetter,
ConstantInt::get(cast<IntegerType>(Call.CS.getType()), TheRetVal));
CSInfo.markDevirt();
}
@@ -808,6 +862,12 @@ std::string DevirtModule::getGlobalName(VTableSlot Slot,
return OS.str();
}
+bool DevirtModule::shouldExportConstantsAsAbsoluteSymbols() {
+ Triple T(M.getTargetTriple());
+ return (T.getArch() == Triple::x86 || T.getArch() == Triple::x86_64) &&
+ T.getObjectFormat() == Triple::ELF;
+}
+
void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
StringRef Name, Constant *C) {
GlobalAlias *GA = GlobalAlias::create(Int8Ty, 0, GlobalValue::ExternalLinkage,
@@ -815,27 +875,55 @@ void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
GA->setVisibility(GlobalValue::HiddenVisibility);
}
+void DevirtModule::exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
+ StringRef Name, uint32_t Const,
+ uint32_t &Storage) {
+ if (shouldExportConstantsAsAbsoluteSymbols()) {
+ exportGlobal(
+ Slot, Args, Name,
+ ConstantExpr::getIntToPtr(ConstantInt::get(Int32Ty, Const), Int8PtrTy));
+ return;
+ }
+
+ Storage = Const;
+}
+
Constant *DevirtModule::importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args,
- StringRef Name, unsigned AbsWidth) {
+ StringRef Name) {
Constant *C = M.getOrInsertGlobal(getGlobalName(Slot, Args, Name), Int8Ty);
auto *GV = dyn_cast<GlobalVariable>(C);
+ if (GV)
+ GV->setVisibility(GlobalValue::HiddenVisibility);
+ return C;
+}
+
+Constant *DevirtModule::importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args,
+ StringRef Name, IntegerType *IntTy,
+ uint32_t Storage) {
+ if (!shouldExportConstantsAsAbsoluteSymbols())
+ return ConstantInt::get(IntTy, Storage);
+
+ Constant *C = importGlobal(Slot, Args, Name);
+ auto *GV = cast<GlobalVariable>(C->stripPointerCasts());
+ C = ConstantExpr::getPtrToInt(C, IntTy);
+
// We only need to set metadata if the global is newly created, in which
// case it would not have hidden visibility.
- if (!GV || GV->getVisibility() == GlobalValue::HiddenVisibility)
+ if (GV->getMetadata(LLVMContext::MD_absolute_symbol))
return C;
- GV->setVisibility(GlobalValue::HiddenVisibility);
auto SetAbsRange = [&](uint64_t Min, uint64_t Max) {
auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Min));
auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Max));
GV->setMetadata(LLVMContext::MD_absolute_symbol,
MDNode::get(M.getContext(), {MinC, MaxC}));
};
+ unsigned AbsWidth = IntTy->getBitWidth();
if (AbsWidth == IntPtrTy->getBitWidth())
SetAbsRange(~0ull, ~0ull); // Full set.
- else if (AbsWidth)
+ else
SetAbsRange(0, 1ull << AbsWidth);
- return GV;
+ return C;
}
void DevirtModule::applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
@@ -843,10 +931,12 @@ void DevirtModule::applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName,
Constant *UniqueMemberAddr) {
for (auto &&Call : CSInfo.CallSites) {
IRBuilder<> B(Call.CS.getInstruction());
- Value *Cmp = B.CreateICmp(IsOne ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE,
- Call.VTable, UniqueMemberAddr);
+ Value *Cmp =
+ B.CreateICmp(IsOne ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE,
+ B.CreateBitCast(Call.VTable, Int8PtrTy), UniqueMemberAddr);
Cmp = B.CreateZExt(Cmp, Call.CS->getType());
- Call.replaceAndErase("unique-ret-val", FnName, RemarksEnabled, Cmp);
+ Call.replaceAndErase("unique-ret-val", FnName, RemarksEnabled, OREGetter,
+ Cmp);
}
CSInfo.markDevirt();
}
@@ -909,17 +999,19 @@ void DevirtModule::applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName,
for (auto Call : CSInfo.CallSites) {
auto *RetType = cast<IntegerType>(Call.CS.getType());
IRBuilder<> B(Call.CS.getInstruction());
- Value *Addr = B.CreateGEP(Int8Ty, Call.VTable, Byte);
+ Value *Addr =
+ B.CreateGEP(Int8Ty, B.CreateBitCast(Call.VTable, Int8PtrTy), Byte);
if (RetType->getBitWidth() == 1) {
Value *Bits = B.CreateLoad(Addr);
Value *BitsAndBit = B.CreateAnd(Bits, Bit);
auto IsBitSet = B.CreateICmpNE(BitsAndBit, ConstantInt::get(Int8Ty, 0));
Call.replaceAndErase("virtual-const-prop-1-bit", FnName, RemarksEnabled,
- IsBitSet);
+ OREGetter, IsBitSet);
} else {
Value *ValAddr = B.CreateBitCast(Addr, RetType->getPointerTo());
Value *Val = B.CreateLoad(RetType, ValAddr);
- Call.replaceAndErase("virtual-const-prop", FnName, RemarksEnabled, Val);
+ Call.replaceAndErase("virtual-const-prop", FnName, RemarksEnabled,
+ OREGetter, Val);
}
}
CSInfo.markDevirt();
@@ -1007,18 +1099,18 @@ bool DevirtModule::tryVirtualConstProp(
for (auto &&Target : TargetsForSlot)
Target.WasDevirt = true;
- Constant *ByteConst = ConstantInt::get(Int32Ty, OffsetByte);
- Constant *BitConst = ConstantInt::get(Int8Ty, 1ULL << OffsetBit);
if (CSByConstantArg.second.isExported()) {
ResByArg->TheKind = WholeProgramDevirtResolution::ByArg::VirtualConstProp;
- exportGlobal(Slot, CSByConstantArg.first, "byte",
- ConstantExpr::getIntToPtr(ByteConst, Int8PtrTy));
- exportGlobal(Slot, CSByConstantArg.first, "bit",
- ConstantExpr::getIntToPtr(BitConst, Int8PtrTy));
+ exportConstant(Slot, CSByConstantArg.first, "byte", OffsetByte,
+ ResByArg->Byte);
+ exportConstant(Slot, CSByConstantArg.first, "bit", 1ULL << OffsetBit,
+ ResByArg->Bit);
}
// Rewrite each call to a load from OffsetByte/OffsetBit.
+ Constant *ByteConst = ConstantInt::get(Int32Ty, OffsetByte);
+ Constant *BitConst = ConstantInt::get(Int8Ty, 1ULL << OffsetBit);
applyVirtualConstProp(CSByConstantArg.second,
TargetsForSlot[0].Fn->getName(), ByteConst, BitConst);
}
@@ -1112,8 +1204,7 @@ void DevirtModule::scanTypeTestUsers(Function *TypeTestFunc,
Value *Ptr = CI->getArgOperand(0)->stripPointerCasts();
if (SeenPtrs.insert(Ptr).second) {
for (DevirtCallSite Call : DevirtCalls) {
- CallSlots[{TypeId, Call.Offset}].addCallSite(CI->getArgOperand(0),
- Call.CS, nullptr);
+ CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CS, nullptr);
}
}
}
@@ -1250,10 +1341,10 @@ void DevirtModule::importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo) {
break;
}
case WholeProgramDevirtResolution::ByArg::VirtualConstProp: {
- Constant *Byte = importGlobal(Slot, CSByConstantArg.first, "byte", 32);
- Byte = ConstantExpr::getPtrToInt(Byte, Int32Ty);
- Constant *Bit = importGlobal(Slot, CSByConstantArg.first, "bit", 8);
- Bit = ConstantExpr::getPtrToInt(Bit, Int8Ty);
+ Constant *Byte = importConstant(Slot, CSByConstantArg.first, "byte",
+ Int32Ty, ResByArg.Byte);
+ Constant *Bit = importConstant(Slot, CSByConstantArg.first, "bit", Int8Ty,
+ ResByArg.Bit);
applyVirtualConstProp(CSByConstantArg.second, "", Byte, Bit);
}
default:
@@ -1406,9 +1497,24 @@ bool DevirtModule::run() {
// Generate remarks for each devirtualized function.
for (const auto &DT : DevirtTargets) {
Function *F = DT.second;
- DISubprogram *SP = F->getSubprogram();
- emitOptimizationRemark(F->getContext(), DEBUG_TYPE, *F, SP,
- Twine("devirtualized ") + F->getName());
+
+ // In the new pass manager, we can request the optimization
+ // remark emitter pass on a per-function-basis, which the
+ // OREGetter will do for us.
+ // In the old pass manager, this is harder, so we just build
+ // a optimization remark emitter on the fly, when we need it.
+ std::unique_ptr<OptimizationRemarkEmitter> OwnedORE;
+ OptimizationRemarkEmitter *ORE;
+ if (OREGetter)
+ ORE = &OREGetter(F);
+ else {
+ OwnedORE = make_unique<OptimizationRemarkEmitter>(F);
+ ORE = OwnedORE.get();
+ }
+
+ using namespace ore;
+ ORE->emit(OptimizationRemark(DEBUG_TYPE, "Devirtualized", F)
+ << "devirtualized " << NV("FunctionName", F->getName()));
}
}