Skip to content

Commit 3fad3f6

Browse files
committed
Extend the .llvm_bb_addr_map section with PT decoding hints.
This change does a couple of things. For each basic block we add: - A table of call offsets. - Information about statically known successors. In combination, this gives us enough information for a PT decoder (specifically our ykpt decoder) to make sense of a PT packet stream without the need to do static disassembly of the instruction steam (which would be slow). Note that to get the call offsets, we have to insert symbols into the binary, and their presence would break a lot of upstream LLVM tests. For now I've gated the insertion of the symbols in with `YkAllocBBAddrMapSection`. I've noticed that our .llvm_bb_addr_map extension have not been gated. My next change should probably be an all-encompassing `YkExtendedAddrMap` gate, which encompasses all of our addr map changes.
1 parent 9ea6491 commit 3fad3f6

File tree

3 files changed

+151
-3
lines changed

3 files changed

+151
-3
lines changed

llvm/include/llvm/CodeGen/AsmPrinter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@ class AsmPrinter : public MachineFunctionPass {
233233
/// The last `.llvm_bb_addr_map` section fragment that we handled (if any).
234234
MCSection *YkLastBBAddrMapSection = nullptr;
235235

236+
/// Symbols marking the call instructions of each block. Used for the Yk JIT.
237+
std::map<const MachineBasicBlock *,
238+
SmallVector<std::tuple<MCSymbol *, MCSymbol *>>>
239+
YkCallMarkerSyms;
240+
236241
protected:
237242
explicit AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer);
238243

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,21 @@ void emitYkBBAddrMapSymbol(MCContext &MCtxt, MCStreamer &OutStreamer,
13481348
OutStreamer.emitLabel(Sym);
13491349
}
13501350

1351+
/// Returns the fallthrough block of `MBB`, or nullptr if there isn't one.
1352+
MachineBasicBlock *findFallthruBlock(const MachineBasicBlock *MBB) {
1353+
for (MachineBasicBlock *SBB : MBB->successors()) {
1354+
if (MBB->isLayoutSuccessor(SBB)) {
1355+
return SBB;
1356+
}
1357+
}
1358+
return nullptr;
1359+
}
1360+
1361+
inline const MCSymbol *BBSym(const MachineBasicBlock *MBB,
1362+
const MCSymbol *FuncSym) {
1363+
return MBB->isEntryBlock() ? FuncSym : MBB->getSymbol();
1364+
}
1365+
13511366
void AsmPrinter::emitBBAddrMapSection(const MachineFunction &MF) {
13521367
MCSection *BBAddrMapSection =
13531368
getObjFileLowering().getBBAddrMapSection(*MF.getSection());
@@ -1408,6 +1423,9 @@ void AsmPrinter::emitBBAddrMapSection(const MachineFunction &MF) {
14081423
MergedBBs.erase(BB);
14091424
}
14101425
}
1426+
1427+
const TargetInstrInfo *TI = MF.getSubtarget().getInstrInfo();
1428+
14111429
// Emit BB Information for each basic block in the funciton.
14121430
for (const MachineBasicBlock &MBB : MF) {
14131431
const MCSymbol *MBBSymbol =
@@ -1456,6 +1474,107 @@ void AsmPrinter::emitBBAddrMapSection(const MachineFunction &MF) {
14561474
OutContext.reportError(SMLoc(), "Couldn't find the block's index");
14571475
OutStreamer->emitULEB128IntValue(I);
14581476
}
1477+
1478+
// Emit call marker table for the Yk JIT.
1479+
//
1480+
// The table is a size header, followed by call instruction addresses.
1481+
//
1482+
// YKOPT: to save space, instead of using absolute symbol addresses, compute
1483+
// the distance from the start of the block and use uleb128 encoding.
1484+
const size_t NumCalls = YkCallMarkerSyms[&MBB].size();
1485+
OutStreamer->emitULEB128IntValue(NumCalls);
1486+
for (auto Tup : YkCallMarkerSyms[&MBB]) {
1487+
// Emit address of the call instruction.
1488+
OutStreamer->emitSymbolValue(std::get<0>(Tup), getPointerSize());
1489+
// Emit address of target if known, or 0.
1490+
MCSymbol *Target = std::get<1>(Tup);
1491+
if (Target)
1492+
OutStreamer->emitSymbolValue(Target, getPointerSize());
1493+
else
1494+
OutStreamer->emitIntValue(0, getPointerSize());
1495+
}
1496+
1497+
// Emit successor information.
1498+
//
1499+
// Each codegenned block gets a record indicating any statically known
1500+
// successors. The record starts with a `SuccessorKind` byte:
1501+
enum SuccessorKind {
1502+
// One successor.
1503+
Unconditional = 0,
1504+
// Choice of two successors.
1505+
Conditional = 1,
1506+
// A control flow edge known only at runtime, e.g. a return or an indirect
1507+
// branch.
1508+
Dynamic = 2,
1509+
};
1510+
1511+
// Then depending of the `SuccessorKind` there is a payload describing the
1512+
// successors:
1513+
//
1514+
// `Unconditional`: [target-address: u64]
1515+
// `Conditional`: [taken-address: u64, not-taken-address: u64]
1516+
// `Dynamic`: [] (i.e. nothing, because nothing is statically known)
1517+
1518+
MachineBasicBlock *TBB = nullptr, *FBB = nullptr;
1519+
SmallVector<MachineOperand> Conds;
1520+
1521+
if (!TI->analyzeBranch(const_cast<MachineBasicBlock &>(MBB), TBB, FBB,
1522+
Conds)) {
1523+
// The block ends with a branch or a fall-thru.
1524+
if (!TBB && !FBB) {
1525+
// Both null: block has no terminator and either falls through or
1526+
// diverges.
1527+
OutStreamer->emitInt8(Unconditional);
1528+
MachineBasicBlock *ThruBB = findFallthruBlock(&MBB);
1529+
if (ThruBB) {
1530+
OutStreamer->emitSymbolValue(BBSym(ThruBB, FunctionSymbol),
1531+
getPointerSize());
1532+
} else {
1533+
OutStreamer->emitInt64(0); // Divergent.
1534+
}
1535+
} else if (TBB && !FBB) {
1536+
// Only FBB null: block either ends with an unconditional branch or a
1537+
// conditional branch whose false arm falls through or diverges.
1538+
if (Conds.empty()) {
1539+
// No conditions: unconditional branch.
1540+
OutStreamer->emitInt8(Unconditional);
1541+
OutStreamer->emitSymbolValue(BBSym(TBB, FunctionSymbol),
1542+
getPointerSize());
1543+
} else {
1544+
// Has conditions: conditional branch followed by fallthru or
1545+
// divergence.
1546+
MachineBasicBlock *ThruBB = findFallthruBlock(&MBB);
1547+
OutStreamer->emitInt8(Conditional);
1548+
OutStreamer->emitSymbolValue(BBSym(TBB, FunctionSymbol),
1549+
getPointerSize());
1550+
if (ThruBB) {
1551+
OutStreamer->emitSymbolValue(BBSym(ThruBB, FunctionSymbol),
1552+
getPointerSize());
1553+
} else {
1554+
OutStreamer->emitInt64(0); // Divergent.
1555+
}
1556+
}
1557+
} else {
1558+
// Conditional branch followed by an unconditional branch.
1559+
assert(TBB && FBB);
1560+
OutStreamer->emitInt8(Conditional);
1561+
OutStreamer->emitSymbolValue(BBSym(TBB, FunctionSymbol),
1562+
getPointerSize());
1563+
OutStreamer->emitSymbolValue(BBSym(FBB, FunctionSymbol),
1564+
getPointerSize());
1565+
}
1566+
} else {
1567+
// Wasn't a branch or a fall-thru. Must be a different kind of terminator.
1568+
const MachineInstr *LastI = &*MBB.instr_rbegin();
1569+
if (LastI->isReturn() || LastI->isIndirectBranch()) {
1570+
OutStreamer->emitInt8(Dynamic);
1571+
} else {
1572+
std::string Msg = "unhandled block terminator in block: ";
1573+
raw_string_ostream OS(Msg);
1574+
LastI->print(OS);
1575+
OutContext.reportError(SMLoc(), Msg);
1576+
}
1577+
}
14591578
}
14601579

14611580
OutStreamer->popSection();
@@ -1577,6 +1696,9 @@ void AsmPrinter::emitFunctionBody() {
15771696
for (auto &MBB : *MF) {
15781697
// Print a label for the basic block.
15791698
emitBasicBlockStart(MBB);
1699+
1700+
YkCallMarkerSyms.insert({&MBB, {}});
1701+
15801702
DenseMap<StringRef, unsigned> MnemonicCounts;
15811703
for (auto &MI : MBB) {
15821704
// Print the assembly for the instruction.
@@ -1651,6 +1773,24 @@ void AsmPrinter::emitFunctionBody() {
16511773
OutStreamer->emitRawComment("ARITH_FENCE");
16521774
break;
16531775
default:
1776+
1777+
if (YkAllocLLVMBBAddrMapSection && MI.isCall() &&
1778+
(MI.getOpcode() != TargetOpcode::STACKMAP) &&
1779+
(MI.getOpcode() != TargetOpcode::PATCHPOINT) &&
1780+
(MI.getOpcode() != TargetOpcode::STATEPOINT)) {
1781+
MCSymbol *YkPreCallSym =
1782+
MF->getContext().createTempSymbol("yk_precall", true);
1783+
OutStreamer->emitLabel(YkPreCallSym);
1784+
const MachineOperand CallOpnd = MI.getOperand(0);
1785+
MCSymbol *CallTargetSym = nullptr;
1786+
if (CallOpnd.isGlobal()) {
1787+
// Statically known function address.
1788+
CallTargetSym = getSymbol(CallOpnd.getGlobal());
1789+
}
1790+
assert(YkCallMarkerSyms.find(&MBB) != YkCallMarkerSyms.end());
1791+
YkCallMarkerSyms[&MBB].push_back({YkPreCallSym, CallTargetSym});
1792+
}
1793+
16541794
emitInstruction(&MI);
16551795
if (CanDoExtraAnalysis) {
16561796
MCInst MCI;

llvm/test/CodeGen/X86/basic-block-sections-labels.ll

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,21 @@ declare i32 @__gxx_personality_v0(...)
5555
; CHECK-NEXT: .byte 8
5656
; CHECK-NEXT: .byte 1
5757
; CHECK-NEXT: .byte 0
58-
; CHECK-NEXT: .uleb128 .LBB0_1-.LBB_END0_0
58+
; ...calls and successor info for Yk JIT...
59+
; CHECK: .uleb128 .LBB0_1-.LBB_END0_0
5960
; CHECK-NEXT: .uleb128 .LBB_END0_1-.LBB0_1
6061
; CHECK-NEXT: .byte 8
6162
; CHECK-NEXT: .byte 1
6263
; CHECK-NEXT: .byte 1
63-
; CHECK-NEXT: .uleb128 .LBB0_2-.LBB_END0_1
64+
; ...calls and successor info for Yk JIT...
65+
; CHECK: .uleb128 .LBB0_2-.LBB_END0_1
6466
; CHECK-NEXT: .uleb128 .LBB_END0_2-.LBB0_2
6567
; CHECK-NEXT: .byte 1
6668
; CHECK-NEXT: .byte 2
6769
; CHECK-NEXT: .byte 3
6870
; CHECK-NEXT: .byte 4
69-
; CHECK-NEXT: .uleb128 .LBB0_3-.LBB_END0_2
71+
; ...calls and successor info for Yk JIT...
72+
; CHECK: .uleb128 .LBB0_3-.LBB_END0_2
7073
; CHECK-NEXT: .uleb128 .LBB_END0_3-.LBB0_3
7174
; CHECK-NEXT: .byte 5
7275
; CHECK-NEXT: .byte 2

0 commit comments

Comments
 (0)