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