Skip to content

Commit b5a5ea9

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 5e906b0 commit b5a5ea9

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
@@ -149,6 +149,134 @@ static inline raw_ostream &operator<<(raw_ostream &OS,
149149
return Ref.print(OS);
150150
}
151151

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

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"
@@ -389,81 +390,58 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
389390

390391
// Iterate over the instructions of BB in reverse order, matching opcodes
391392
// and operands.
392-
MCPhysReg TestedReg = 0;
393-
MCPhysReg ScratchReg = 0;
393+
394394
auto It = BB.end();
395-
auto StepAndGetOpcode = [&It, &BB]() -> int {
396-
if (It == BB.begin())
397-
return -1;
398-
--It;
399-
return It->getOpcode();
395+
auto StepBack = [&]() {
396+
while (It != BB.begin()) {
397+
--It;
398+
if (!isCFI(*It))
399+
return true;
400+
}
401+
return false;
400402
};
401-
402-
switch (StepAndGetOpcode()) {
403-
default:
404-
// Not matched the branch instruction.
403+
// Step to the last non-CFI instruction.
404+
if (!StepBack())
405405
return std::nullopt;
406-
case AArch64::Bcc:
407-
// Bcc EQ, .Lon_success
408-
if (It->getOperand(0).getImm() != AArch64CC::EQ)
409-
return std::nullopt;
410-
// Not checking .Lon_success (see above).
411406

412-
// SUBSXrs XZR, TestedReg, ScratchReg, 0 (used by "CMP reg, reg" alias)
413-
if (StepAndGetOpcode() != AArch64::SUBSXrs ||
414-
It->getOperand(0).getReg() != AArch64::XZR ||
415-
It->getOperand(3).getImm() != 0)
407+
using namespace llvm::bolt::MCInstMatcher;
408+
Reg TestedReg;
409+
Reg ScratchReg;
410+
411+
if (matchInst(*It, AArch64::Bcc, Imm(AArch64CC::EQ) /*, .Lon_success*/)) {
412+
if (!StepBack() || !matchInst(*It, AArch64::SUBSXrs, Reg(AArch64::XZR),
413+
TestedReg, ScratchReg, Imm(0)))
416414
return std::nullopt;
417-
TestedReg = It->getOperand(1).getReg();
418-
ScratchReg = It->getOperand(2).getReg();
419415

420416
// Either XPAC(I|D) ScratchReg, ScratchReg
421417
// or XPACLRI
422-
switch (StepAndGetOpcode()) {
423-
default:
418+
if (!StepBack())
424419
return std::nullopt;
425-
case AArch64::XPACLRI:
420+
if (matchInst(*It, AArch64::XPACLRI)) {
426421
// No operands to check, but using XPACLRI forces TestedReg to be X30.
427-
if (TestedReg != AArch64::LR)
428-
return std::nullopt;
429-
break;
430-
case AArch64::XPACI:
431-
case AArch64::XPACD:
432-
if (It->getOperand(0).getReg() != ScratchReg ||
433-
It->getOperand(1).getReg() != ScratchReg)
422+
if (TestedReg.get() != AArch64::LR)
434423
return std::nullopt;
435-
break;
424+
} else if (!matchInst(*It, AArch64::XPACI, ScratchReg, ScratchReg) &&
425+
!matchInst(*It, AArch64::XPACD, ScratchReg, ScratchReg)) {
426+
return std::nullopt;
436427
}
437428

438-
// ORRXrs ScratchReg, XZR, TestedReg, 0 (used by "MOV reg, reg" alias)
439-
if (StepAndGetOpcode() != AArch64::ORRXrs)
429+
if (!StepBack() || !matchInst(*It, AArch64::ORRXrs, ScratchReg,
430+
Reg(AArch64::XZR), TestedReg, Imm(0)))
440431
return std::nullopt;
441-
if (It->getOperand(0).getReg() != ScratchReg ||
442-
It->getOperand(1).getReg() != AArch64::XZR ||
443-
It->getOperand(2).getReg() != TestedReg ||
444-
It->getOperand(3).getImm() != 0)
445-
return std::nullopt;
446-
447-
return std::make_pair(TestedReg, &*It);
448432

449-
case AArch64::TBZX:
450-
// TBZX ScratchReg, 62, .Lon_success
451-
ScratchReg = It->getOperand(0).getReg();
452-
if (It->getOperand(1).getImm() != 62)
453-
return std::nullopt;
454-
// Not checking .Lon_success (see above).
433+
return std::make_pair(TestedReg.get(), &*It);
434+
}
455435

456-
// EORXrs ScratchReg, TestedReg, TestedReg, 1
457-
if (StepAndGetOpcode() != AArch64::EORXrs)
458-
return std::nullopt;
459-
TestedReg = It->getOperand(1).getReg();
460-
if (It->getOperand(0).getReg() != ScratchReg ||
461-
It->getOperand(2).getReg() != TestedReg ||
462-
It->getOperand(3).getImm() != 1)
436+
if (matchInst(*It, AArch64::TBZX, ScratchReg, Imm(62) /*, .Lon_success*/)) {
437+
if (!StepBack() || !matchInst(*It, AArch64::EORXrs, Reg(ScratchReg),
438+
TestedReg, TestedReg, Imm(1)))
463439
return std::nullopt;
464440

465-
return std::make_pair(TestedReg, &*It);
441+
return std::make_pair(TestedReg.get(), &*It);
466442
}
443+
444+
return std::nullopt;
467445
}
468446

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

0 commit comments

Comments
 (0)