@@ -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
0 commit comments