aboutsummaryrefslogtreecommitdiff
path: root/lib/Transforms/IPO/LowerTypeTests.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
committerDimitry Andric <dim@FreeBSD.org>2018-07-28 10:51:19 +0000
commiteb11fae6d08f479c0799db45860a98af528fa6e7 (patch)
tree44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /lib/Transforms/IPO/LowerTypeTests.cpp
parentb8a2042aa938069e862750553db0e4d82d25822c (diff)
downloadsrc-eb11fae6d08f479c0799db45860a98af528fa6e7.tar.gz
src-eb11fae6d08f479c0799db45860a98af528fa6e7.zip
Vendor import of llvm trunk r338150:vendor/llvm/llvm-trunk-r338150
Notes
Notes: svn path=/vendor/llvm/dist/; revision=336809 svn path=/vendor/llvm/llvm-trunk-r338150/; revision=336814; tag=vendor/llvm/llvm-trunk-r338150
Diffstat (limited to 'lib/Transforms/IPO/LowerTypeTests.cpp')
-rw-r--r--lib/Transforms/IPO/LowerTypeTests.cpp433
1 files changed, 363 insertions, 70 deletions
diff --git a/lib/Transforms/IPO/LowerTypeTests.cpp b/lib/Transforms/IPO/LowerTypeTests.cpp
index 8db7e1e142d2..4f7571884707 100644
--- a/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -8,6 +8,8 @@
//===----------------------------------------------------------------------===//
//
// This pass lowers type metadata and calls to the llvm.type.test intrinsic.
+// It also ensures that globals are properly laid out for the
+// llvm.icall.branch.funnel intrinsic.
// See http://llvm.org/docs/TypeMetadata.html for more information.
//
//===----------------------------------------------------------------------===//
@@ -25,6 +27,7 @@
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/TypeMetadataUtils.h"
+#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
@@ -291,6 +294,33 @@ public:
}
};
+struct ICallBranchFunnel final
+ : TrailingObjects<ICallBranchFunnel, GlobalTypeMember *> {
+ static ICallBranchFunnel *create(BumpPtrAllocator &Alloc, CallInst *CI,
+ ArrayRef<GlobalTypeMember *> Targets,
+ unsigned UniqueId) {
+ auto *Call = static_cast<ICallBranchFunnel *>(
+ Alloc.Allocate(totalSizeToAlloc<GlobalTypeMember *>(Targets.size()),
+ alignof(ICallBranchFunnel)));
+ Call->CI = CI;
+ Call->UniqueId = UniqueId;
+ Call->NTargets = Targets.size();
+ std::uninitialized_copy(Targets.begin(), Targets.end(),
+ Call->getTrailingObjects<GlobalTypeMember *>());
+ return Call;
+ }
+
+ CallInst *CI;
+ ArrayRef<GlobalTypeMember *> targets() const {
+ return makeArrayRef(getTrailingObjects<GlobalTypeMember *>(), NTargets);
+ }
+
+ unsigned UniqueId;
+
+private:
+ size_t NTargets;
+};
+
class LowerTypeTestsModule {
Module &M;
@@ -372,6 +402,7 @@ class LowerTypeTestsModule {
const DenseMap<GlobalTypeMember *, uint64_t> &GlobalLayout);
Value *lowerTypeTestCall(Metadata *TypeId, CallInst *CI,
const TypeIdLowering &TIL);
+
void buildBitSetsFromGlobalVariables(ArrayRef<Metadata *> TypeIds,
ArrayRef<GlobalTypeMember *> Globals);
unsigned getJumpTableEntrySize();
@@ -383,19 +414,32 @@ class LowerTypeTestsModule {
void buildBitSetsFromFunctions(ArrayRef<Metadata *> TypeIds,
ArrayRef<GlobalTypeMember *> Functions);
void buildBitSetsFromFunctionsNative(ArrayRef<Metadata *> TypeIds,
- ArrayRef<GlobalTypeMember *> Functions);
+ ArrayRef<GlobalTypeMember *> Functions);
void buildBitSetsFromFunctionsWASM(ArrayRef<Metadata *> TypeIds,
ArrayRef<GlobalTypeMember *> Functions);
- void buildBitSetsFromDisjointSet(ArrayRef<Metadata *> TypeIds,
- ArrayRef<GlobalTypeMember *> Globals);
+ void
+ buildBitSetsFromDisjointSet(ArrayRef<Metadata *> TypeIds,
+ ArrayRef<GlobalTypeMember *> Globals,
+ ArrayRef<ICallBranchFunnel *> ICallBranchFunnels);
- void replaceWeakDeclarationWithJumpTablePtr(Function *F, Constant *JT);
+ void replaceWeakDeclarationWithJumpTablePtr(Function *F, Constant *JT, bool IsDefinition);
void moveInitializerToModuleConstructor(GlobalVariable *GV);
void findGlobalVariableUsersOf(Constant *C,
SmallSetVector<GlobalVariable *, 8> &Out);
void createJumpTable(Function *F, ArrayRef<GlobalTypeMember *> Functions);
+ /// replaceCfiUses - Go through the uses list for this definition
+ /// and make each use point to "V" instead of "this" when the use is outside
+ /// the block. 'This's use list is expected to have at least one element.
+ /// Unlike replaceAllUsesWith this function skips blockaddr and direct call
+ /// uses.
+ void replaceCfiUses(Function *Old, Value *New, bool IsDefinition);
+
+ /// replaceDirectCalls - Go through the uses list for this definition and
+ /// replace each use, which is a direct function call.
+ void replaceDirectCalls(Value *Old, Value *New);
+
public:
LowerTypeTestsModule(Module &M, ModuleSummaryIndex *ExportSummary,
const ModuleSummaryIndex *ImportSummary);
@@ -427,8 +471,6 @@ struct LowerTypeTests : public ModulePass {
}
bool runOnModule(Module &M) override {
- if (skipModule(M))
- return false;
if (UseCommandLine)
return LowerTypeTestsModule::runForTesting(M);
return LowerTypeTestsModule(M, ExportSummary, ImportSummary).lower();
@@ -729,10 +771,12 @@ void LowerTypeTestsModule::buildBitSetsFromGlobalVariables(
// Compute the amount of padding required.
uint64_t Padding = NextPowerOf2(InitSize - 1) - InitSize;
- // Cap at 128 was found experimentally to have a good data/instruction
- // overhead tradeoff.
- if (Padding > 128)
- Padding = alignTo(InitSize, 128) - InitSize;
+ // Experiments of different caps with Chromium on both x64 and ARM64
+ // have shown that the 32-byte cap generates the smallest binary on
+ // both platforms while different caps yield similar performance.
+ // (see https://lists.llvm.org/pipermail/llvm-dev/2018-July/124694.html)
+ if (Padding > 32)
+ Padding = alignTo(InitSize, 32) - InitSize;
GlobalInits.push_back(
ConstantAggregateZero::get(ArrayType::get(Int8Ty, Padding)));
@@ -936,14 +980,23 @@ void LowerTypeTestsModule::importTypeTest(CallInst *CI) {
void LowerTypeTestsModule::importFunction(Function *F, bool isDefinition) {
assert(F->getType()->getAddressSpace() == 0);
- // Declaration of a local function - nothing to do.
- if (F->isDeclarationForLinker() && isDefinition)
- return;
-
GlobalValue::VisibilityTypes Visibility = F->getVisibility();
std::string Name = F->getName();
- Function *FDecl;
+ if (F->isDeclarationForLinker() && isDefinition) {
+ // Non-dso_local functions may be overriden at run time,
+ // don't short curcuit them
+ if (F->isDSOLocal()) {
+ Function *RealF = Function::Create(F->getFunctionType(),
+ GlobalValue::ExternalLinkage,
+ Name + ".cfi", &M);
+ RealF->setVisibility(GlobalVariable::HiddenVisibility);
+ replaceDirectCalls(F, RealF);
+ }
+ return;
+ }
+
+ Function *FDecl;
if (F->isDeclarationForLinker() && !isDefinition) {
// Declaration of an external function.
FDecl = Function::Create(F->getFunctionType(), GlobalValue::ExternalLinkage,
@@ -952,10 +1005,25 @@ void LowerTypeTestsModule::importFunction(Function *F, bool isDefinition) {
} else if (isDefinition) {
F->setName(Name + ".cfi");
F->setLinkage(GlobalValue::ExternalLinkage);
- F->setVisibility(GlobalValue::HiddenVisibility);
FDecl = Function::Create(F->getFunctionType(), GlobalValue::ExternalLinkage,
Name, &M);
FDecl->setVisibility(Visibility);
+ Visibility = GlobalValue::HiddenVisibility;
+
+ // Delete aliases pointing to this function, they'll be re-created in the
+ // merged output
+ SmallVector<GlobalAlias*, 4> ToErase;
+ for (auto &U : F->uses()) {
+ if (auto *A = dyn_cast<GlobalAlias>(U.getUser())) {
+ Function *AliasDecl = Function::Create(
+ F->getFunctionType(), GlobalValue::ExternalLinkage, "", &M);
+ AliasDecl->takeName(A);
+ A->replaceAllUsesWith(AliasDecl);
+ ToErase.push_back(A);
+ }
+ }
+ for (auto *A : ToErase)
+ A->eraseFromParent();
} else {
// Function definition without type metadata, where some other translation
// unit contained a declaration with type metadata. This normally happens
@@ -966,9 +1034,13 @@ void LowerTypeTestsModule::importFunction(Function *F, bool isDefinition) {
}
if (F->isWeakForLinker())
- replaceWeakDeclarationWithJumpTablePtr(F, FDecl);
+ replaceWeakDeclarationWithJumpTablePtr(F, FDecl, isDefinition);
else
- F->replaceAllUsesWith(FDecl);
+ replaceCfiUses(F, FDecl, isDefinition);
+
+ // Set visibility late because it's used in replaceCfiUses() to determine
+ // whether uses need to to be replaced.
+ F->setVisibility(Visibility);
}
void LowerTypeTestsModule::lowerTypeTestCalls(
@@ -980,7 +1052,7 @@ void LowerTypeTestsModule::lowerTypeTestCalls(
for (Metadata *TypeId : TypeIds) {
// Build the bitset.
BitSetInfo BSI = buildBitSet(TypeId, GlobalLayout);
- DEBUG({
+ LLVM_DEBUG({
if (auto MDS = dyn_cast<MDString>(TypeId))
dbgs() << MDS->getString() << ": ";
else
@@ -1150,7 +1222,7 @@ void LowerTypeTestsModule::findGlobalVariableUsersOf(
// Replace all uses of F with (F ? JT : 0).
void LowerTypeTestsModule::replaceWeakDeclarationWithJumpTablePtr(
- Function *F, Constant *JT) {
+ Function *F, Constant *JT, bool IsDefinition) {
// The target expression can not appear in a constant initializer on most
// (all?) targets. Switch to a runtime initializer.
SmallSetVector<GlobalVariable *, 8> GlobalVarUsers;
@@ -1163,7 +1235,7 @@ void LowerTypeTestsModule::replaceWeakDeclarationWithJumpTablePtr(
Function *PlaceholderFn =
Function::Create(cast<FunctionType>(F->getValueType()),
GlobalValue::ExternalWeakLinkage, "", &M);
- F->replaceAllUsesWith(PlaceholderFn);
+ replaceCfiUses(F, PlaceholderFn, IsDefinition);
Constant *Target = ConstantExpr::getSelect(
ConstantExpr::getICmp(CmpInst::ICMP_NE, F,
@@ -1226,12 +1298,6 @@ void LowerTypeTestsModule::createJumpTable(
createJumpTableEntry(AsmOS, ConstraintOS, JumpTableArch, AsmArgs,
cast<Function>(Functions[I]->getGlobal()));
- // Try to emit the jump table at the end of the text segment.
- // Jump table must come after __cfi_check in the cross-dso mode.
- // FIXME: this magic section name seems to do the trick.
- F->setSection(ObjectFormat == Triple::MachO
- ? "__TEXT,__text,regular,pure_instructions"
- : ".text.cfi");
// Align the whole table by entry size.
F->setAlignment(getJumpTableEntrySize());
// Skip prologue.
@@ -1248,6 +1314,8 @@ void LowerTypeTestsModule::createJumpTable(
// by Clang for -march=armv7.
F->addFnAttr("target-cpu", "cortex-a8");
}
+ // Make sure we don't emit .eh_frame for this function.
+ F->addFnAttr(Attribute::NoUnwind);
BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", F);
IRBuilder<> IRB(BB);
@@ -1389,9 +1457,9 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsNative(
}
if (!IsDefinition) {
if (F->isWeakForLinker())
- replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr);
+ replaceWeakDeclarationWithJumpTablePtr(F, CombinedGlobalElemPtr, IsDefinition);
else
- F->replaceAllUsesWith(CombinedGlobalElemPtr);
+ replaceCfiUses(F, CombinedGlobalElemPtr, IsDefinition);
} else {
assert(F->getType()->getAddressSpace() == 0);
@@ -1401,10 +1469,10 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsNative(
FAlias->takeName(F);
if (FAlias->hasName())
F->setName(FAlias->getName() + ".cfi");
- F->replaceUsesExceptBlockAddr(FAlias);
+ replaceCfiUses(F, FAlias, IsDefinition);
+ if (!F->hasLocalLinkage())
+ F->setVisibility(GlobalVariable::HiddenVisibility);
}
- if (!F->isDeclarationForLinker())
- F->setLinkage(GlobalValue::InternalLinkage);
}
createJumpTable(JumpTableFn, Functions);
@@ -1447,7 +1515,8 @@ void LowerTypeTestsModule::buildBitSetsFromFunctionsWASM(
}
void LowerTypeTestsModule::buildBitSetsFromDisjointSet(
- ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalTypeMember *> Globals) {
+ ArrayRef<Metadata *> TypeIds, ArrayRef<GlobalTypeMember *> Globals,
+ ArrayRef<ICallBranchFunnel *> ICallBranchFunnels) {
DenseMap<Metadata *, uint64_t> TypeIdIndices;
for (unsigned I = 0; I != TypeIds.size(); ++I)
TypeIdIndices[TypeIds[I]] = I;
@@ -1456,15 +1525,25 @@ void LowerTypeTestsModule::buildBitSetsFromDisjointSet(
// the type identifier.
std::vector<std::set<uint64_t>> TypeMembers(TypeIds.size());
unsigned GlobalIndex = 0;
+ DenseMap<GlobalTypeMember *, uint64_t> GlobalIndices;
for (GlobalTypeMember *GTM : Globals) {
for (MDNode *Type : GTM->types()) {
// Type = { offset, type identifier }
- unsigned TypeIdIndex = TypeIdIndices[Type->getOperand(1)];
- TypeMembers[TypeIdIndex].insert(GlobalIndex);
+ auto I = TypeIdIndices.find(Type->getOperand(1));
+ if (I != TypeIdIndices.end())
+ TypeMembers[I->second].insert(GlobalIndex);
}
+ GlobalIndices[GTM] = GlobalIndex;
GlobalIndex++;
}
+ for (ICallBranchFunnel *JT : ICallBranchFunnels) {
+ TypeMembers.emplace_back();
+ std::set<uint64_t> &TMSet = TypeMembers.back();
+ for (GlobalTypeMember *T : JT->targets())
+ TMSet.insert(GlobalIndices[T]);
+ }
+
// Order the sets of indices by size. The GlobalLayoutBuilder works best
// when given small index sets first.
std::stable_sort(
@@ -1514,7 +1593,7 @@ LowerTypeTestsModule::LowerTypeTestsModule(
}
bool LowerTypeTestsModule::runForTesting(Module &M) {
- ModuleSummaryIndex Summary;
+ ModuleSummaryIndex Summary(/*HaveGVs=*/false);
// Handle the command-line summary arguments. This code is for testing
// purposes only, so we handle errors directly.
@@ -1549,11 +1628,71 @@ bool LowerTypeTestsModule::runForTesting(Module &M) {
return Changed;
}
+static bool isDirectCall(Use& U) {
+ auto *Usr = dyn_cast<CallInst>(U.getUser());
+ if (Usr) {
+ CallSite CS(Usr);
+ if (CS.isCallee(&U))
+ return true;
+ }
+ return false;
+}
+
+void LowerTypeTestsModule::replaceCfiUses(Function *Old, Value *New, bool IsDefinition) {
+ SmallSetVector<Constant *, 4> Constants;
+ auto UI = Old->use_begin(), E = Old->use_end();
+ for (; UI != E;) {
+ Use &U = *UI;
+ ++UI;
+
+ // Skip block addresses
+ if (isa<BlockAddress>(U.getUser()))
+ continue;
+
+ // Skip direct calls to externally defined or non-dso_local functions
+ if (isDirectCall(U) && (Old->isDSOLocal() || !IsDefinition))
+ continue;
+
+ // Must handle Constants specially, we cannot call replaceUsesOfWith on a
+ // constant because they are uniqued.
+ if (auto *C = dyn_cast<Constant>(U.getUser())) {
+ if (!isa<GlobalValue>(C)) {
+ // Save unique users to avoid processing operand replacement
+ // more than once.
+ Constants.insert(C);
+ continue;
+ }
+ }
+
+ U.set(New);
+ }
+
+ // Process operand replacement of saved constants.
+ for (auto *C : Constants)
+ C->handleOperandChange(Old, New);
+}
+
+void LowerTypeTestsModule::replaceDirectCalls(Value *Old, Value *New) {
+ auto UI = Old->use_begin(), E = Old->use_end();
+ for (; UI != E;) {
+ Use &U = *UI;
+ ++UI;
+
+ if (!isDirectCall(U))
+ continue;
+
+ U.set(New);
+ }
+}
+
bool LowerTypeTestsModule::lower() {
Function *TypeTestFunc =
M.getFunction(Intrinsic::getName(Intrinsic::type_test));
- if ((!TypeTestFunc || TypeTestFunc->use_empty()) && !ExportSummary &&
- !ImportSummary)
+ Function *ICallBranchFunnelFunc =
+ M.getFunction(Intrinsic::getName(Intrinsic::icall_branch_funnel));
+ if ((!TypeTestFunc || TypeTestFunc->use_empty()) &&
+ (!ICallBranchFunnelFunc || ICallBranchFunnelFunc->use_empty()) &&
+ !ExportSummary && !ImportSummary)
return false;
if (ImportSummary) {
@@ -1565,6 +1704,10 @@ bool LowerTypeTestsModule::lower() {
}
}
+ if (ICallBranchFunnelFunc && !ICallBranchFunnelFunc->use_empty())
+ report_fatal_error(
+ "unexpected call to llvm.icall.branch.funnel during import phase");
+
SmallVector<Function *, 8> Defs;
SmallVector<Function *, 8> Decls;
for (auto &F : M) {
@@ -1589,8 +1732,8 @@ bool LowerTypeTestsModule::lower() {
// Equivalence class set containing type identifiers and the globals that
// reference them. This is used to partition the set of type identifiers in
// the module into disjoint sets.
- using GlobalClassesTy =
- EquivalenceClasses<PointerUnion<GlobalTypeMember *, Metadata *>>;
+ using GlobalClassesTy = EquivalenceClasses<
+ PointerUnion3<GlobalTypeMember *, Metadata *, ICallBranchFunnel *>>;
GlobalClassesTy GlobalClasses;
// Verify the type metadata and build a few data structures to let us
@@ -1602,33 +1745,61 @@ bool LowerTypeTestsModule::lower() {
// identifiers.
BumpPtrAllocator Alloc;
struct TIInfo {
- unsigned Index;
+ unsigned UniqueId;
std::vector<GlobalTypeMember *> RefGlobals;
};
DenseMap<Metadata *, TIInfo> TypeIdInfo;
- unsigned I = 0;
+ unsigned CurUniqueId = 0;
SmallVector<MDNode *, 2> Types;
+ // Cross-DSO CFI emits jumptable entries for exported functions as well as
+ // address taken functions in case they are address taken in other modules.
+ const bool CrossDsoCfi = M.getModuleFlag("Cross-DSO CFI") != nullptr;
+
struct ExportedFunctionInfo {
CfiFunctionLinkage Linkage;
MDNode *FuncMD; // {name, linkage, type[, type...]}
};
DenseMap<StringRef, ExportedFunctionInfo> ExportedFunctions;
if (ExportSummary) {
+ // A set of all functions that are address taken by a live global object.
+ DenseSet<GlobalValue::GUID> AddressTaken;
+ for (auto &I : *ExportSummary)
+ for (auto &GVS : I.second.SummaryList)
+ if (GVS->isLive())
+ for (auto &Ref : GVS->refs())
+ AddressTaken.insert(Ref.getGUID());
+
NamedMDNode *CfiFunctionsMD = M.getNamedMetadata("cfi.functions");
if (CfiFunctionsMD) {
for (auto FuncMD : CfiFunctionsMD->operands()) {
assert(FuncMD->getNumOperands() >= 2);
StringRef FunctionName =
cast<MDString>(FuncMD->getOperand(0))->getString();
- if (!ExportSummary->isGUIDLive(GlobalValue::getGUID(
- GlobalValue::dropLLVMManglingEscape(FunctionName))))
- continue;
CfiFunctionLinkage Linkage = static_cast<CfiFunctionLinkage>(
cast<ConstantAsMetadata>(FuncMD->getOperand(1))
->getValue()
->getUniqueInteger()
.getZExtValue());
+ const GlobalValue::GUID GUID = GlobalValue::getGUID(
+ GlobalValue::dropLLVMManglingEscape(FunctionName));
+ // Do not emit jumptable entries for functions that are not-live and
+ // have no live references (and are not exported with cross-DSO CFI.)
+ if (!ExportSummary->isGUIDLive(GUID))
+ continue;
+ if (!AddressTaken.count(GUID)) {
+ if (!CrossDsoCfi || Linkage != CFL_Definition)
+ continue;
+
+ bool Exported = false;
+ if (auto VI = ExportSummary->getValueInfo(GUID))
+ for (auto &GVS : VI.getSummaryList())
+ if (GVS->isLive() && !GlobalValue::isLocalLinkage(GVS->linkage()))
+ Exported = true;
+
+ if (!Exported)
+ continue;
+ }
auto P = ExportedFunctions.insert({FunctionName, {Linkage, FuncMD}});
if (!P.second && P.first->second.Linkage != CFL_Definition)
P.first->second = {Linkage, FuncMD};
@@ -1656,6 +1827,11 @@ bool LowerTypeTestsModule::lower() {
F->clearMetadata();
}
+ // Update the linkage for extern_weak declarations when a definition
+ // exists.
+ if (Linkage == CFL_Definition && F->hasExternalWeakLinkage())
+ F->setLinkage(GlobalValue::ExternalLinkage);
+
// If the function in the full LTO module is a declaration, replace its
// type metadata with the type metadata we found in cfi.functions. That
// metadata is presumed to be more accurate than the metadata attached
@@ -1673,28 +1849,37 @@ bool LowerTypeTestsModule::lower() {
}
}
+ DenseMap<GlobalObject *, GlobalTypeMember *> GlobalTypeMembers;
for (GlobalObject &GO : M.global_objects()) {
if (isa<GlobalVariable>(GO) && GO.isDeclarationForLinker())
continue;
Types.clear();
GO.getMetadata(LLVMContext::MD_type, Types);
- if (Types.empty())
- continue;
bool IsDefinition = !GO.isDeclarationForLinker();
bool IsExported = false;
- if (isa<Function>(GO) && ExportedFunctions.count(GO.getName())) {
- IsDefinition |= ExportedFunctions[GO.getName()].Linkage == CFL_Definition;
- IsExported = true;
+ if (Function *F = dyn_cast<Function>(&GO)) {
+ if (ExportedFunctions.count(F->getName())) {
+ IsDefinition |= ExportedFunctions[F->getName()].Linkage == CFL_Definition;
+ IsExported = true;
+ // TODO: The logic here checks only that the function is address taken,
+ // not that the address takers are live. This can be updated to check
+ // their liveness and emit fewer jumptable entries once monolithic LTO
+ // builds also emit summaries.
+ } else if (!F->hasAddressTaken()) {
+ if (!CrossDsoCfi || !IsDefinition || F->hasLocalLinkage())
+ continue;
+ }
}
auto *GTM =
GlobalTypeMember::create(Alloc, &GO, IsDefinition, IsExported, Types);
+ GlobalTypeMembers[&GO] = GTM;
for (MDNode *Type : Types) {
verifyTypeMDNode(&GO, Type);
auto &Info = TypeIdInfo[Type->getOperand(1)];
- Info.Index = ++I;
+ Info.UniqueId = ++CurUniqueId;
Info.RefGlobals.push_back(GTM);
}
}
@@ -1731,6 +1916,44 @@ bool LowerTypeTestsModule::lower() {
}
}
+ if (ICallBranchFunnelFunc) {
+ for (const Use &U : ICallBranchFunnelFunc->uses()) {
+ if (Arch != Triple::x86_64)
+ report_fatal_error(
+ "llvm.icall.branch.funnel not supported on this target");
+
+ auto CI = cast<CallInst>(U.getUser());
+
+ std::vector<GlobalTypeMember *> Targets;
+ if (CI->getNumArgOperands() % 2 != 1)
+ report_fatal_error("number of arguments should be odd");
+
+ GlobalClassesTy::member_iterator CurSet;
+ for (unsigned I = 1; I != CI->getNumArgOperands(); I += 2) {
+ int64_t Offset;
+ auto *Base = dyn_cast<GlobalObject>(GetPointerBaseWithConstantOffset(
+ CI->getOperand(I), Offset, M.getDataLayout()));
+ if (!Base)
+ report_fatal_error(
+ "Expected branch funnel operand to be global value");
+
+ GlobalTypeMember *GTM = GlobalTypeMembers[Base];
+ Targets.push_back(GTM);
+ GlobalClassesTy::member_iterator NewSet =
+ GlobalClasses.findLeader(GlobalClasses.insert(GTM));
+ if (I == 1)
+ CurSet = NewSet;
+ else
+ CurSet = GlobalClasses.unionSets(CurSet, NewSet);
+ }
+
+ GlobalClasses.unionSets(
+ CurSet, GlobalClasses.findLeader(
+ GlobalClasses.insert(ICallBranchFunnel::create(
+ Alloc, CI, Targets, ++CurUniqueId))));
+ }
+ }
+
if (ExportSummary) {
DenseMap<GlobalValue::GUID, TinyPtrVector<Metadata *>> MetadataByGUID;
for (auto &P : TypeIdInfo) {
@@ -1764,54 +1987,124 @@ bool LowerTypeTestsModule::lower() {
continue;
++NumTypeIdDisjointSets;
- unsigned MaxIndex = 0;
+ unsigned MaxUniqueId = 0;
for (GlobalClassesTy::member_iterator MI = GlobalClasses.member_begin(I);
MI != GlobalClasses.member_end(); ++MI) {
- if ((*MI).is<Metadata *>())
- MaxIndex = std::max(MaxIndex, TypeIdInfo[MI->get<Metadata *>()].Index);
+ if (auto *MD = MI->dyn_cast<Metadata *>())
+ MaxUniqueId = std::max(MaxUniqueId, TypeIdInfo[MD].UniqueId);
+ else if (auto *BF = MI->dyn_cast<ICallBranchFunnel *>())
+ MaxUniqueId = std::max(MaxUniqueId, BF->UniqueId);
}
- Sets.emplace_back(I, MaxIndex);
+ Sets.emplace_back(I, MaxUniqueId);
}
- std::sort(Sets.begin(), Sets.end(),
- [](const std::pair<GlobalClassesTy::iterator, unsigned> &S1,
- const std::pair<GlobalClassesTy::iterator, unsigned> &S2) {
- return S1.second < S2.second;
- });
+ llvm::sort(Sets.begin(), Sets.end(),
+ [](const std::pair<GlobalClassesTy::iterator, unsigned> &S1,
+ const std::pair<GlobalClassesTy::iterator, unsigned> &S2) {
+ return S1.second < S2.second;
+ });
// For each disjoint set we found...
for (const auto &S : Sets) {
// Build the list of type identifiers in this disjoint set.
std::vector<Metadata *> TypeIds;
std::vector<GlobalTypeMember *> Globals;
+ std::vector<ICallBranchFunnel *> ICallBranchFunnels;
for (GlobalClassesTy::member_iterator MI =
GlobalClasses.member_begin(S.first);
MI != GlobalClasses.member_end(); ++MI) {
- if ((*MI).is<Metadata *>())
+ if (MI->is<Metadata *>())
TypeIds.push_back(MI->get<Metadata *>());
- else
+ else if (MI->is<GlobalTypeMember *>())
Globals.push_back(MI->get<GlobalTypeMember *>());
+ else
+ ICallBranchFunnels.push_back(MI->get<ICallBranchFunnel *>());
}
- // Order type identifiers by global index for determinism. This ordering is
- // stable as there is a one-to-one mapping between metadata and indices.
- std::sort(TypeIds.begin(), TypeIds.end(), [&](Metadata *M1, Metadata *M2) {
- return TypeIdInfo[M1].Index < TypeIdInfo[M2].Index;
+ // Order type identifiers by unique ID for determinism. This ordering is
+ // stable as there is a one-to-one mapping between metadata and unique IDs.
+ llvm::sort(TypeIds.begin(), TypeIds.end(), [&](Metadata *M1, Metadata *M2) {
+ return TypeIdInfo[M1].UniqueId < TypeIdInfo[M2].UniqueId;
});
+ // Same for the branch funnels.
+ llvm::sort(ICallBranchFunnels.begin(), ICallBranchFunnels.end(),
+ [&](ICallBranchFunnel *F1, ICallBranchFunnel *F2) {
+ return F1->UniqueId < F2->UniqueId;
+ });
+
// Build bitsets for this disjoint set.
- buildBitSetsFromDisjointSet(TypeIds, Globals);
+ buildBitSetsFromDisjointSet(TypeIds, Globals, ICallBranchFunnels);
}
allocateByteArrays();
+ // Parse alias data to replace stand-in function declarations for aliases
+ // with an alias to the intended target.
+ if (ExportSummary) {
+ if (NamedMDNode *AliasesMD = M.getNamedMetadata("aliases")) {
+ for (auto AliasMD : AliasesMD->operands()) {
+ assert(AliasMD->getNumOperands() >= 4);
+ StringRef AliasName =
+ cast<MDString>(AliasMD->getOperand(0))->getString();
+ StringRef Aliasee = cast<MDString>(AliasMD->getOperand(1))->getString();
+
+ if (!ExportedFunctions.count(Aliasee) ||
+ ExportedFunctions[Aliasee].Linkage != CFL_Definition ||
+ !M.getNamedAlias(Aliasee))
+ continue;
+
+ GlobalValue::VisibilityTypes Visibility =
+ static_cast<GlobalValue::VisibilityTypes>(
+ cast<ConstantAsMetadata>(AliasMD->getOperand(2))
+ ->getValue()
+ ->getUniqueInteger()
+ .getZExtValue());
+ bool Weak =
+ static_cast<bool>(cast<ConstantAsMetadata>(AliasMD->getOperand(3))
+ ->getValue()
+ ->getUniqueInteger()
+ .getZExtValue());
+
+ auto *Alias = GlobalAlias::create("", M.getNamedAlias(Aliasee));
+ Alias->setVisibility(Visibility);
+ if (Weak)
+ Alias->setLinkage(GlobalValue::WeakAnyLinkage);
+
+ if (auto *F = M.getFunction(AliasName)) {
+ Alias->takeName(F);
+ F->replaceAllUsesWith(Alias);
+ F->eraseFromParent();
+ } else {
+ Alias->setName(AliasName);
+ }
+ }
+ }
+ }
+
+ // Emit .symver directives for exported functions, if they exist.
+ if (ExportSummary) {
+ if (NamedMDNode *SymversMD = M.getNamedMetadata("symvers")) {
+ for (auto Symver : SymversMD->operands()) {
+ assert(Symver->getNumOperands() >= 2);
+ StringRef SymbolName =
+ cast<MDString>(Symver->getOperand(0))->getString();
+ StringRef Alias = cast<MDString>(Symver->getOperand(1))->getString();
+
+ if (!ExportedFunctions.count(SymbolName))
+ continue;
+
+ M.appendModuleInlineAsm(
+ (llvm::Twine(".symver ") + SymbolName + ", " + Alias).str());
+ }
+ }
+ }
+
return true;
}
PreservedAnalyses LowerTypeTestsPass::run(Module &M,
ModuleAnalysisManager &AM) {
- bool Changed = LowerTypeTestsModule(M, /*ExportSummary=*/nullptr,
- /*ImportSummary=*/nullptr)
- .lower();
+ bool Changed = LowerTypeTestsModule(M, ExportSummary, ImportSummary).lower();
if (!Changed)
return PreservedAnalyses::all();
return PreservedAnalyses::none();