Skip to content

Commit 4fd7521

Browse files
committed
[BOLT][BTI] Add MCPlusBuilder::addBTItoBBStart
This function contains most of the logic for BTI: - it takes the BasicBlock and the instruction used to jump to it. - then it checks if the first non-pseudo instruction is a sufficient landing pad for the used call. - if not, it generates the correct BTI instruction. Also introduce the isBTIVariantCoveringCall helper to simplify the logic.
1 parent 8b3ff18 commit 4fd7521

File tree

3 files changed

+193
-0
lines changed

3 files changed

+193
-0
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1890,6 +1890,19 @@ class MCPlusBuilder {
18901890
llvm_unreachable("not implemented");
18911891
}
18921892

1893+
/// Checks if the indirect call / jump is accepted by the landing pad at the
1894+
/// start of the target BasicBlock.
1895+
virtual bool isBTIVariantCoveringCall(MCInst &Call, MCInst &Pad) const {
1896+
llvm_unreachable("not implemented");
1897+
return false;
1898+
}
1899+
1900+
/// Adds a BTI landing pad to the start of the BB, that matches the indirect
1901+
/// call/jump inst.
1902+
virtual void addBTItoBBStart(BinaryBasicBlock &BB, MCInst &Call) const {
1903+
llvm_unreachable("not implemented");
1904+
}
1905+
18931906
/// Store \p Target absolute address to \p RegName
18941907
virtual InstructionListType materializeAddress(const MCSymbol *Target,
18951908
MCContext *Ctx,

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,6 +2738,81 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
27382738
Inst.addOperand(MCOperand::createImm(HintNum));
27392739
}
27402740

2741+
bool isBTIVariantCoveringCall(MCInst &Call, MCInst &Pad) const override {
2742+
assert((isIndirectCall(Call) || isIndirectBranch(Call)) &&
2743+
"Not an indirect call or branch.");
2744+
2745+
// A BLR can be accepted by a BTI c.
2746+
if (isIndirectCall(Call))
2747+
return isBTILandingPad(Pad, true, false) ||
2748+
isBTILandingPad(Pad, true, true);
2749+
2750+
// A BR can be accepted by a BTI j or BTI c (and BTI jc) IF the operand is
2751+
// x16 or x17. If the operand is not x16 or x17, it can be accepted by a BTI
2752+
// j or BTI jc (and not BTI c).
2753+
if (isIndirectBranch(Call)) {
2754+
assert(Call.getNumOperands() == 1 &&
2755+
"Indirect branch needs to have 1 operand.");
2756+
assert(Call.getOperand(0).isReg() &&
2757+
"Indirect branch does not have a register operand.");
2758+
MCPhysReg Reg = Call.getOperand(0).getReg();
2759+
if (Reg == AArch64::X16 || Reg == AArch64::X17)
2760+
return isBTILandingPad(Pad, true, false) ||
2761+
isBTILandingPad(Pad, false, true) ||
2762+
isBTILandingPad(Pad, true, true);
2763+
return isBTILandingPad(Pad, false, true) ||
2764+
isBTILandingPad(Pad, true, true);
2765+
}
2766+
return false;
2767+
}
2768+
2769+
void addBTItoBBStart(BinaryBasicBlock &BB, MCInst &Call) const override {
2770+
auto II = BB.getFirstNonPseudo();
2771+
if (II != BB.end()) {
2772+
if (isBTIVariantCoveringCall(Call, *II))
2773+
return;
2774+
// A BLR can be accepted by a BTI c.
2775+
if (isIndirectCall(Call)) {
2776+
// if we have a BTI j at the start, extend it to a BTI jc,
2777+
// otherwise insert a new BTI c.
2778+
if (isBTILandingPad(*II, false, true)) {
2779+
updateBTIVariant(*II, true, true);
2780+
} else {
2781+
MCInst BTIInst;
2782+
createBTI(BTIInst, true, false);
2783+
BB.insertInstruction(II, BTIInst);
2784+
}
2785+
}
2786+
2787+
// A BR can be accepted by a BTI j or BTI c (and BTI jc) IF the operand is
2788+
// x16 or x17. If the operand is not x16 or x17, it can be accepted by a
2789+
// BTI j or BTI jc (and not BTI c).
2790+
if (isIndirectBranch(Call)) {
2791+
assert(Call.getNumOperands() == 1 &&
2792+
"Indirect branch needs to have 1 operand.");
2793+
assert(Call.getOperand(0).isReg() &&
2794+
"Indirect branch does not have a register operand.");
2795+
MCPhysReg Reg = Call.getOperand(0).getReg();
2796+
if (Reg == AArch64::X16 || Reg == AArch64::X17) {
2797+
// Add a new BTI c
2798+
MCInst BTIInst;
2799+
createBTI(BTIInst, true, false);
2800+
BB.insertInstruction(II, BTIInst);
2801+
} else {
2802+
// If BB starts with a BTI c, extend it to BTI jc,
2803+
// otherwise insert a new BTI j.
2804+
if (isBTILandingPad(*II, true, false)) {
2805+
updateBTIVariant(*II, true, true);
2806+
} else {
2807+
MCInst BTIInst;
2808+
createBTI(BTIInst, false, true);
2809+
BB.insertInstruction(II, BTIInst);
2810+
}
2811+
}
2812+
}
2813+
}
2814+
}
2815+
27412816
InstructionListType materializeAddress(const MCSymbol *Target, MCContext *Ctx,
27422817
MCPhysReg RegName,
27432818
int64_t Addend = 0) const override {

bolt/unittests/Core/MCPlusBuilder.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,111 @@ TEST_P(MCPlusBuilderTester, AArch64_BTI) {
196196
ASSERT_TRUE(BC->MIB->isImplicitBTIC(*II));
197197
}
198198

199+
TEST_P(MCPlusBuilderTester, AArch64_addBTItoBBStart_0) {
200+
if (GetParam() != Triple::aarch64)
201+
GTEST_SKIP();
202+
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
203+
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
204+
MCInst Inst = MCInstBuilder(AArch64::RET).addReg(AArch64::LR);
205+
BB->addInstruction(Inst);
206+
// BR x16 needs BTI c or BTI j. We prefer adding a BTI c.
207+
MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X16);
208+
BC->MIB->addBTItoBBStart(*BB, CallInst);
209+
auto II = BB->begin();
210+
ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, true, false));
211+
}
212+
213+
TEST_P(MCPlusBuilderTester, AArch64_addBTItoBBStart_1) {
214+
if (GetParam() != Triple::aarch64)
215+
GTEST_SKIP();
216+
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
217+
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
218+
MCInst BTIc;
219+
BC->MIB->createBTI(BTIc, true, false);
220+
BB->addInstruction(BTIc);
221+
// BR x16 needs BTI c or BTI j. We have a BTI c, no change is needed.
222+
MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X16);
223+
BC->MIB->addBTItoBBStart(*BB, CallInst);
224+
auto II = BB->begin();
225+
ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, true, false));
226+
}
227+
228+
TEST_P(MCPlusBuilderTester, AArch64_addBTItoBBStart_2) {
229+
if (GetParam() != Triple::aarch64)
230+
GTEST_SKIP();
231+
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
232+
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
233+
MCInst BTIc;
234+
BC->MIB->createBTI(BTIc, true, false);
235+
BB->addInstruction(BTIc);
236+
// BR x5 needs BTI j
237+
// we have BTI c -> extend it to BTI jc.
238+
MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X5);
239+
BC->MIB->addBTItoBBStart(*BB, CallInst);
240+
auto II = BB->begin();
241+
ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, true, true));
242+
}
243+
244+
TEST_P(MCPlusBuilderTester, AArch64_addBTItoBBStart_3) {
245+
if (GetParam() != Triple::aarch64)
246+
GTEST_SKIP();
247+
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
248+
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
249+
MCInst Inst = MCInstBuilder(AArch64::RET).addReg(AArch64::LR);
250+
BB->addInstruction(Inst);
251+
// BR x5 needs BTI j
252+
MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X5);
253+
BC->MIB->addBTItoBBStart(*BB, CallInst);
254+
auto II = BB->begin();
255+
ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, false, true));
256+
}
257+
258+
TEST_P(MCPlusBuilderTester, AArch64_addBTItoBBStart_4) {
259+
if (GetParam() != Triple::aarch64)
260+
GTEST_SKIP();
261+
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
262+
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
263+
MCInst Inst = MCInstBuilder(AArch64::RET).addReg(AArch64::LR);
264+
BB->addInstruction(Inst);
265+
// BLR needs BTI c, regardless of the register used.
266+
MCInst CallInst = MCInstBuilder(AArch64::BLR).addReg(AArch64::X5);
267+
BC->MIB->addBTItoBBStart(*BB, CallInst);
268+
auto II = BB->begin();
269+
ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, true, false));
270+
}
271+
272+
TEST_P(MCPlusBuilderTester, AArch64_addBTItoBBStart_5) {
273+
if (GetParam() != Triple::aarch64)
274+
GTEST_SKIP();
275+
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
276+
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
277+
MCInst BTIj;
278+
BC->MIB->createBTI(BTIj, false, true);
279+
BB->addInstruction(BTIj);
280+
// BLR needs BTI c, regardless of the register used.
281+
// We have a BTI j -> extend it to BTI jc.
282+
MCInst CallInst = MCInstBuilder(AArch64::BLR).addReg(AArch64::X5);
283+
BC->MIB->addBTItoBBStart(*BB, CallInst);
284+
auto II = BB->begin();
285+
ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, true, true));
286+
}
287+
288+
TEST_P(MCPlusBuilderTester, AArch64_addBTItoBBStart_6) {
289+
if (GetParam() != Triple::aarch64)
290+
GTEST_SKIP();
291+
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
292+
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
293+
MCInst Paciasp =
294+
MCInstBuilder(AArch64::PACIASP).addReg(AArch64::LR).addReg(AArch64::SP);
295+
BB->addInstruction(Paciasp);
296+
// PACI(AB)SP are implicit BTI c, no change needed.
297+
MCInst CallInst = MCInstBuilder(AArch64::BR).addReg(AArch64::X17);
298+
BC->MIB->addBTItoBBStart(*BB, CallInst);
299+
auto II = BB->begin();
300+
ASSERT_TRUE(BC->MIB->isBTILandingPad(*II, true, false));
301+
ASSERT_TRUE(BC->MIB->isPSignOnLR(*II));
302+
}
303+
199304
TEST_P(MCPlusBuilderTester, AArch64_CmpJNE) {
200305
if (GetParam() != Triple::aarch64)
201306
GTEST_SKIP();

0 commit comments

Comments
 (0)