1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 04:02:41 +01:00
llvm-mirror/lib/IR/MDBuilder.cpp
Hongtao Yu 85e4f6f241 [CSSPGO] Pseudo probe encoding and emission.
This change implements pseudo probe encoding and emission for CSSPGO. Please see RFC here for more context: https://groups.google.com/g/llvm-dev/c/1p1rdYbL93s

Pseudo probes are in the form of intrinsic calls on IR/MIR but they do not turn into any machine instructions. Instead they are emitted into the binary as a piece of data in standalone sections.  The probe-specific sections are not needed to be loaded into memory at execution time, thus they do not incur a runtime overhead. 

**ELF object emission**

The binary data to emit are organized as two ELF sections, i.e, the `.pseudo_probe_desc` section and the `.pseudo_probe` section. The `.pseudo_probe_desc` section stores a function descriptor for each function and the `.pseudo_probe` section stores the actual probes, each fo which corresponds to an IR basic block or an IR function callsite. A function descriptor is stored as a module-level metadata during the compilation and is serialized into the object file during object emission.

Both the probe descriptors and pseudo probes can be emitted into a separate ELF section per function to leverage the linker for deduplication.  A `.pseudo_probe` section shares the same COMDAT group with the function code so that when the function is dead, the probes are dead and disposed too. On the contrary, a `.pseudo_probe_desc` section has its own COMDAT group. This is because even if a function is dead, its probes may be inlined into other functions and its descriptor is still needed by the profile generation tool.

The format of `.pseudo_probe_desc` section looks like:

```
.section   .pseudo_probe_desc,"",@progbits
.quad   6309742469962978389  // Func GUID
.quad   4294967295           // Func Hash
.byte   9                    // Length of func name
.ascii  "_Z5funcAi"          // Func name
.quad   7102633082150537521
.quad   138828622701
.byte   12
.ascii  "_Z8funcLeafi"
.quad   446061515086924981
.quad   4294967295
.byte   9
.ascii  "_Z5funcBi"
.quad   -2016976694713209516
.quad   72617220756
.byte   7
.ascii  "_Z3fibi"
```

For each `.pseudoprobe` section, the encoded binary data consists of a single function record corresponding to an outlined function (i.e, a function with a code entry in the `.text` section). A function record has the following format :

```
FUNCTION BODY (one for each outlined function present in the text section)
    GUID (uint64)
        GUID of the function
    NPROBES (ULEB128)
        Number of probes originating from this function.
    NUM_INLINED_FUNCTIONS (ULEB128)
        Number of callees inlined into this function, aka number of
        first-level inlinees
    PROBE RECORDS
        A list of NPROBES entries. Each entry contains:
          INDEX (ULEB128)
          TYPE (uint4)
            0 - block probe, 1 - indirect call, 2 - direct call
          ATTRIBUTE (uint3)
            reserved
          ADDRESS_TYPE (uint1)
            0 - code address, 1 - address delta
          CODE_ADDRESS (uint64 or ULEB128)
            code address or address delta, depending on ADDRESS_TYPE
    INLINED FUNCTION RECORDS
        A list of NUM_INLINED_FUNCTIONS entries describing each of the inlined
        callees.  Each record contains:
          INLINE SITE
            GUID of the inlinee (uint64)
            ID of the callsite probe (ULEB128)
          FUNCTION BODY
            A FUNCTION BODY entry describing the inlined function.
```

To support building a context-sensitive profile, probes from inlinees are grouped by their inline contexts. An inline context is logically a call path through which a callee function lands in a caller function. The probe emitter builds an inline tree based on the debug metadata for each outlined function in the form of a trie tree. A tree root is the outlined function. Each tree edge stands for a callsite where inlining happens. Pseudo probes originating from an inlinee function are stored in a tree node and the tree path starting from the root all the way down to the tree node is the inline context of the probes. The emission happens on the whole tree top-down recursively. Probes of a tree node will be emitted altogether with their direct parent edge. Since a pseudo probe corresponds to a real code address, for size savings, the address is encoded as a delta from the previous probe except for the first probe. Variant-sized integer encoding, aka LEB128, is used for address delta and probe index.

**Assembling**

Pseudo probes can be printed as assembly directives alternatively. This allows for good assembly code readability and also provides a view of how optimizations and pseudo probes affect each other, especially helpful for diff time assembly analysis.

A pseudo probe directive has the following operands in order: function GUID, probe index, probe type, probe attributes and inline context. The directive is generated by the compiler and can be parsed by the assembler to form an encoded `.pseudoprobe` section in the object file.

A example assembly looks like:

```
foo2: # @foo2
# %bb.0: # %bb0
pushq %rax
testl %edi, %edi
.pseudoprobe 837061429793323041 1 0 0
je .LBB1_1
# %bb.2: # %bb2
.pseudoprobe 837061429793323041 6 2 0
callq foo
.pseudoprobe 837061429793323041 3 0 0
.pseudoprobe 837061429793323041 4 0 0
popq %rax
retq
.LBB1_1: # %bb1
.pseudoprobe 837061429793323041 5 1 0
callq *%rsi
.pseudoprobe 837061429793323041 2 0 0
.pseudoprobe 837061429793323041 4 0 0
popq %rax
retq
# -- End function
.section .pseudo_probe_desc,"",@progbits
.quad 6699318081062747564
.quad 72617220756
.byte 3
.ascii "foo"
.quad 837061429793323041
.quad 281547593931412
.byte 4
.ascii "foo2"
```

With inlining turned on, the assembly may look different around %bb2 with an inlined probe:

```
# %bb.2:                                # %bb2
.pseudoprobe    837061429793323041 3 0
.pseudoprobe    6699318081062747564 1 0 @ 837061429793323041:6
.pseudoprobe    837061429793323041 4 0
popq    %rax
retq
```

**Disassembling**

We have a disassembling tool (llvm-profgen) that can display disassembly alongside with pseudo probes. So far it only supports ELF executable file.

An example disassembly looks like:

```
00000000002011a0 <foo2>:
  2011a0: 50                    push   rax
  2011a1: 85 ff                 test   edi,edi
  [Probe]:  FUNC: foo2  Index: 1  Type: Block
  2011a3: 74 02                 je     2011a7 <foo2+0x7>
  [Probe]:  FUNC: foo2  Index: 3  Type: Block
  [Probe]:  FUNC: foo2  Index: 4  Type: Block
  [Probe]:  FUNC: foo   Index: 1  Type: Block  Inlined: @ foo2:6
  2011a5: 58                    pop    rax
  2011a6: c3                    ret
  [Probe]:  FUNC: foo2  Index: 2  Type: Block
  2011a7: bf 01 00 00 00        mov    edi,0x1
  [Probe]:  FUNC: foo2  Index: 5  Type: IndirectCall
  2011ac: ff d6                 call   rsi
  [Probe]:  FUNC: foo2  Index: 4  Type: Block
  2011ae: 58                    pop    rax
  2011af: c3                    ret
```

Reviewed By: wmi

Differential Revision: https://reviews.llvm.org/D91878
2020-12-10 17:29:28 -08:00

318 lines
12 KiB
C++

//===---- llvm/MDBuilder.cpp - Builder for LLVM metadata ------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines the MDBuilder class, which is used as a convenient way to
// create LLVM metadata with a consistent and simplified interface.
//
//===----------------------------------------------------------------------===//
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Metadata.h"
using namespace llvm;
MDString *MDBuilder::createString(StringRef Str) {
return MDString::get(Context, Str);
}
ConstantAsMetadata *MDBuilder::createConstant(Constant *C) {
return ConstantAsMetadata::get(C);
}
MDNode *MDBuilder::createFPMath(float Accuracy) {
if (Accuracy == 0.0)
return nullptr;
assert(Accuracy > 0.0 && "Invalid fpmath accuracy!");
auto *Op =
createConstant(ConstantFP::get(Type::getFloatTy(Context), Accuracy));
return MDNode::get(Context, Op);
}
MDNode *MDBuilder::createBranchWeights(uint32_t TrueWeight,
uint32_t FalseWeight) {
return createBranchWeights({TrueWeight, FalseWeight});
}
MDNode *MDBuilder::createBranchWeights(ArrayRef<uint32_t> Weights) {
assert(Weights.size() >= 1 && "Need at least one branch weights!");
SmallVector<Metadata *, 4> Vals(Weights.size() + 1);
Vals[0] = createString("branch_weights");
Type *Int32Ty = Type::getInt32Ty(Context);
for (unsigned i = 0, e = Weights.size(); i != e; ++i)
Vals[i + 1] = createConstant(ConstantInt::get(Int32Ty, Weights[i]));
return MDNode::get(Context, Vals);
}
MDNode *MDBuilder::createUnpredictable() {
return MDNode::get(Context, None);
}
MDNode *MDBuilder::createFunctionEntryCount(
uint64_t Count, bool Synthetic,
const DenseSet<GlobalValue::GUID> *Imports) {
Type *Int64Ty = Type::getInt64Ty(Context);
SmallVector<Metadata *, 8> Ops;
if (Synthetic)
Ops.push_back(createString("synthetic_function_entry_count"));
else
Ops.push_back(createString("function_entry_count"));
Ops.push_back(createConstant(ConstantInt::get(Int64Ty, Count)));
if (Imports) {
SmallVector<GlobalValue::GUID, 2> OrderID(Imports->begin(), Imports->end());
llvm::sort(OrderID);
for (auto ID : OrderID)
Ops.push_back(createConstant(ConstantInt::get(Int64Ty, ID)));
}
return MDNode::get(Context, Ops);
}
MDNode *MDBuilder::createFunctionSectionPrefix(StringRef Prefix) {
return MDNode::get(Context,
{createString("function_section_prefix"),
createString(Prefix)});
}
MDNode *MDBuilder::createRange(const APInt &Lo, const APInt &Hi) {
assert(Lo.getBitWidth() == Hi.getBitWidth() && "Mismatched bitwidths!");
Type *Ty = IntegerType::get(Context, Lo.getBitWidth());
return createRange(ConstantInt::get(Ty, Lo), ConstantInt::get(Ty, Hi));
}
MDNode *MDBuilder::createRange(Constant *Lo, Constant *Hi) {
// If the range is everything then it is useless.
if (Hi == Lo)
return nullptr;
// Return the range [Lo, Hi).
return MDNode::get(Context, {createConstant(Lo), createConstant(Hi)});
}
MDNode *MDBuilder::createCallees(ArrayRef<Function *> Callees) {
SmallVector<Metadata *, 4> Ops;
for (Function *F : Callees)
Ops.push_back(createConstant(F));
return MDNode::get(Context, Ops);
}
MDNode *MDBuilder::createCallbackEncoding(unsigned CalleeArgNo,
ArrayRef<int> Arguments,
bool VarArgArePassed) {
SmallVector<Metadata *, 4> Ops;
Type *Int64 = Type::getInt64Ty(Context);
Ops.push_back(createConstant(ConstantInt::get(Int64, CalleeArgNo)));
for (int ArgNo : Arguments)
Ops.push_back(createConstant(ConstantInt::get(Int64, ArgNo, true)));
Type *Int1 = Type::getInt1Ty(Context);
Ops.push_back(createConstant(ConstantInt::get(Int1, VarArgArePassed)));
return MDNode::get(Context, Ops);
}
MDNode *MDBuilder::mergeCallbackEncodings(MDNode *ExistingCallbacks,
MDNode *NewCB) {
if (!ExistingCallbacks)
return MDNode::get(Context, {NewCB});
auto *NewCBCalleeIdxAsCM = cast<ConstantAsMetadata>(NewCB->getOperand(0));
uint64_t NewCBCalleeIdx =
cast<ConstantInt>(NewCBCalleeIdxAsCM->getValue())->getZExtValue();
(void)NewCBCalleeIdx;
SmallVector<Metadata *, 4> Ops;
unsigned NumExistingOps = ExistingCallbacks->getNumOperands();
Ops.resize(NumExistingOps + 1);
for (unsigned u = 0; u < NumExistingOps; u++) {
Ops[u] = ExistingCallbacks->getOperand(u);
auto *OldCBCalleeIdxAsCM = cast<ConstantAsMetadata>(Ops[u]);
uint64_t OldCBCalleeIdx =
cast<ConstantInt>(OldCBCalleeIdxAsCM->getValue())->getZExtValue();
(void)OldCBCalleeIdx;
assert(NewCBCalleeIdx != OldCBCalleeIdx &&
"Cannot map a callback callee index twice!");
}
Ops[NumExistingOps] = NewCB;
return MDNode::get(Context, Ops);
}
MDNode *MDBuilder::createAnonymousAARoot(StringRef Name, MDNode *Extra) {
SmallVector<Metadata *, 3> Args(1, nullptr);
if (Extra)
Args.push_back(Extra);
if (!Name.empty())
Args.push_back(createString(Name));
MDNode *Root = MDNode::getDistinct(Context, Args);
// At this point we have
// !0 = distinct !{null} <- root
// Replace the reserved operand with the root node itself.
Root->replaceOperandWith(0, Root);
// We now have
// !0 = distinct !{!0} <- root
return Root;
}
MDNode *MDBuilder::createTBAARoot(StringRef Name) {
return MDNode::get(Context, createString(Name));
}
/// Return metadata for a non-root TBAA node with the given name,
/// parent in the TBAA tree, and value for 'pointsToConstantMemory'.
MDNode *MDBuilder::createTBAANode(StringRef Name, MDNode *Parent,
bool isConstant) {
if (isConstant) {
Constant *Flags = ConstantInt::get(Type::getInt64Ty(Context), 1);
return MDNode::get(Context,
{createString(Name), Parent, createConstant(Flags)});
}
return MDNode::get(Context, {createString(Name), Parent});
}
MDNode *MDBuilder::createAliasScopeDomain(StringRef Name) {
return MDNode::get(Context, createString(Name));
}
MDNode *MDBuilder::createAliasScope(StringRef Name, MDNode *Domain) {
return MDNode::get(Context, {createString(Name), Domain});
}
/// Return metadata for a tbaa.struct node with the given
/// struct field descriptions.
MDNode *MDBuilder::createTBAAStructNode(ArrayRef<TBAAStructField> Fields) {
SmallVector<Metadata *, 4> Vals(Fields.size() * 3);
Type *Int64 = Type::getInt64Ty(Context);
for (unsigned i = 0, e = Fields.size(); i != e; ++i) {
Vals[i * 3 + 0] = createConstant(ConstantInt::get(Int64, Fields[i].Offset));
Vals[i * 3 + 1] = createConstant(ConstantInt::get(Int64, Fields[i].Size));
Vals[i * 3 + 2] = Fields[i].Type;
}
return MDNode::get(Context, Vals);
}
/// Return metadata for a TBAA struct node in the type DAG
/// with the given name, a list of pairs (offset, field type in the type DAG).
MDNode *MDBuilder::createTBAAStructTypeNode(
StringRef Name, ArrayRef<std::pair<MDNode *, uint64_t>> Fields) {
SmallVector<Metadata *, 4> Ops(Fields.size() * 2 + 1);
Type *Int64 = Type::getInt64Ty(Context);
Ops[0] = createString(Name);
for (unsigned i = 0, e = Fields.size(); i != e; ++i) {
Ops[i * 2 + 1] = Fields[i].first;
Ops[i * 2 + 2] = createConstant(ConstantInt::get(Int64, Fields[i].second));
}
return MDNode::get(Context, Ops);
}
/// Return metadata for a TBAA scalar type node with the
/// given name, an offset and a parent in the TBAA type DAG.
MDNode *MDBuilder::createTBAAScalarTypeNode(StringRef Name, MDNode *Parent,
uint64_t Offset) {
ConstantInt *Off = ConstantInt::get(Type::getInt64Ty(Context), Offset);
return MDNode::get(Context,
{createString(Name), Parent, createConstant(Off)});
}
/// Return metadata for a TBAA tag node with the given
/// base type, access type and offset relative to the base type.
MDNode *MDBuilder::createTBAAStructTagNode(MDNode *BaseType, MDNode *AccessType,
uint64_t Offset, bool IsConstant) {
IntegerType *Int64 = Type::getInt64Ty(Context);
ConstantInt *Off = ConstantInt::get(Int64, Offset);
if (IsConstant) {
return MDNode::get(Context, {BaseType, AccessType, createConstant(Off),
createConstant(ConstantInt::get(Int64, 1))});
}
return MDNode::get(Context, {BaseType, AccessType, createConstant(Off)});
}
MDNode *MDBuilder::createTBAATypeNode(MDNode *Parent, uint64_t Size,
Metadata *Id,
ArrayRef<TBAAStructField> Fields) {
SmallVector<Metadata *, 4> Ops(3 + Fields.size() * 3);
Type *Int64 = Type::getInt64Ty(Context);
Ops[0] = Parent;
Ops[1] = createConstant(ConstantInt::get(Int64, Size));
Ops[2] = Id;
for (unsigned I = 0, E = Fields.size(); I != E; ++I) {
Ops[I * 3 + 3] = Fields[I].Type;
Ops[I * 3 + 4] = createConstant(ConstantInt::get(Int64, Fields[I].Offset));
Ops[I * 3 + 5] = createConstant(ConstantInt::get(Int64, Fields[I].Size));
}
return MDNode::get(Context, Ops);
}
MDNode *MDBuilder::createTBAAAccessTag(MDNode *BaseType, MDNode *AccessType,
uint64_t Offset, uint64_t Size,
bool IsImmutable) {
IntegerType *Int64 = Type::getInt64Ty(Context);
auto *OffsetNode = createConstant(ConstantInt::get(Int64, Offset));
auto *SizeNode = createConstant(ConstantInt::get(Int64, Size));
if (IsImmutable) {
auto *ImmutabilityFlagNode = createConstant(ConstantInt::get(Int64, 1));
return MDNode::get(Context, {BaseType, AccessType, OffsetNode, SizeNode,
ImmutabilityFlagNode});
}
return MDNode::get(Context, {BaseType, AccessType, OffsetNode, SizeNode});
}
MDNode *MDBuilder::createMutableTBAAAccessTag(MDNode *Tag) {
MDNode *BaseType = cast<MDNode>(Tag->getOperand(0));
MDNode *AccessType = cast<MDNode>(Tag->getOperand(1));
Metadata *OffsetNode = Tag->getOperand(2);
uint64_t Offset = mdconst::extract<ConstantInt>(OffsetNode)->getZExtValue();
bool NewFormat = isa<MDNode>(AccessType->getOperand(0));
// See if the tag is already mutable.
unsigned ImmutabilityFlagOp = NewFormat ? 4 : 3;
if (Tag->getNumOperands() <= ImmutabilityFlagOp)
return Tag;
// If Tag is already mutable then return it.
Metadata *ImmutabilityFlagNode = Tag->getOperand(ImmutabilityFlagOp);
if (!mdconst::extract<ConstantInt>(ImmutabilityFlagNode)->getValue())
return Tag;
// Otherwise, create another node.
if (!NewFormat)
return createTBAAStructTagNode(BaseType, AccessType, Offset);
Metadata *SizeNode = Tag->getOperand(3);
uint64_t Size = mdconst::extract<ConstantInt>(SizeNode)->getZExtValue();
return createTBAAAccessTag(BaseType, AccessType, Offset, Size);
}
MDNode *MDBuilder::createIrrLoopHeaderWeight(uint64_t Weight) {
Metadata *Vals[] = {
createString("loop_header_weight"),
createConstant(ConstantInt::get(Type::getInt64Ty(Context), Weight)),
};
return MDNode::get(Context, Vals);
}
MDNode *MDBuilder::createPseudoProbeDesc(uint64_t GUID, uint64_t Hash,
Function *F) {
auto *Int64Ty = Type::getInt64Ty(Context);
SmallVector<Metadata *, 3> Ops(3);
Ops[0] = createConstant(ConstantInt::get(Int64Ty, GUID));
Ops[1] = createConstant(ConstantInt::get(Int64Ty, Hash));
Ops[2] = createString(F->getName());
return MDNode::get(Context, Ops);
}