|
10 | 10 | #define BOLT_CORE_MCINSTUTILS_H
|
11 | 11 |
|
12 | 12 | #include "bolt/Core/BinaryBasicBlock.h"
|
| 13 | +#include "bolt/Core/MCPlus.h" |
13 | 14 | #include <map>
|
14 | 15 | #include <variant>
|
15 | 16 |
|
@@ -175,6 +176,171 @@ static inline raw_ostream &operator<<(raw_ostream &OS,
|
175 | 176 | return Ref.print(OS);
|
176 | 177 | }
|
177 | 178 |
|
| 179 | +/// Instruction-matching helpers operating on a single instruction at a time. |
| 180 | +/// |
| 181 | +/// The idea is to make low-level instruction matching as readable as possible. |
| 182 | +/// The classes contained in this namespace are intended to be used as a |
| 183 | +/// domain-specific language to match MCInst with the particular opcode and |
| 184 | +/// operands. |
| 185 | +/// |
| 186 | +/// The goals of this DSL include |
| 187 | +/// * matching a single instruction against the template consisting of the |
| 188 | +/// particular target-specific opcode and a pattern of operands |
| 189 | +/// * matching operands against the known values (such as 42, AArch64::X1 or |
| 190 | +/// "the value of --brk-operand=N command line argument") |
| 191 | +/// * capturing operands of an instruction ("whatever is the destination |
| 192 | +/// register of AArch64::ADDXri instruction, store it to Xd variable to be |
| 193 | +/// queried later") |
| 194 | +/// * expressing repeated operands of a single matched instruction (such as |
| 195 | +/// "ADDXri Xd, Xd, 42, 0" for an arbitrary register Xd) as well as across |
| 196 | +/// multiple calls to matchInst(), which is naturally achieved by sequentially |
| 197 | +/// capturing the operands and matching operands against the known values |
| 198 | +/// * matching multi-instruction code patterns by sequentially calling |
| 199 | +/// matchInst() while passing around already matched operands |
| 200 | +/// |
| 201 | +/// The non-goals (compared to MCPlusBuilder::MCInstMatcher) include |
| 202 | +/// * matching an arbitrary tree of instructions in a single matchInst() call |
| 203 | +/// * encapsulation of target-specific knowledge ("match an increment of Xm |
| 204 | +/// by 42") |
| 205 | +/// |
| 206 | +/// Unlike MCPlusBuilder::MCInstMatcher, this DSL focuses on the use cases when |
| 207 | +/// the precise control over the instruction order is important. For example, |
| 208 | +/// let's consider a target-specific function that has to match two particular |
| 209 | +/// instructions against this pattern (for two different registers Xm and Xn) |
| 210 | +/// |
| 211 | +/// ADDXrs Xm, Xn, Xm, #0 |
| 212 | +/// BR Xm |
| 213 | +/// |
| 214 | +/// and return the register holding the branch target. Assuming the instructions |
| 215 | +/// are available as MaybeAdd and MaybeBr, the following code can be used: |
| 216 | +/// |
| 217 | +/// // Bring the short names into the local scope: |
| 218 | +/// using namespace LowLevelInstMatcherDSL; |
| 219 | +/// // Declare the registers to capture: |
| 220 | +/// Reg Xn, Xm; |
| 221 | +/// // Capture the 0th and 1st operands, match the 2nd operand against the |
| 222 | +/// // just captured Xm register, match the 3rd operand against literal 0: |
| 223 | +/// if (!matchInst(MaybeAdd, AArch64::ADDXrs, Xm, Xn, Xm, Imm(0)) |
| 224 | +/// return AArch64::NoRegister; |
| 225 | +/// // Match the 0th operand against Xm: |
| 226 | +/// if (!matchInst(MaybeBr, AArch64::BR, Xm)) |
| 227 | +/// return AArch64::NoRegister; |
| 228 | +/// // Manually check that Xm and Xn did not match the same register: |
| 229 | +/// if (Xm.get() == Xn.get()) |
| 230 | +/// return AArch64::NoRegister; |
| 231 | +/// // Return the matched register: |
| 232 | +/// return Xm.get(); |
| 233 | +/// |
| 234 | +namespace LowLevelInstMatcherDSL { |
| 235 | + |
| 236 | +// The base class to match an operand of type T. |
| 237 | +// |
| 238 | +// The subclasses of OpMatcher are intended to be allocated on the stack and |
| 239 | +// to only be used by passing them to matchInst() and by calling their get() |
| 240 | +// function, thus the peculiar `mutable` specifiers: to make the calling code |
| 241 | +// compact and readable, the templated matchInst() function has to accept both |
| 242 | +// long-lived Imm/Reg wrappers declared as local variables (intended to capture |
| 243 | +// the first operand's value and match the subsequent operands, whether inside |
| 244 | +// a single instruction or across multiple instructions), as well as temporary |
| 245 | +// wrappers around literal values to match, f.e. Imm(42) or Reg(AArch64::XZR). |
| 246 | +template <typename T> class OpMatcher { |
| 247 | + mutable std::optional<T> Value; |
| 248 | + mutable std::optional<T> SavedValue; |
| 249 | + |
| 250 | + // Remember/restore the last Value - to be called by matchInst. |
| 251 | + void remember() const { SavedValue = Value; } |
| 252 | + void restore() const { Value = SavedValue; } |
| 253 | + |
| 254 | + template <class... OpMatchers> |
| 255 | + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); |
| 256 | + |
| 257 | +protected: |
| 258 | + OpMatcher(std::optional<T> ValueToMatch) : Value(ValueToMatch) {} |
| 259 | + |
| 260 | + bool matchValue(T OpValue) const { |
| 261 | + // Check that OpValue does not contradict the existing Value. |
| 262 | + bool MatchResult = !Value || *Value == OpValue; |
| 263 | + // If MatchResult is false, all matchers will be reset before returning from |
| 264 | + // matchInst, including this one, thus no need to assign conditionally. |
| 265 | + Value = OpValue; |
| 266 | + |
| 267 | + return MatchResult; |
| 268 | + } |
| 269 | + |
| 270 | +public: |
| 271 | + /// Returns the captured value. |
| 272 | + T get() const { |
| 273 | + assert(Value.has_value()); |
| 274 | + return *Value; |
| 275 | + } |
| 276 | +}; |
| 277 | + |
| 278 | +class Reg : public OpMatcher<MCPhysReg> { |
| 279 | + bool matches(const MCOperand &Op) const { |
| 280 | + if (!Op.isReg()) |
| 281 | + return false; |
| 282 | + |
| 283 | + return matchValue(Op.getReg()); |
| 284 | + } |
| 285 | + |
| 286 | + template <class... OpMatchers> |
| 287 | + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); |
| 288 | + |
| 289 | +public: |
| 290 | + Reg(std::optional<MCPhysReg> RegToMatch = std::nullopt) |
| 291 | + : OpMatcher<MCPhysReg>(RegToMatch) {} |
| 292 | +}; |
| 293 | + |
| 294 | +class Imm : public OpMatcher<int64_t> { |
| 295 | + bool matches(const MCOperand &Op) const { |
| 296 | + if (!Op.isImm()) |
| 297 | + return false; |
| 298 | + |
| 299 | + return matchValue(Op.getImm()); |
| 300 | + } |
| 301 | + |
| 302 | + template <class... OpMatchers> |
| 303 | + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); |
| 304 | + |
| 305 | +public: |
| 306 | + Imm(std::optional<int64_t> ImmToMatch = std::nullopt) |
| 307 | + : OpMatcher<int64_t>(ImmToMatch) {} |
| 308 | +}; |
| 309 | + |
| 310 | +/// Tries to match Inst and updates Ops on success. |
| 311 | +/// |
| 312 | +/// If Inst has the specified Opcode and its operand list prefix matches Ops, |
| 313 | +/// this function returns true and updates Ops, otherwise false is returned and |
| 314 | +/// values of Ops are kept as before matchInst was called. |
| 315 | +/// |
| 316 | +/// Please note that while Ops are technically passed by a const reference to |
| 317 | +/// make invocations like `matchInst(MI, Opcode, Imm(42))` possible, all their |
| 318 | +/// fields are marked mutable. |
| 319 | +template <class... OpMatchers> |
| 320 | +bool matchInst(const MCInst &Inst, unsigned Opcode, const OpMatchers &...Ops) { |
| 321 | + if (Inst.getOpcode() != Opcode) |
| 322 | + return false; |
| 323 | + assert(sizeof...(Ops) <= MCPlus::getNumPrimeOperands(Inst) && |
| 324 | + "Too many operands are matched for the Opcode"); |
| 325 | + |
| 326 | + // Ask each matcher to remember its current value in case of rollback. |
| 327 | + (Ops.remember(), ...); |
| 328 | + |
| 329 | + // Check if all matchers match the corresponding operands. |
| 330 | + auto It = Inst.begin(); |
| 331 | + auto AllMatched = (Ops.matches(*(It++)) && ... && true); |
| 332 | + |
| 333 | + // If match failed, restore the original captured values. |
| 334 | + if (!AllMatched) { |
| 335 | + (Ops.restore(), ...); |
| 336 | + return false; |
| 337 | + } |
| 338 | + |
| 339 | + return true; |
| 340 | +} |
| 341 | + |
| 342 | +} // namespace LowLevelInstMatcherDSL |
| 343 | + |
178 | 344 | } // namespace bolt
|
179 | 345 | } // namespace llvm
|
180 | 346 |
|
|
0 commit comments