Skip to content

Commit

Permalink
IR: Function summary extensions for whole-program devirtualization pass.
Browse files Browse the repository at this point in the history
The summary information includes all uses of llvm.type.test and
llvm.type.checked.load intrinsics that can be used to devirtualize calls,
including any constant arguments for virtual constant propagation.

Differential Revision: https://reviews.llvm.org/D29734

llvm-svn: 294795
  • Loading branch information
pcc committed Feb 10, 2017
1 parent 03ab8a3 commit be9ffaa
Show file tree
Hide file tree
Showing 8 changed files with 426 additions and 35 deletions.
22 changes: 21 additions & 1 deletion llvm/include/llvm/Bitcode/LLVMBitCodes.h
Expand Up @@ -213,8 +213,28 @@ enum GlobalValueSummarySymtabCodes {
FS_COMBINED_ORIGINAL_NAME = 9,
// VERSION of the summary, bumped when adding flags for instance.
FS_VERSION = 10,
// The list of llvm.type.test type identifiers used by the following function.
// The list of llvm.type.test type identifiers used by the following function
// that are used other than by an llvm.assume.
// [n x typeid]
FS_TYPE_TESTS = 11,
// The list of virtual calls made by this function using
// llvm.assume(llvm.type.test) intrinsics that do not have all constant
// integer arguments.
// [n x (typeid, offset)]
FS_TYPE_TEST_ASSUME_VCALLS = 12,
// The list of virtual calls made by this function using
// llvm.type.checked.load intrinsics that do not have all constant integer
// arguments.
// [n x (typeid, offset)]
FS_TYPE_CHECKED_LOAD_VCALLS = 13,
// Identifies a virtual call made by this function using an
// llvm.assume(llvm.type.test) intrinsic with all constant integer arguments.
// [typeid, offset, n x arg]
FS_TYPE_TEST_ASSUME_CONST_VCALL = 14,
// Identifies a virtual call made by this function using an
// llvm.type.checked.load intrinsic with all constant integer arguments.
// [typeid, offset, n x arg]
FS_TYPE_CHECKED_LOAD_CONST_VCALL = 15,
};

enum MetadataCodes {
Expand Down
106 changes: 100 additions & 6 deletions llvm/include/llvm/IR/ModuleSummaryIndex.h
Expand Up @@ -249,6 +249,22 @@ class FunctionSummary : public GlobalValueSummary {
/// <CalleeValueInfo, CalleeInfo> call edge pair.
typedef std::pair<ValueInfo, CalleeInfo> EdgeTy;

/// An "identifier" for a virtual function. This contains the type identifier
/// represented as a GUID and the offset from the address point to the virtual
/// function pointer.
struct VFuncId {
GlobalValue::GUID GUID;
uint64_t Offset;
};

/// A specification for a virtual function call with all constant integer
/// arguments. This is used to perform virtual constant propagation on the
/// summary.
struct ConstVCall {
VFuncId VFunc;
std::vector<uint64_t> Args;
};

private:
/// Number of instructions (ignoring debug instructions, e.g.) computed
/// during the initial compile step when the summary index is first built.
Expand All @@ -257,17 +273,36 @@ class FunctionSummary : public GlobalValueSummary {
/// List of <CalleeValueInfo, CalleeInfo> call edge pairs from this function.
std::vector<EdgeTy> CallGraphEdgeList;

/// List of type identifiers used by this function, represented as GUIDs.
std::vector<GlobalValue::GUID> TypeIdList;
/// List of type identifiers used by this function in llvm.type.test
/// intrinsics other than by an llvm.assume intrinsic, represented as GUIDs.
std::vector<GlobalValue::GUID> TypeTests;

/// List of virtual calls made by this function using (respectively)
/// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics that do
/// not have all constant integer arguments.
std::vector<VFuncId> TypeTestAssumeVCalls, TypeCheckedLoadVCalls;

/// List of virtual calls made by this function using (respectively)
/// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics with
/// all constant integer arguments.
std::vector<ConstVCall> TypeTestAssumeConstVCalls, TypeCheckedLoadConstVCalls;

public:
/// Summary constructors.
FunctionSummary(GVFlags Flags, unsigned NumInsts, std::vector<ValueInfo> Refs,
std::vector<EdgeTy> CGEdges,
std::vector<GlobalValue::GUID> TypeIds)
std::vector<GlobalValue::GUID> TypeTests,
std::vector<VFuncId> TypeTestAssumeVCalls,
std::vector<VFuncId> TypeCheckedLoadVCalls,
std::vector<ConstVCall> TypeTestAssumeConstVCalls,
std::vector<ConstVCall> TypeCheckedLoadConstVCalls)
: GlobalValueSummary(FunctionKind, Flags, std::move(Refs)),
InstCount(NumInsts), CallGraphEdgeList(std::move(CGEdges)),
TypeIdList(std::move(TypeIds)) {}
TypeTests(std::move(TypeTests)),
TypeTestAssumeVCalls(std::move(TypeTestAssumeVCalls)),
TypeCheckedLoadVCalls(std::move(TypeCheckedLoadVCalls)),
TypeTestAssumeConstVCalls(std::move(TypeTestAssumeConstVCalls)),
TypeCheckedLoadConstVCalls(std::move(TypeCheckedLoadConstVCalls)) {}

/// Check if this is a function summary.
static bool classof(const GlobalValueSummary *GVS) {
Expand All @@ -280,8 +315,67 @@ class FunctionSummary : public GlobalValueSummary {
/// Return the list of <CalleeValueInfo, CalleeInfo> pairs.
ArrayRef<EdgeTy> calls() const { return CallGraphEdgeList; }

/// Returns the list of type identifiers used by this function.
ArrayRef<GlobalValue::GUID> type_tests() const { return TypeIdList; }
/// Returns the list of type identifiers used by this function in
/// llvm.type.test intrinsics other than by an llvm.assume intrinsic,
/// represented as GUIDs.
ArrayRef<GlobalValue::GUID> type_tests() const { return TypeTests; }

/// Returns the list of virtual calls made by this function using
/// llvm.assume(llvm.type.test) intrinsics that do not have all constant
/// integer arguments.
ArrayRef<VFuncId> type_test_assume_vcalls() const {
return TypeTestAssumeVCalls;
}

/// Returns the list of virtual calls made by this function using
/// llvm.type.checked.load intrinsics that do not have all constant integer
/// arguments.
ArrayRef<VFuncId> type_checked_load_vcalls() const {
return TypeCheckedLoadVCalls;
}

/// Returns the list of virtual calls made by this function using
/// llvm.assume(llvm.type.test) intrinsics with all constant integer
/// arguments.
ArrayRef<ConstVCall> type_test_assume_const_vcalls() const {
return TypeTestAssumeConstVCalls;
}

/// Returns the list of virtual calls made by this function using
/// llvm.type.checked.load intrinsics with all constant integer arguments.
ArrayRef<ConstVCall> type_checked_load_const_vcalls() const {
return TypeCheckedLoadConstVCalls;
}
};

template <> struct DenseMapInfo<FunctionSummary::VFuncId> {
static inline FunctionSummary::VFuncId getEmptyKey() {
return {0, uint64_t(-1)};
}
static inline FunctionSummary::VFuncId getTombstoneKey() {
return {0, uint64_t(-2)};
}
static bool isEqual(FunctionSummary::VFuncId L, FunctionSummary::VFuncId R) {
return L.GUID == R.GUID && L.Offset == R.Offset;
}
static unsigned getHashValue(FunctionSummary::VFuncId I) { return I.GUID; }
};

template <> struct DenseMapInfo<FunctionSummary::ConstVCall> {
static inline FunctionSummary::ConstVCall getEmptyKey() {
return {{0, uint64_t(-1)}, {}};
}
static inline FunctionSummary::ConstVCall getTombstoneKey() {
return {{0, uint64_t(-2)}, {}};
}
static bool isEqual(FunctionSummary::ConstVCall L,
FunctionSummary::ConstVCall R) {
return DenseMapInfo<FunctionSummary::VFuncId>::isEqual(L.VFunc, R.VFunc) &&
L.Args == R.Args;
}
static unsigned getHashValue(FunctionSummary::ConstVCall I) {
return I.VFunc.GUID;
}
};

/// \brief Global variable summary information to aid decisions and
Expand Down
6 changes: 5 additions & 1 deletion llvm/include/llvm/IR/ModuleSummaryIndexYAML.h
Expand Up @@ -82,7 +82,11 @@ template <> struct CustomMappingTraits<GlobalValueSummaryMapTy> {
false);
Elem.push_back(llvm::make_unique<FunctionSummary>(
GVFlags, 0, ArrayRef<ValueInfo>{},
ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests)));
ArrayRef<FunctionSummary::EdgeTy>{}, std::move(FSum.TypeTests),
ArrayRef<FunctionSummary::VFuncId>{},
ArrayRef<FunctionSummary::VFuncId>{},
ArrayRef<FunctionSummary::ConstVCall>{},
ArrayRef<FunctionSummary::ConstVCall>{}));
}
}
static void output(IO &io, GlobalValueSummaryMapTy &V) {
Expand Down
125 changes: 104 additions & 21 deletions llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
Expand Up @@ -84,6 +84,92 @@ static bool isNonRenamableLocal(const GlobalValue &GV) {
return GV.hasSection() && GV.hasLocalLinkage();
}

/// Determine whether this call has all constant integer arguments (excluding
/// "this") and summarize it to VCalls or ConstVCalls as appropriate.
static void addVCallToSet(DevirtCallSite Call, GlobalValue::GUID Guid,
SetVector<FunctionSummary::VFuncId> &VCalls,
SetVector<FunctionSummary::ConstVCall> &ConstVCalls) {
std::vector<uint64_t> Args;
// Start from the second argument to skip the "this" pointer.
for (auto &Arg : make_range(Call.CS.arg_begin() + 1, Call.CS.arg_end())) {
auto *CI = dyn_cast<ConstantInt>(Arg);
if (!CI || CI->getBitWidth() > 64) {
VCalls.insert({Guid, Call.Offset});
return;
}
Args.push_back(CI->getZExtValue());
}
ConstVCalls.insert({{Guid, Call.Offset}, std::move(Args)});
}

/// If this intrinsic call requires that we add information to the function
/// summary, do so via the non-constant reference arguments.
static void addIntrinsicToSummary(
const CallInst *CI, SetVector<GlobalValue::GUID> &TypeTests,
SetVector<FunctionSummary::VFuncId> &TypeTestAssumeVCalls,
SetVector<FunctionSummary::VFuncId> &TypeCheckedLoadVCalls,
SetVector<FunctionSummary::ConstVCall> &TypeTestAssumeConstVCalls,
SetVector<FunctionSummary::ConstVCall> &TypeCheckedLoadConstVCalls) {
switch (CI->getCalledFunction()->getIntrinsicID()) {
case Intrinsic::type_test: {
auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(1));
auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata());
if (!TypeId)
break;
GlobalValue::GUID Guid = GlobalValue::getGUID(TypeId->getString());

// Produce a summary from type.test intrinsics. We only summarize type.test
// intrinsics that are used other than by an llvm.assume intrinsic.
// Intrinsics that are assumed are relevant only to the devirtualization
// pass, not the type test lowering pass.
bool HasNonAssumeUses = llvm::any_of(CI->uses(), [](const Use &CIU) {
auto *AssumeCI = dyn_cast<CallInst>(CIU.getUser());
if (!AssumeCI)
return true;
Function *F = AssumeCI->getCalledFunction();
return !F || F->getIntrinsicID() != Intrinsic::assume;
});
if (HasNonAssumeUses)
TypeTests.insert(Guid);

SmallVector<DevirtCallSite, 4> DevirtCalls;
SmallVector<CallInst *, 4> Assumes;
findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI);
for (auto &Call : DevirtCalls)
addVCallToSet(Call, Guid, TypeTestAssumeVCalls,
TypeTestAssumeConstVCalls);

break;
}

case Intrinsic::type_checked_load: {
auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(2));
auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata());
if (!TypeId)
break;
GlobalValue::GUID Guid = GlobalValue::getGUID(TypeId->getString());

SmallVector<DevirtCallSite, 4> DevirtCalls;
SmallVector<Instruction *, 4> LoadedPtrs;
SmallVector<Instruction *, 4> Preds;
bool HasNonCallUses = false;
findDevirtualizableCallsForTypeCheckedLoad(DevirtCalls, LoadedPtrs, Preds,
HasNonCallUses, CI);
// Any non-call uses of the result of llvm.type.checked.load will
// prevent us from optimizing away the llvm.type.test.
if (HasNonCallUses)
TypeTests.insert(Guid);
for (auto &Call : DevirtCalls)
addVCallToSet(Call, Guid, TypeCheckedLoadVCalls,
TypeCheckedLoadConstVCalls);

break;
}
default:
break;
}
}

static void
computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
const Function &F, BlockFrequencyInfo *BFI,
Expand All @@ -99,6 +185,10 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
MapVector<ValueInfo, CalleeInfo> CallGraphEdges;
SetVector<ValueInfo> RefEdges;
SetVector<GlobalValue::GUID> TypeTests;
SetVector<FunctionSummary::VFuncId> TypeTestAssumeVCalls,
TypeCheckedLoadVCalls;
SetVector<FunctionSummary::ConstVCall> TypeTestAssumeConstVCalls,
TypeCheckedLoadConstVCalls;
ICallPromotionAnalysis ICallAnalysis;

bool HasInlineAsmMaybeReferencingInternal = false;
Expand Down Expand Up @@ -133,25 +223,11 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
// Check if this is a direct call to a known function or a known
// intrinsic, or an indirect call with profile data.
if (CalledFunction) {
if (CalledFunction->isIntrinsic()) {
if (CalledFunction->getIntrinsicID() != Intrinsic::type_test)
continue;
// Produce a summary from type.test intrinsics. We only summarize
// type.test intrinsics that are used other than by an llvm.assume
// intrinsic. Intrinsics that are assumed are relevant only to the
// devirtualization pass, not the type test lowering pass.
bool HasNonAssumeUses = llvm::any_of(CI->uses(), [](const Use &CIU) {
auto *AssumeCI = dyn_cast<CallInst>(CIU.getUser());
if (!AssumeCI)
return true;
Function *F = AssumeCI->getCalledFunction();
return !F || F->getIntrinsicID() != Intrinsic::assume;
});
if (HasNonAssumeUses) {
auto *TypeMDVal = cast<MetadataAsValue>(CI->getArgOperand(1));
if (auto *TypeId = dyn_cast<MDString>(TypeMDVal->getMetadata()))
TypeTests.insert(GlobalValue::getGUID(TypeId->getString()));
}
if (CI && CalledFunction->isIntrinsic()) {
addIntrinsicToSummary(
CI, TypeTests, TypeTestAssumeVCalls, TypeCheckedLoadVCalls,
TypeTestAssumeConstVCalls, TypeCheckedLoadConstVCalls);
continue;
}
// We should have named any anonymous globals
assert(CalledFunction->hasName());
Expand Down Expand Up @@ -193,7 +269,10 @@ computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
/* LiveRoot = */ false);
auto FuncSummary = llvm::make_unique<FunctionSummary>(
Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector(),
TypeTests.takeVector());
TypeTests.takeVector(), TypeTestAssumeVCalls.takeVector(),
TypeCheckedLoadVCalls.takeVector(),
TypeTestAssumeConstVCalls.takeVector(),
TypeCheckedLoadConstVCalls.takeVector());
if (NonRenamableLocal)
CantBePromoted.insert(F.getGUID());
Index.addGlobalValueSummary(F.getName(), std::move(FuncSummary));
Expand Down Expand Up @@ -347,7 +426,11 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(
llvm::make_unique<FunctionSummary>(
GVFlags, 0, ArrayRef<ValueInfo>{},
ArrayRef<FunctionSummary::EdgeTy>{},
ArrayRef<GlobalValue::GUID>{});
ArrayRef<GlobalValue::GUID>{},
ArrayRef<FunctionSummary::VFuncId>{},
ArrayRef<FunctionSummary::VFuncId>{},
ArrayRef<FunctionSummary::ConstVCall>{},
ArrayRef<FunctionSummary::ConstVCall>{});
Index.addGlobalValueSummary(Name, std::move(Summary));
} else {
std::unique_ptr<GlobalVarSummary> Summary =
Expand Down

0 comments on commit be9ffaa

Please sign in to comment.