Skip to content

Commit 9f2ff8f

Browse files
committed
[BOLT] Introduce helpers to match MCInsts one at a time (NFC)
Introduce matchInst helper function to capture and/or match the operands of MCInst. Unlike the existing `MCPlusBuilder::MCInstMatcher` machinery, matchInst is intended for the use cases when precise control over the instruction order is required. For example, when validating PtrAuth hardening, all registers are usually considered unsafe after a function call, even though callee-saved registers should preserve their old values *under normal operation*.
1 parent 2f7252a commit 9f2ff8f

File tree

2 files changed

+162
-56
lines changed

2 files changed

+162
-56
lines changed

bolt/include/bolt/Core/MCInstUtils.h

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,134 @@ static inline raw_ostream &operator<<(raw_ostream &OS,
175175
return Ref.print(OS);
176176
}
177177

178+
/// Instruction-matching helpers operating on a single instruction at a time.
179+
///
180+
/// Unlike MCPlusBuilder::MCInstMatcher, this matchInst() function focuses on
181+
/// the cases where a precise control over the instruction order is important:
182+
///
183+
/// // Bring the short names into the local scope:
184+
/// using namespace MCInstMatcher;
185+
/// // Declare the registers to capture:
186+
/// Reg Xn, Xm;
187+
/// // Capture the 0th and 1st operands, match the 2nd operand against the
188+
/// // just captured Xm register, match the 3rd operand against literal 0:
189+
/// if (!matchInst(MaybeAdd, AArch64::ADDXrs, Xm, Xn, Xm, Imm(0))
190+
/// return AArch64::NoRegister;
191+
/// // Match the 0th operand against Xm:
192+
/// if (!matchInst(MaybeBr, AArch64::BR, Xm))
193+
/// return AArch64::NoRegister;
194+
/// // Return the matched register:
195+
/// return Xm.get();
196+
namespace MCInstMatcher {
197+
198+
// The base class to match an operand of type T.
199+
//
200+
// The subclasses of OpMatcher are intended to be allocated on the stack and
201+
// to only be used by passing them to matchInst() and by calling their get()
202+
// function, thus the peculiar `mutable` specifiers: to make the calling code
203+
// compact and readable, the templated matchInst() function has to accept both
204+
// long-lived Imm/Reg wrappers declared as local variables (intended to capture
205+
// the first operand's value and match the subsequent operands, whether inside
206+
// a single instruction or across multiple instructions), as well as temporary
207+
// wrappers around literal values to match, f.e. Imm(42) or Reg(AArch64::XZR).
208+
template <typename T> class OpMatcher {
209+
mutable std::optional<T> Value;
210+
mutable std::optional<T> SavedValue;
211+
212+
// Remember/restore the last Value - to be called by matchInst.
213+
void remember() const { SavedValue = Value; }
214+
void restore() const { Value = SavedValue; }
215+
216+
template <class... OpMatchers>
217+
friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...);
218+
219+
protected:
220+
OpMatcher(std::optional<T> ValueToMatch) : Value(ValueToMatch) {}
221+
222+
bool matchValue(T OpValue) const {
223+
// Check that OpValue does not contradict the existing Value.
224+
bool MatchResult = !Value || *Value == OpValue;
225+
// If MatchResult is false, all matchers will be reset before returning from
226+
// matchInst, including this one, thus no need to assign conditionally.
227+
Value = OpValue;
228+
229+
return MatchResult;
230+
}
231+
232+
public:
233+
/// Returns the captured value.
234+
T get() const {
235+
assert(Value.has_value());
236+
return *Value;
237+
}
238+
};
239+
240+
class Reg : public OpMatcher<MCPhysReg> {
241+
bool matches(const MCOperand &Op) const {
242+
if (!Op.isReg())
243+
return false;
244+
245+
return matchValue(Op.getReg());
246+
}
247+
248+
template <class... OpMatchers>
249+
friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...);
250+
251+
public:
252+
Reg(std::optional<MCPhysReg> RegToMatch = std::nullopt)
253+
: OpMatcher<MCPhysReg>(RegToMatch) {}
254+
};
255+
256+
class Imm : public OpMatcher<int64_t> {
257+
bool matches(const MCOperand &Op) const {
258+
if (!Op.isImm())
259+
return false;
260+
261+
return matchValue(Op.getImm());
262+
}
263+
264+
template <class... OpMatchers>
265+
friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...);
266+
267+
public:
268+
Imm(std::optional<int64_t> ImmToMatch = std::nullopt)
269+
: OpMatcher<int64_t>(ImmToMatch) {}
270+
};
271+
272+
/// Tries to match Inst and updates Ops on success.
273+
///
274+
/// If Inst has the specified Opcode and its operand list prefix matches Ops,
275+
/// this function returns true and updates Ops, otherwise false is returned and
276+
/// values of Ops are kept as before matchInst was called.
277+
///
278+
/// Please note that while Ops are technically passed by a const reference to
279+
/// make invocations like `matchInst(MI, Opcode, Imm(42))` possible, all their
280+
/// fields are marked mutable.
281+
template <class... OpMatchers>
282+
bool matchInst(const MCInst &Inst, unsigned Opcode, const OpMatchers &...Ops) {
283+
if (Inst.getOpcode() != Opcode)
284+
return false;
285+
assert(sizeof...(Ops) <= Inst.getNumOperands() &&
286+
"Too many operands are matched for the Opcode");
287+
288+
// Ask each matcher to remember its current value in case of rollback.
289+
(Ops.remember(), ...);
290+
291+
// Check if all matchers match the corresponding operands.
292+
auto It = Inst.begin();
293+
auto AllMatched = (Ops.matches(*(It++)) && ... && true);
294+
295+
// If match failed, restore the original captured values.
296+
if (!AllMatched) {
297+
(Ops.restore(), ...);
298+
return false;
299+
}
300+
301+
return true;
302+
}
303+
304+
} // namespace MCInstMatcher
305+
178306
} // namespace bolt
179307
} // namespace llvm
180308

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

Lines changed: 34 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "Utils/AArch64BaseInfo.h"
2020
#include "bolt/Core/BinaryBasicBlock.h"
2121
#include "bolt/Core/BinaryFunction.h"
22+
#include "bolt/Core/MCInstUtils.h"
2223
#include "bolt/Core/MCPlusBuilder.h"
2324
#include "llvm/BinaryFormat/ELF.h"
2425
#include "llvm/MC/MCContext.h"
@@ -401,81 +402,58 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
401402

402403
// Iterate over the instructions of BB in reverse order, matching opcodes
403404
// and operands.
404-
MCPhysReg TestedReg = 0;
405-
MCPhysReg ScratchReg = 0;
405+
406406
auto It = BB.end();
407-
auto StepAndGetOpcode = [&It, &BB]() -> int {
408-
if (It == BB.begin())
409-
return -1;
410-
--It;
411-
return It->getOpcode();
407+
auto StepBack = [&]() {
408+
while (It != BB.begin()) {
409+
--It;
410+
if (!isCFI(*It))
411+
return true;
412+
}
413+
return false;
412414
};
413-
414-
switch (StepAndGetOpcode()) {
415-
default:
416-
// Not matched the branch instruction.
415+
// Step to the last non-CFI instruction.
416+
if (!StepBack())
417417
return std::nullopt;
418-
case AArch64::Bcc:
419-
// Bcc EQ, .Lon_success
420-
if (It->getOperand(0).getImm() != AArch64CC::EQ)
421-
return std::nullopt;
422-
// Not checking .Lon_success (see above).
423418

424-
// SUBSXrs XZR, TestedReg, ScratchReg, 0 (used by "CMP reg, reg" alias)
425-
if (StepAndGetOpcode() != AArch64::SUBSXrs ||
426-
It->getOperand(0).getReg() != AArch64::XZR ||
427-
It->getOperand(3).getImm() != 0)
419+
using namespace llvm::bolt::MCInstMatcher;
420+
Reg TestedReg;
421+
Reg ScratchReg;
422+
423+
if (matchInst(*It, AArch64::Bcc, Imm(AArch64CC::EQ) /*, .Lon_success*/)) {
424+
if (!StepBack() || !matchInst(*It, AArch64::SUBSXrs, Reg(AArch64::XZR),
425+
TestedReg, ScratchReg, Imm(0)))
428426
return std::nullopt;
429-
TestedReg = It->getOperand(1).getReg();
430-
ScratchReg = It->getOperand(2).getReg();
431427

432428
// Either XPAC(I|D) ScratchReg, ScratchReg
433429
// or XPACLRI
434-
switch (StepAndGetOpcode()) {
435-
default:
430+
if (!StepBack())
436431
return std::nullopt;
437-
case AArch64::XPACLRI:
432+
if (matchInst(*It, AArch64::XPACLRI)) {
438433
// No operands to check, but using XPACLRI forces TestedReg to be X30.
439-
if (TestedReg != AArch64::LR)
440-
return std::nullopt;
441-
break;
442-
case AArch64::XPACI:
443-
case AArch64::XPACD:
444-
if (It->getOperand(0).getReg() != ScratchReg ||
445-
It->getOperand(1).getReg() != ScratchReg)
434+
if (TestedReg.get() != AArch64::LR)
446435
return std::nullopt;
447-
break;
436+
} else if (!matchInst(*It, AArch64::XPACI, ScratchReg, ScratchReg) &&
437+
!matchInst(*It, AArch64::XPACD, ScratchReg, ScratchReg)) {
438+
return std::nullopt;
448439
}
449440

450-
// ORRXrs ScratchReg, XZR, TestedReg, 0 (used by "MOV reg, reg" alias)
451-
if (StepAndGetOpcode() != AArch64::ORRXrs)
441+
if (!StepBack() || !matchInst(*It, AArch64::ORRXrs, ScratchReg,
442+
Reg(AArch64::XZR), TestedReg, Imm(0)))
452443
return std::nullopt;
453-
if (It->getOperand(0).getReg() != ScratchReg ||
454-
It->getOperand(1).getReg() != AArch64::XZR ||
455-
It->getOperand(2).getReg() != TestedReg ||
456-
It->getOperand(3).getImm() != 0)
457-
return std::nullopt;
458-
459-
return std::make_pair(TestedReg, &*It);
460444

461-
case AArch64::TBZX:
462-
// TBZX ScratchReg, 62, .Lon_success
463-
ScratchReg = It->getOperand(0).getReg();
464-
if (It->getOperand(1).getImm() != 62)
465-
return std::nullopt;
466-
// Not checking .Lon_success (see above).
445+
return std::make_pair(TestedReg.get(), &*It);
446+
}
467447

468-
// EORXrs ScratchReg, TestedReg, TestedReg, 1
469-
if (StepAndGetOpcode() != AArch64::EORXrs)
470-
return std::nullopt;
471-
TestedReg = It->getOperand(1).getReg();
472-
if (It->getOperand(0).getReg() != ScratchReg ||
473-
It->getOperand(2).getReg() != TestedReg ||
474-
It->getOperand(3).getImm() != 1)
448+
if (matchInst(*It, AArch64::TBZX, ScratchReg, Imm(62) /*, .Lon_success*/)) {
449+
if (!StepBack() || !matchInst(*It, AArch64::EORXrs, Reg(ScratchReg),
450+
TestedReg, TestedReg, Imm(1)))
475451
return std::nullopt;
476452

477-
return std::make_pair(TestedReg, &*It);
453+
return std::make_pair(TestedReg.get(), &*It);
478454
}
455+
456+
return std::nullopt;
479457
}
480458

481459
std::optional<MCPhysReg> getAuthCheckedReg(const MCInst &Inst,

0 commit comments

Comments
 (0)