Skip to content

Commit 8acd2f8

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 92a1e07 commit 8acd2f8

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
@@ -166,6 +166,134 @@ static inline raw_ostream &operator<<(raw_ostream &OS,
166166
return Ref.print(OS);
167167
}
168168

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

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"
@@ -393,81 +394,58 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
393394

394395
// Iterate over the instructions of BB in reverse order, matching opcodes
395396
// and operands.
396-
MCPhysReg TestedReg = 0;
397-
MCPhysReg ScratchReg = 0;
397+
398398
auto It = BB.end();
399-
auto StepAndGetOpcode = [&It, &BB]() -> int {
400-
if (It == BB.begin())
401-
return -1;
402-
--It;
403-
return It->getOpcode();
399+
auto StepBack = [&]() {
400+
while (It != BB.begin()) {
401+
--It;
402+
if (!isCFI(*It))
403+
return true;
404+
}
405+
return false;
404406
};
405-
406-
switch (StepAndGetOpcode()) {
407-
default:
408-
// Not matched the branch instruction.
407+
// Step to the last non-CFI instruction.
408+
if (!StepBack())
409409
return std::nullopt;
410-
case AArch64::Bcc:
411-
// Bcc EQ, .Lon_success
412-
if (It->getOperand(0).getImm() != AArch64CC::EQ)
413-
return std::nullopt;
414-
// Not checking .Lon_success (see above).
415410

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

424420
// Either XPAC(I|D) ScratchReg, ScratchReg
425421
// or XPACLRI
426-
switch (StepAndGetOpcode()) {
427-
default:
422+
if (!StepBack())
428423
return std::nullopt;
429-
case AArch64::XPACLRI:
424+
if (matchInst(*It, AArch64::XPACLRI)) {
430425
// No operands to check, but using XPACLRI forces TestedReg to be X30.
431-
if (TestedReg != AArch64::LR)
432-
return std::nullopt;
433-
break;
434-
case AArch64::XPACI:
435-
case AArch64::XPACD:
436-
if (It->getOperand(0).getReg() != ScratchReg ||
437-
It->getOperand(1).getReg() != ScratchReg)
426+
if (TestedReg.get() != AArch64::LR)
438427
return std::nullopt;
439-
break;
428+
} else if (!matchInst(*It, AArch64::XPACI, ScratchReg, ScratchReg) &&
429+
!matchInst(*It, AArch64::XPACD, ScratchReg, ScratchReg)) {
430+
return std::nullopt;
440431
}
441432

442-
// ORRXrs ScratchReg, XZR, TestedReg, 0 (used by "MOV reg, reg" alias)
443-
if (StepAndGetOpcode() != AArch64::ORRXrs)
433+
if (!StepBack() || !matchInst(*It, AArch64::ORRXrs, ScratchReg,
434+
Reg(AArch64::XZR), TestedReg, Imm(0)))
444435
return std::nullopt;
445-
if (It->getOperand(0).getReg() != ScratchReg ||
446-
It->getOperand(1).getReg() != AArch64::XZR ||
447-
It->getOperand(2).getReg() != TestedReg ||
448-
It->getOperand(3).getImm() != 0)
449-
return std::nullopt;
450-
451-
return std::make_pair(TestedReg, &*It);
452436

453-
case AArch64::TBZX:
454-
// TBZX ScratchReg, 62, .Lon_success
455-
ScratchReg = It->getOperand(0).getReg();
456-
if (It->getOperand(1).getImm() != 62)
457-
return std::nullopt;
458-
// Not checking .Lon_success (see above).
437+
return std::make_pair(TestedReg.get(), &*It);
438+
}
459439

460-
// EORXrs ScratchReg, TestedReg, TestedReg, 1
461-
if (StepAndGetOpcode() != AArch64::EORXrs)
462-
return std::nullopt;
463-
TestedReg = It->getOperand(1).getReg();
464-
if (It->getOperand(0).getReg() != ScratchReg ||
465-
It->getOperand(2).getReg() != TestedReg ||
466-
It->getOperand(3).getImm() != 1)
440+
if (matchInst(*It, AArch64::TBZX, ScratchReg, Imm(62) /*, .Lon_success*/)) {
441+
if (!StepBack() || !matchInst(*It, AArch64::EORXrs, Reg(ScratchReg),
442+
TestedReg, TestedReg, Imm(1)))
467443
return std::nullopt;
468444

469-
return std::make_pair(TestedReg, &*It);
445+
return std::make_pair(TestedReg.get(), &*It);
470446
}
447+
448+
return std::nullopt;
471449
}
472450

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

0 commit comments

Comments
 (0)