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