Skip to content

Commit 6b1cf00

Browse files
authored
[BOLT] Add support for Linux kernel static keys jump table (#86090)
Runtime code modification used by static keys is the most ubiquitous self-modifying feature of the Linux kernel. The idea is to to eliminate the condition check and associated conditional jump on a hot path if that condition (based on a boolean value of a static key) does not change often. Whenever they condition changes, the kernel runtime modifies all code paths associated with that key flipping the code between nop and (unconditional) jump.
1 parent 2ab106c commit 6b1cf00

File tree

9 files changed

+547
-3
lines changed

9 files changed

+547
-3
lines changed

bolt/include/bolt/Core/MCPlus.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class MCAnnotation {
7373
kOffset, /// Offset in the function.
7474
kLabel, /// MCSymbol pointing to this instruction.
7575
kSize, /// Size of the instruction.
76+
kDynamicBranch, /// Jit instruction patched at runtime.
7677
kGeneric /// First generic annotation.
7778
};
7879

bolt/include/bolt/Core/MCPlusBuilder.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,16 @@ class MCPlusBuilder {
11991199
/// Set instruction size.
12001200
void setSize(MCInst &Inst, uint32_t Size) const;
12011201

1202+
/// Check if the branch instruction could be modified at runtime.
1203+
bool isDynamicBranch(const MCInst &Inst) const;
1204+
1205+
/// Return ID for runtime-modifiable instruction.
1206+
std::optional<uint32_t> getDynamicBranchID(const MCInst &Inst) const;
1207+
1208+
/// Mark instruction as a dynamic branch, i.e. a branch that can be
1209+
/// overwritten at runtime.
1210+
void setDynamicBranch(MCInst &Inst, uint32_t ID) const;
1211+
12021212
/// Return MCSymbol that represents a target of this instruction at a given
12031213
/// operand number \p OpNum. If there's no symbol associated with
12041214
/// the operand - return nullptr.
@@ -1688,6 +1698,13 @@ class MCPlusBuilder {
16881698
llvm_unreachable("not implemented");
16891699
}
16901700

1701+
/// Create long conditional branch with a target-specific conditional code
1702+
/// \p CC.
1703+
virtual void createLongCondBranch(MCInst &Inst, const MCSymbol *Target,
1704+
unsigned CC, MCContext *Ctx) const {
1705+
llvm_unreachable("not implemented");
1706+
}
1707+
16911708
/// Reverses the branch condition in Inst and update its taken target to TBB.
16921709
///
16931710
/// Returns true on success.

bolt/lib/Core/BinaryContext.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1939,7 +1939,13 @@ void BinaryContext::printInstruction(raw_ostream &OS, const MCInst &Instruction,
19391939
OS << Endl;
19401940
return;
19411941
}
1942-
InstPrinter->printInst(&Instruction, 0, "", *STI, OS);
1942+
if (std::optional<uint32_t> DynamicID =
1943+
MIB->getDynamicBranchID(Instruction)) {
1944+
OS << "\tjit\t" << MIB->getTargetSymbol(Instruction)->getName()
1945+
<< " # ID: " << DynamicID;
1946+
} else {
1947+
InstPrinter->printInst(&Instruction, 0, "", *STI, OS);
1948+
}
19431949
if (MIB->isCall(Instruction)) {
19441950
if (MIB->isTailCall(Instruction))
19451951
OS << " # TAILCALL ";

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3350,6 +3350,16 @@ void BinaryFunction::fixBranches() {
33503350

33513351
// Eliminate unnecessary conditional branch.
33523352
if (TSuccessor == FSuccessor) {
3353+
// FIXME: at the moment, we cannot safely remove static key branches.
3354+
if (MIB->isDynamicBranch(*CondBranch)) {
3355+
if (opts::Verbosity) {
3356+
BC.outs()
3357+
<< "BOLT-INFO: unable to remove redundant dynamic branch in "
3358+
<< *this << '\n';
3359+
}
3360+
continue;
3361+
}
3362+
33533363
BB->removeDuplicateConditionalSuccessor(CondBranch);
33543364
if (TSuccessor != NextBB)
33553365
BB->addBranchInstruction(TSuccessor);
@@ -3358,8 +3368,13 @@ void BinaryFunction::fixBranches() {
33583368

33593369
// Reverse branch condition and swap successors.
33603370
auto swapSuccessors = [&]() {
3361-
if (MIB->isUnsupportedBranch(*CondBranch))
3371+
if (MIB->isUnsupportedBranch(*CondBranch)) {
3372+
if (opts::Verbosity) {
3373+
BC.outs() << "BOLT-INFO: unable to swap successors in " << *this
3374+
<< '\n';
3375+
}
33623376
return false;
3377+
}
33633378
std::swap(TSuccessor, FSuccessor);
33643379
BB->swapConditionalSuccessors();
33653380
auto L = BC.scopeLock();

bolt/lib/Core/MCPlusBuilder.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,28 @@ void MCPlusBuilder::setSize(MCInst &Inst, uint32_t Size) const {
303303
setAnnotationOpValue(Inst, MCAnnotation::kSize, Size);
304304
}
305305

306+
bool MCPlusBuilder::isDynamicBranch(const MCInst &Inst) const {
307+
if (!hasAnnotation(Inst, MCAnnotation::kDynamicBranch))
308+
return false;
309+
assert(isBranch(Inst) && "Branch expected.");
310+
return true;
311+
}
312+
313+
std::optional<uint32_t>
314+
MCPlusBuilder::getDynamicBranchID(const MCInst &Inst) const {
315+
if (std::optional<int64_t> Value =
316+
getAnnotationOpValue(Inst, MCAnnotation::kDynamicBranch)) {
317+
assert(isBranch(Inst) && "Branch expected.");
318+
return static_cast<uint32_t>(*Value);
319+
}
320+
return std::nullopt;
321+
}
322+
323+
void MCPlusBuilder::setDynamicBranch(MCInst &Inst, uint32_t ID) const {
324+
assert(isBranch(Inst) && "Branch expected.");
325+
setAnnotationOpValue(Inst, MCAnnotation::kDynamicBranch, ID);
326+
}
327+
306328
bool MCPlusBuilder::hasAnnotation(const MCInst &Inst, unsigned Index) const {
307329
return (bool)getAnnotationOpValue(Inst, Index);
308330
}

bolt/lib/Passes/BinaryPasses.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ static cl::opt<unsigned>
107107
cl::desc("print statistics about basic block ordering"),
108108
cl::init(0), cl::cat(BoltOptCategory));
109109

110+
static cl::opt<bool> PrintLargeFunctions(
111+
"print-large-functions",
112+
cl::desc("print functions that could not be overwritten due to excessive "
113+
"size"),
114+
cl::init(false), cl::cat(BoltOptCategory));
115+
110116
static cl::list<bolt::DynoStats::Category>
111117
PrintSortedBy("print-sorted-by", cl::CommaSeparated,
112118
cl::desc("print functions sorted by order of dyno stats"),
@@ -570,8 +576,12 @@ Error CheckLargeFunctions::runOnFunctions(BinaryContext &BC) {
570576
uint64_t HotSize, ColdSize;
571577
std::tie(HotSize, ColdSize) =
572578
BC.calculateEmittedSize(BF, /*FixBranches=*/false);
573-
if (HotSize > BF.getMaxSize())
579+
if (HotSize > BF.getMaxSize()) {
580+
if (opts::PrintLargeFunctions)
581+
BC.outs() << "BOLT-INFO: " << BF << " size exceeds allocated space by "
582+
<< (HotSize - BF.getMaxSize()) << " bytes\n";
574583
BF.setSimple(false);
584+
}
575585
};
576586

577587
ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) {
@@ -852,6 +862,10 @@ uint64_t SimplifyConditionalTailCalls::fixTailCalls(BinaryFunction &BF) {
852862
assert(Result && "internal error analyzing conditional branch");
853863
assert(CondBranch && "conditional branch expected");
854864

865+
// Skip dynamic branches for now.
866+
if (BF.getBinaryContext().MIB->isDynamicBranch(*CondBranch))
867+
continue;
868+
855869
// It's possible that PredBB is also a successor to BB that may have
856870
// been processed by a previous iteration of the SCTC loop, in which
857871
// case it may have been marked invalid. We should skip rewriting in
@@ -1012,6 +1026,10 @@ uint64_t ShortenInstructions::shortenInstructions(BinaryFunction &Function) {
10121026
const BinaryContext &BC = Function.getBinaryContext();
10131027
for (BinaryBasicBlock &BB : Function) {
10141028
for (MCInst &Inst : BB) {
1029+
// Skip shortening instructions with Size annotation.
1030+
if (BC.MIB->getSize(Inst))
1031+
continue;
1032+
10151033
MCInst OriginalInst;
10161034
if (opts::Verbosity > 2)
10171035
OriginalInst = Inst;

0 commit comments

Comments
 (0)