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