Skip to content

Commit

Permalink
[ThinLTO] Add summary entries for index-based WPD
Browse files Browse the repository at this point in the history
Summary:
If LTOUnit splitting is disabled, the module summary analysis computes
the summary information necessary to perform single implementation
devirtualization during the thin link with the index and no IR. The
information collected from the regular LTO IR in the current hybrid WPD
algorithm is summarized, including:
1) For vtable definitions, record the function pointers and their offset
within the vtable initializer (subsumes the information collected from
IR by tryFindVirtualCallTargets).
2) A record for each type metadata summarizing the vtable definitions
decorated with that metadata (subsumes the TypeIdentiferMap collected
from IR).

Also added are the necessary bitcode records, and the corresponding
assembly support.

The follow-on index-based WPD patch is D55153.

Depends on D53890.

Reviewers: pcc

Subscribers: mehdi_amini, Prazek, inglorion, eraman, steven_wu, dexonsmith, arphaman, llvm-commits

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

llvm-svn: 364960
  • Loading branch information
teresajohnson committed Jul 2, 2019
1 parent 5fe851b commit a700436
Show file tree
Hide file tree
Showing 14 changed files with 765 additions and 25 deletions.
21 changes: 21 additions & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Expand Up @@ -263,10 +263,31 @@ enum GlobalValueSummarySymtabCodes {
// Index-wide flags
FS_FLAGS = 20,
// Maps type identifier to summary information for that type identifier.
// Produced by the thin link (only lives in combined index).
// TYPE_ID: [typeid, kind, bitwidth, align, size, bitmask, inlinebits,
// n x (typeid, kind, name, numrba,
// numrba x (numarg, numarg x arg, kind, info, byte, bit))]
FS_TYPE_ID = 21,
// For background see overview at https://llvm.org/docs/TypeMetadata.html.
// The type metadata includes both the type identifier and the offset of
// the address point of the type (the address held by objects of that type
// which may not be the beginning of the virtual table). Vtable definitions
// are decorated with type metadata for the types they are compatible with.
//
// Maps type identifier to summary information for that type identifier
// computed from type metadata: the valueid of each vtable definition
// decorated with a type metadata for that identifier, and the offset from
// the corresponding type metadata.
// Exists in the per-module summary to provide information to thin link
// for index-based whole program devirtualization.
// TYPE_ID_METADATA: [typeid, n x (valueid, offset)]
FS_TYPE_ID_METADATA = 22,
// Summarizes vtable definition for use in index-based whole program
// devirtualization during the thin link.
// PERMODULE_VTABLE_GLOBALVAR_INIT_REFS: [valueid, flags, varflags,
// numrefs, numrefs x valueid,
// n x (valueid, offset)]
FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS = 23,
};

enum MetadataCodes {
Expand Down
82 changes: 81 additions & 1 deletion llvm/include/llvm/IR/ModuleSummaryIndex.h
Expand Up @@ -698,13 +698,30 @@ template <> struct DenseMapInfo<FunctionSummary::ConstVCall> {
}
};

/// The ValueInfo and offset for a function within a vtable definition
/// initializer array.
struct VirtFuncOffset {
VirtFuncOffset(ValueInfo VI, uint64_t Offset)
: FuncVI(VI), VTableOffset(Offset) {}

ValueInfo FuncVI;
uint64_t VTableOffset;
};
/// List of functions referenced by a particular vtable definition.
using VTableFuncList = std::vector<VirtFuncOffset>;

/// Global variable summary information to aid decisions and
/// implementation of importing.
///
/// Global variable summary has extra flag, telling if it is
/// modified during the program run or not. This affects ThinLTO
/// internalization
class GlobalVarSummary : public GlobalValueSummary {
private:
/// For vtable definitions this holds the list of functions and
/// their corresponding offsets within the initializer array.
std::unique_ptr<VTableFuncList> VTableFuncs;

public:
struct GVarFlags {
GVarFlags(bool ReadOnly = false) : ReadOnly(ReadOnly) {}
Expand All @@ -725,6 +742,17 @@ class GlobalVarSummary : public GlobalValueSummary {
GVarFlags varflags() const { return VarFlags; }
void setReadOnly(bool RO) { VarFlags.ReadOnly = RO; }
bool isReadOnly() const { return VarFlags.ReadOnly; }

void setVTableFuncs(VTableFuncList Funcs) {
assert(!VTableFuncs);
VTableFuncs = llvm::make_unique<VTableFuncList>(std::move(Funcs));
}

ArrayRef<VirtFuncOffset> vTableFuncs() const {
if (VTableFuncs)
return *VTableFuncs;
return {};
}
};

struct TypeTestResolution {
Expand Down Expand Up @@ -823,6 +851,29 @@ using GVSummaryMapTy = DenseMap<GlobalValue::GUID, GlobalValueSummary *>;
using TypeIdSummaryMapTy =
std::multimap<GlobalValue::GUID, std::pair<std::string, TypeIdSummary>>;

/// The following data structures summarize type metadata information.
/// For type metadata overview see https://llvm.org/docs/TypeMetadata.html.
/// Each type metadata includes both the type identifier and the offset of
/// the address point of the type (the address held by objects of that type
/// which may not be the beginning of the virtual table). Vtable definitions
/// are decorated with type metadata for the types they are compatible with.
///
/// Holds information about vtable definitions decorated with type metadata:
/// the vtable definition value and its address point offset in a type
/// identifier metadata it is decorated (compatible) with.
struct TypeIdOffsetVtableInfo {
TypeIdOffsetVtableInfo(uint64_t Offset, ValueInfo VI)
: AddressPointOffset(Offset), VTableVI(VI) {}

uint64_t AddressPointOffset;
ValueInfo VTableVI;
};
/// List of vtable definitions decorated by a particular type identifier,
/// and their corresponding offsets in that type identifier's metadata.
/// Note that each type identifier may be compatible with multiple vtables, due
/// to inheritance, which is why this is a vector.
using TypeIdCompatibleVtableInfo = std::vector<TypeIdOffsetVtableInfo>;

/// Class to hold module path string table and global value map,
/// and encapsulate methods for operating on them.
class ModuleSummaryIndex {
Expand All @@ -835,9 +886,15 @@ class ModuleSummaryIndex {
ModulePathStringTableTy ModulePathStringTable;

/// Mapping from type identifier GUIDs to type identifier and its summary
/// information.
/// information. Produced by thin link.
TypeIdSummaryMapTy TypeIdMap;

/// Mapping from type identifier to information about vtables decorated
/// with that type identifier's metadata. Produced by per module summary
/// analysis and consumed by thin link. For more information, see description
/// above where TypeIdCompatibleVtableInfo is defined.
std::map<std::string, TypeIdCompatibleVtableInfo> TypeIdCompatibleVtableMap;

/// Mapping from original ID to GUID. If original ID can map to multiple
/// GUIDs, it will be mapped to 0.
std::map<GlobalValue::GUID, GlobalValue::GUID> OidGuidMap;
Expand Down Expand Up @@ -1201,6 +1258,29 @@ class ModuleSummaryIndex {
return nullptr;
}

const std::map<std::string, TypeIdCompatibleVtableInfo> &
typeIdCompatibleVtableMap() const {
return TypeIdCompatibleVtableMap;
}

/// Return an existing or new TypeIdCompatibleVtableMap entry for \p TypeId.
/// This accessor can mutate the map and therefore should not be used in
/// the ThinLTO backends.
TypeIdCompatibleVtableInfo &
getOrInsertTypeIdCompatibleVtableSummary(StringRef TypeId) {
return TypeIdCompatibleVtableMap[TypeId];
}

/// For the given \p TypeId, this returns the TypeIdCompatibleVtableMap
/// entry if present in the summary map. This may be used when importing.
Optional<const TypeIdCompatibleVtableInfo>
getTypeIdCompatibleVtableSummary(StringRef TypeId) const {
auto I = TypeIdCompatibleVtableMap.find(TypeId);
if (I == TypeIdCompatibleVtableMap.end())
return None;
return I->second;
}

/// Collect for the given module the list of functions it defines
/// (GUID -> Summary).
void collectDefinedFunctionsForModule(StringRef ModulePath,
Expand Down
113 changes: 110 additions & 3 deletions llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
Expand Up @@ -411,9 +411,98 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M,
Index.addGlobalValueSummary(F, std::move(FuncSummary));
}

/// Find function pointers referenced within the given vtable initializer
/// (or subset of an initializer) \p I. The starting offset of \p I within
/// the vtable initializer is \p StartingOffset. Any discovered function
/// pointers are added to \p VTableFuncs along with their cumulative offset
/// within the initializer.
static void findFuncPointers(const Constant *I, uint64_t StartingOffset,
const Module &M, ModuleSummaryIndex &Index,
VTableFuncList &VTableFuncs) {
// First check if this is a function pointer.
if (I->getType()->isPointerTy()) {
auto Fn = dyn_cast<Function>(I->stripPointerCasts());
// We can disregard __cxa_pure_virtual as a possible call target, as
// calls to pure virtuals are UB.
if (Fn && Fn->getName() != "__cxa_pure_virtual")
VTableFuncs.push_back({Index.getOrInsertValueInfo(Fn), StartingOffset});
return;
}

// Walk through the elements in the constant struct or array and recursively
// look for virtual function pointers.
const DataLayout &DL = M.getDataLayout();
if (auto *C = dyn_cast<ConstantStruct>(I)) {
StructType *STy = dyn_cast<StructType>(C->getType());
assert(STy);
const StructLayout *SL = DL.getStructLayout(C->getType());

for (StructType::element_iterator EB = STy->element_begin(), EI = EB,
EE = STy->element_end();
EI != EE; ++EI) {
auto Offset = SL->getElementOffset(EI - EB);
unsigned Op = SL->getElementContainingOffset(Offset);
findFuncPointers(cast<Constant>(I->getOperand(Op)),
StartingOffset + Offset, M, Index, VTableFuncs);
}
} else if (auto *C = dyn_cast<ConstantArray>(I)) {
ArrayType *ATy = C->getType();
Type *EltTy = ATy->getElementType();
uint64_t EltSize = DL.getTypeAllocSize(EltTy);
for (unsigned i = 0, e = ATy->getNumElements(); i != e; ++i) {
findFuncPointers(cast<Constant>(I->getOperand(i)),
StartingOffset + i * EltSize, M, Index, VTableFuncs);
}
}
}

// Identify the function pointers referenced by vtable definition \p V.
static void computeVTableFuncs(ModuleSummaryIndex &Index,
const GlobalVariable &V, const Module &M,
VTableFuncList &VTableFuncs) {
if (!V.isConstant())
return;

findFuncPointers(V.getInitializer(), /*StartingOffset=*/0, M, Index,
VTableFuncs);

#ifndef NDEBUG
// Validate that the VTableFuncs list is ordered by offset.
uint64_t PrevOffset = 0;
for (auto &P : VTableFuncs) {
// The findVFuncPointers traversal should have encountered the
// functions in offset order. We need to use ">=" since PrevOffset
// starts at 0.
assert(P.VTableOffset >= PrevOffset);
PrevOffset = P.VTableOffset;
}
#endif
}

/// Record vtable definition \p V for each type metadata it references.
static void
computeVariableSummary(ModuleSummaryIndex &Index, const GlobalVariable &V,
DenseSet<GlobalValue::GUID> &CantBePromoted) {
recordTypeIdCompatibleVtableReferences(ModuleSummaryIndex &Index,
const GlobalVariable &V,
SmallVectorImpl<MDNode *> &Types) {
for (MDNode *Type : Types) {
auto TypeID = Type->getOperand(1).get();

uint64_t Offset =
cast<ConstantInt>(
cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())
->getZExtValue();

if (auto *TypeId = dyn_cast<MDString>(TypeID))
Index.getOrInsertTypeIdCompatibleVtableSummary(TypeId->getString())
.push_back({Offset, Index.getOrInsertValueInfo(&V)});
}
}

static void computeVariableSummary(ModuleSummaryIndex &Index,
const GlobalVariable &V,
DenseSet<GlobalValue::GUID> &CantBePromoted,
const Module &M,
SmallVectorImpl<MDNode *> &Types) {
SetVector<ValueInfo> RefEdges;
SmallPtrSet<const User *, 8> Visited;
bool HasBlockAddress = findRefEdges(Index, &V, RefEdges, Visited);
Expand All @@ -422,6 +511,21 @@ computeVariableSummary(ModuleSummaryIndex &Index, const GlobalVariable &V,
/* Live = */ false, V.isDSOLocal(),
V.hasLinkOnceODRLinkage() && V.hasGlobalUnnamedAddr());

VTableFuncList VTableFuncs;
// If splitting is not enabled, then we compute the summary information
// necessary for index-based whole program devirtualization.
if (!Index.enableSplitLTOUnit()) {
Types.clear();
V.getMetadata(LLVMContext::MD_type, Types);
if (!Types.empty()) {
// Identify the function pointers referenced by this vtable definition.
computeVTableFuncs(Index, V, M, VTableFuncs);

// Record this vtable definition for each type metadata it references.
recordTypeIdCompatibleVtableReferences(Index, V, Types);
}
}

// Don't mark variables we won't be able to internalize as read-only.
GlobalVarSummary::GVarFlags VarFlags(
!V.hasComdat() && !V.hasAppendingLinkage() && !V.isInterposable() &&
Expand All @@ -432,6 +536,8 @@ computeVariableSummary(ModuleSummaryIndex &Index, const GlobalVariable &V,
CantBePromoted.insert(V.getGUID());
if (HasBlockAddress)
GVarSummary->setNotEligibleToImport();
if (!VTableFuncs.empty())
GVarSummary->setVTableFuncs(VTableFuncs);
Index.addGlobalValueSummary(V, std::move(GVarSummary));
}

Expand Down Expand Up @@ -578,10 +684,11 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex(

// Compute summaries for all variables defined in module, and save in the
// index.
SmallVector<MDNode *, 2> Types;
for (const GlobalVariable &G : M.globals()) {
if (G.isDeclaration())
continue;
computeVariableSummary(Index, G, CantBePromoted);
computeVariableSummary(Index, G, CantBePromoted, M, Types);
}

// Compute summaries for all aliases defined in module, and save in the
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Expand Up @@ -752,6 +752,8 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(critical);
KEYWORD(relbf);
KEYWORD(variable);
KEYWORD(vTableFuncs);
KEYWORD(virtFunc);
KEYWORD(aliasee);
KEYWORD(refs);
KEYWORD(typeIdInfo);
Expand All @@ -764,6 +766,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(offset);
KEYWORD(args);
KEYWORD(typeid);
KEYWORD(typeidCompatibleVTable);
KEYWORD(summary);
KEYWORD(typeTestRes);
KEYWORD(kind);
Expand Down

0 comments on commit a700436

Please sign in to comment.