xref: /freebsd/contrib/llvm-project/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //==------ llvm/CodeGen/GlobalISel/MIPatternMatch.h -------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 /// \file
9 /// Contains matchers for matching SSA Machine Instructions.
10 ///
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_CODEGEN_GLOBALISEL_MIPATTERNMATCH_H
14 #define LLVM_CODEGEN_GLOBALISEL_MIPATTERNMATCH_H
15 
16 #include "llvm/ADT/APInt.h"
17 #include "llvm/ADT/FloatingPointMode.h"
18 #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
19 #include "llvm/CodeGen/GlobalISel/Utils.h"
20 #include "llvm/CodeGen/MachineRegisterInfo.h"
21 #include "llvm/CodeGen/TargetOpcodes.h"
22 #include "llvm/IR/InstrTypes.h"
23 
24 namespace llvm {
25 namespace MIPatternMatch {
26 
27 template <typename Reg, typename Pattern>
mi_match(Reg R,const MachineRegisterInfo & MRI,Pattern && P)28 [[nodiscard]] bool mi_match(Reg R, const MachineRegisterInfo &MRI,
29                             Pattern &&P) {
30   return P.match(MRI, R);
31 }
32 
33 template <typename Pattern>
mi_match(MachineInstr & MI,const MachineRegisterInfo & MRI,Pattern && P)34 [[nodiscard]] bool mi_match(MachineInstr &MI, const MachineRegisterInfo &MRI,
35                             Pattern &&P) {
36   return P.match(MRI, &MI);
37 }
38 
39 template <typename Pattern>
mi_match(const MachineInstr & MI,const MachineRegisterInfo & MRI,Pattern && P)40 [[nodiscard]] bool mi_match(const MachineInstr &MI,
41                             const MachineRegisterInfo &MRI, Pattern &&P) {
42   return P.match(MRI, &MI);
43 }
44 
45 // TODO: Extend for N use.
46 template <typename SubPatternT> struct OneUse_match {
47   SubPatternT SubPat;
OneUse_matchOneUse_match48   OneUse_match(const SubPatternT &SP) : SubPat(SP) {}
49 
matchOneUse_match50   bool match(const MachineRegisterInfo &MRI, Register Reg) {
51     return MRI.hasOneUse(Reg) && SubPat.match(MRI, Reg);
52   }
53 };
54 
55 template <typename SubPat>
m_OneUse(const SubPat & SP)56 inline OneUse_match<SubPat> m_OneUse(const SubPat &SP) {
57   return SP;
58 }
59 
60 template <typename SubPatternT> struct OneNonDBGUse_match {
61   SubPatternT SubPat;
OneNonDBGUse_matchOneNonDBGUse_match62   OneNonDBGUse_match(const SubPatternT &SP) : SubPat(SP) {}
63 
matchOneNonDBGUse_match64   bool match(const MachineRegisterInfo &MRI, Register Reg) {
65     return MRI.hasOneNonDBGUse(Reg) && SubPat.match(MRI, Reg);
66   }
67 };
68 
69 template <typename SubPat>
m_OneNonDBGUse(const SubPat & SP)70 inline OneNonDBGUse_match<SubPat> m_OneNonDBGUse(const SubPat &SP) {
71   return SP;
72 }
73 
74 template <typename ConstT>
75 inline std::optional<ConstT> matchConstant(Register,
76                                            const MachineRegisterInfo &);
77 
78 template <>
matchConstant(Register Reg,const MachineRegisterInfo & MRI)79 inline std::optional<APInt> matchConstant(Register Reg,
80                                           const MachineRegisterInfo &MRI) {
81   return getIConstantVRegVal(Reg, MRI);
82 }
83 
84 template <>
matchConstant(Register Reg,const MachineRegisterInfo & MRI)85 inline std::optional<int64_t> matchConstant(Register Reg,
86                                             const MachineRegisterInfo &MRI) {
87   return getIConstantVRegSExtVal(Reg, MRI);
88 }
89 
90 template <typename ConstT> struct ConstantMatch {
91   ConstT &CR;
ConstantMatchConstantMatch92   ConstantMatch(ConstT &C) : CR(C) {}
matchConstantMatch93   bool match(const MachineRegisterInfo &MRI, Register Reg) {
94     if (auto MaybeCst = matchConstant<ConstT>(Reg, MRI)) {
95       CR = *MaybeCst;
96       return true;
97     }
98     return false;
99   }
100 };
101 
m_ICst(APInt & Cst)102 inline ConstantMatch<APInt> m_ICst(APInt &Cst) {
103   return ConstantMatch<APInt>(Cst);
104 }
m_ICst(int64_t & Cst)105 inline ConstantMatch<int64_t> m_ICst(int64_t &Cst) {
106   return ConstantMatch<int64_t>(Cst);
107 }
108 
109 template <typename ConstT>
110 inline std::optional<ConstT> matchConstantSplat(Register,
111                                                 const MachineRegisterInfo &);
112 
113 template <>
matchConstantSplat(Register Reg,const MachineRegisterInfo & MRI)114 inline std::optional<APInt> matchConstantSplat(Register Reg,
115                                                const MachineRegisterInfo &MRI) {
116   return getIConstantSplatVal(Reg, MRI);
117 }
118 
119 template <>
120 inline std::optional<int64_t>
matchConstantSplat(Register Reg,const MachineRegisterInfo & MRI)121 matchConstantSplat(Register Reg, const MachineRegisterInfo &MRI) {
122   return getIConstantSplatSExtVal(Reg, MRI);
123 }
124 
125 template <typename ConstT> struct ICstOrSplatMatch {
126   ConstT &CR;
ICstOrSplatMatchICstOrSplatMatch127   ICstOrSplatMatch(ConstT &C) : CR(C) {}
matchICstOrSplatMatch128   bool match(const MachineRegisterInfo &MRI, Register Reg) {
129     if (auto MaybeCst = matchConstant<ConstT>(Reg, MRI)) {
130       CR = *MaybeCst;
131       return true;
132     }
133 
134     if (auto MaybeCstSplat = matchConstantSplat<ConstT>(Reg, MRI)) {
135       CR = *MaybeCstSplat;
136       return true;
137     }
138 
139     return false;
140   };
141 };
142 
m_ICstOrSplat(APInt & Cst)143 inline ICstOrSplatMatch<APInt> m_ICstOrSplat(APInt &Cst) {
144   return ICstOrSplatMatch<APInt>(Cst);
145 }
146 
m_ICstOrSplat(int64_t & Cst)147 inline ICstOrSplatMatch<int64_t> m_ICstOrSplat(int64_t &Cst) {
148   return ICstOrSplatMatch<int64_t>(Cst);
149 }
150 
151 struct GCstAndRegMatch {
152   std::optional<ValueAndVReg> &ValReg;
GCstAndRegMatchGCstAndRegMatch153   GCstAndRegMatch(std::optional<ValueAndVReg> &ValReg) : ValReg(ValReg) {}
matchGCstAndRegMatch154   bool match(const MachineRegisterInfo &MRI, Register Reg) {
155     ValReg = getIConstantVRegValWithLookThrough(Reg, MRI);
156     return ValReg ? true : false;
157   }
158 };
159 
m_GCst(std::optional<ValueAndVReg> & ValReg)160 inline GCstAndRegMatch m_GCst(std::optional<ValueAndVReg> &ValReg) {
161   return GCstAndRegMatch(ValReg);
162 }
163 
164 struct GFCstAndRegMatch {
165   std::optional<FPValueAndVReg> &FPValReg;
GFCstAndRegMatchGFCstAndRegMatch166   GFCstAndRegMatch(std::optional<FPValueAndVReg> &FPValReg)
167       : FPValReg(FPValReg) {}
matchGFCstAndRegMatch168   bool match(const MachineRegisterInfo &MRI, Register Reg) {
169     FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI);
170     return FPValReg ? true : false;
171   }
172 };
173 
m_GFCst(std::optional<FPValueAndVReg> & FPValReg)174 inline GFCstAndRegMatch m_GFCst(std::optional<FPValueAndVReg> &FPValReg) {
175   return GFCstAndRegMatch(FPValReg);
176 }
177 
178 struct GFCstOrSplatGFCstMatch {
179   std::optional<FPValueAndVReg> &FPValReg;
GFCstOrSplatGFCstMatchGFCstOrSplatGFCstMatch180   GFCstOrSplatGFCstMatch(std::optional<FPValueAndVReg> &FPValReg)
181       : FPValReg(FPValReg) {}
matchGFCstOrSplatGFCstMatch182   bool match(const MachineRegisterInfo &MRI, Register Reg) {
183     return (FPValReg = getFConstantSplat(Reg, MRI)) ||
184            (FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI));
185   };
186 };
187 
188 inline GFCstOrSplatGFCstMatch
m_GFCstOrSplat(std::optional<FPValueAndVReg> & FPValReg)189 m_GFCstOrSplat(std::optional<FPValueAndVReg> &FPValReg) {
190   return GFCstOrSplatGFCstMatch(FPValReg);
191 }
192 
193 /// Matcher for a specific constant value.
194 struct SpecificConstantMatch {
195   int64_t RequestedVal;
SpecificConstantMatchSpecificConstantMatch196   SpecificConstantMatch(int64_t RequestedVal) : RequestedVal(RequestedVal) {}
matchSpecificConstantMatch197   bool match(const MachineRegisterInfo &MRI, Register Reg) {
198     int64_t MatchedVal;
199     return mi_match(Reg, MRI, m_ICst(MatchedVal)) && MatchedVal == RequestedVal;
200   }
201 };
202 
203 /// Matches a constant equal to \p RequestedValue.
m_SpecificICst(int64_t RequestedValue)204 inline SpecificConstantMatch m_SpecificICst(int64_t RequestedValue) {
205   return SpecificConstantMatch(RequestedValue);
206 }
207 
208 /// Matcher for a specific constant splat.
209 struct SpecificConstantSplatMatch {
210   int64_t RequestedVal;
SpecificConstantSplatMatchSpecificConstantSplatMatch211   SpecificConstantSplatMatch(int64_t RequestedVal)
212       : RequestedVal(RequestedVal) {}
matchSpecificConstantSplatMatch213   bool match(const MachineRegisterInfo &MRI, Register Reg) {
214     return isBuildVectorConstantSplat(Reg, MRI, RequestedVal,
215                                       /* AllowUndef */ false);
216   }
217 };
218 
219 /// Matches a constant splat of \p RequestedValue.
m_SpecificICstSplat(int64_t RequestedValue)220 inline SpecificConstantSplatMatch m_SpecificICstSplat(int64_t RequestedValue) {
221   return SpecificConstantSplatMatch(RequestedValue);
222 }
223 
224 /// Matcher for a specific constant or constant splat.
225 struct SpecificConstantOrSplatMatch {
226   int64_t RequestedVal;
SpecificConstantOrSplatMatchSpecificConstantOrSplatMatch227   SpecificConstantOrSplatMatch(int64_t RequestedVal)
228       : RequestedVal(RequestedVal) {}
matchSpecificConstantOrSplatMatch229   bool match(const MachineRegisterInfo &MRI, Register Reg) {
230     int64_t MatchedVal;
231     if (mi_match(Reg, MRI, m_ICst(MatchedVal)) && MatchedVal == RequestedVal)
232       return true;
233     return isBuildVectorConstantSplat(Reg, MRI, RequestedVal,
234                                       /* AllowUndef */ false);
235   }
236 };
237 
238 /// Matches a \p RequestedValue constant or a constant splat of \p
239 /// RequestedValue.
240 inline SpecificConstantOrSplatMatch
m_SpecificICstOrSplat(int64_t RequestedValue)241 m_SpecificICstOrSplat(int64_t RequestedValue) {
242   return SpecificConstantOrSplatMatch(RequestedValue);
243 }
244 
245 ///{
246 /// Convenience matchers for specific integer values.
m_ZeroInt()247 inline SpecificConstantMatch m_ZeroInt() { return SpecificConstantMatch(0); }
m_AllOnesInt()248 inline SpecificConstantMatch m_AllOnesInt() {
249   return SpecificConstantMatch(-1);
250 }
251 ///}
252 
253 /// Matcher for a specific register.
254 struct SpecificRegisterMatch {
255   Register RequestedReg;
SpecificRegisterMatchSpecificRegisterMatch256   SpecificRegisterMatch(Register RequestedReg) : RequestedReg(RequestedReg) {}
matchSpecificRegisterMatch257   bool match(const MachineRegisterInfo &MRI, Register Reg) {
258     return Reg == RequestedReg;
259   }
260 };
261 
262 /// Matches a register only if it is equal to \p RequestedReg.
m_SpecificReg(Register RequestedReg)263 inline SpecificRegisterMatch m_SpecificReg(Register RequestedReg) {
264   return SpecificRegisterMatch(RequestedReg);
265 }
266 
267 // TODO: Rework this for different kinds of MachineOperand.
268 // Currently assumes the Src for a match is a register.
269 // We might want to support taking in some MachineOperands and call getReg on
270 // that.
271 
272 struct operand_type_match {
matchoperand_type_match273   bool match(const MachineRegisterInfo &MRI, Register Reg) { return true; }
matchoperand_type_match274   bool match(const MachineRegisterInfo &MRI, MachineOperand *MO) {
275     return MO->isReg();
276   }
277 };
278 
m_Reg()279 inline operand_type_match m_Reg() { return operand_type_match(); }
280 
281 /// Matching combinators.
282 template <typename... Preds> struct And {
283   template <typename MatchSrc>
matchAnd284   bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
285     return true;
286   }
287 };
288 
289 template <typename Pred, typename... Preds>
290 struct And<Pred, Preds...> : And<Preds...> {
291   Pred P;
292   And(Pred &&p, Preds &&... preds)
293       : And<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {
294   }
295   template <typename MatchSrc>
296   bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
297     return P.match(MRI, src) && And<Preds...>::match(MRI, src);
298   }
299 };
300 
301 template <typename... Preds> struct Or {
302   template <typename MatchSrc>
303   bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
304     return false;
305   }
306 };
307 
308 template <typename Pred, typename... Preds>
309 struct Or<Pred, Preds...> : Or<Preds...> {
310   Pred P;
311   Or(Pred &&p, Preds &&... preds)
312       : Or<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {}
313   template <typename MatchSrc>
314   bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
315     return P.match(MRI, src) || Or<Preds...>::match(MRI, src);
316   }
317 };
318 
319 template <typename... Preds> And<Preds...> m_all_of(Preds &&... preds) {
320   return And<Preds...>(std::forward<Preds>(preds)...);
321 }
322 
323 template <typename... Preds> Or<Preds...> m_any_of(Preds &&... preds) {
324   return Or<Preds...>(std::forward<Preds>(preds)...);
325 }
326 
327 template <typename BindTy> struct bind_helper {
328   static bool bind(const MachineRegisterInfo &MRI, BindTy &VR, BindTy &V) {
329     VR = V;
330     return true;
331   }
332 };
333 
334 template <> struct bind_helper<MachineInstr *> {
335   static bool bind(const MachineRegisterInfo &MRI, MachineInstr *&MI,
336                    Register Reg) {
337     MI = MRI.getVRegDef(Reg);
338     if (MI)
339       return true;
340     return false;
341   }
342   static bool bind(const MachineRegisterInfo &MRI, MachineInstr *&MI,
343                    MachineInstr *Inst) {
344     MI = Inst;
345     return MI;
346   }
347 };
348 
349 template <> struct bind_helper<const MachineInstr *> {
350   static bool bind(const MachineRegisterInfo &MRI, const MachineInstr *&MI,
351                    Register Reg) {
352     MI = MRI.getVRegDef(Reg);
353     return MI;
354   }
355   static bool bind(const MachineRegisterInfo &MRI, const MachineInstr *&MI,
356                    const MachineInstr *Inst) {
357     MI = Inst;
358     return MI;
359   }
360 };
361 
362 template <> struct bind_helper<LLT> {
363   static bool bind(const MachineRegisterInfo &MRI, LLT &Ty, Register Reg) {
364     Ty = MRI.getType(Reg);
365     if (Ty.isValid())
366       return true;
367     return false;
368   }
369 };
370 
371 template <> struct bind_helper<const ConstantFP *> {
372   static bool bind(const MachineRegisterInfo &MRI, const ConstantFP *&F,
373                    Register Reg) {
374     F = getConstantFPVRegVal(Reg, MRI);
375     if (F)
376       return true;
377     return false;
378   }
379 };
380 
381 template <typename Class> struct bind_ty {
382   Class &VR;
383 
384   bind_ty(Class &V) : VR(V) {}
385 
386   template <typename ITy> bool match(const MachineRegisterInfo &MRI, ITy &&V) {
387     return bind_helper<Class>::bind(MRI, VR, V);
388   }
389 };
390 
391 inline bind_ty<Register> m_Reg(Register &R) { return R; }
392 inline bind_ty<MachineInstr *> m_MInstr(MachineInstr *&MI) { return MI; }
393 inline bind_ty<const MachineInstr *> m_MInstr(const MachineInstr *&MI) {
394   return MI;
395 }
396 inline bind_ty<LLT> m_Type(LLT &Ty) { return Ty; }
397 inline bind_ty<CmpInst::Predicate> m_Pred(CmpInst::Predicate &P) { return P; }
398 inline operand_type_match m_Pred() { return operand_type_match(); }
399 inline bind_ty<FPClassTest> m_FPClassTest(FPClassTest &T) { return T; }
400 
401 template <typename BindTy> struct deferred_helper {
402   static bool match(const MachineRegisterInfo &MRI, BindTy &VR, BindTy &V) {
403     return VR == V;
404   }
405 };
406 
407 template <> struct deferred_helper<LLT> {
408   static bool match(const MachineRegisterInfo &MRI, LLT VT, Register R) {
409     return VT == MRI.getType(R);
410   }
411 };
412 
413 template <typename Class> struct deferred_ty {
414   Class &VR;
415 
416   deferred_ty(Class &V) : VR(V) {}
417 
418   template <typename ITy> bool match(const MachineRegisterInfo &MRI, ITy &&V) {
419     return deferred_helper<Class>::match(MRI, VR, V);
420   }
421 };
422 
423 /// Similar to m_SpecificReg/Type, but the specific value to match originated
424 /// from an earlier sub-pattern in the same mi_match expression. For example,
425 /// we cannot match `(add X, X)` with `m_GAdd(m_Reg(X), m_SpecificReg(X))`
426 /// because `X` is not initialized at the time it's passed to `m_SpecificReg`.
427 /// Instead, we can use `m_GAdd(m_Reg(x), m_DeferredReg(X))`.
428 inline deferred_ty<Register> m_DeferredReg(Register &R) { return R; }
429 inline deferred_ty<LLT> m_DeferredType(LLT &Ty) { return Ty; }
430 
431 struct ImplicitDefMatch {
432   bool match(const MachineRegisterInfo &MRI, Register Reg) {
433     MachineInstr *TmpMI;
434     if (mi_match(Reg, MRI, m_MInstr(TmpMI)))
435       return TmpMI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF;
436     return false;
437   }
438 };
439 
440 inline ImplicitDefMatch m_GImplicitDef() { return ImplicitDefMatch(); }
441 
442 // Helper for matching G_FCONSTANT
443 inline bind_ty<const ConstantFP *> m_GFCst(const ConstantFP *&C) { return C; }
444 
445 // General helper for all the binary generic MI such as G_ADD/G_SUB etc
446 template <typename LHS_P, typename RHS_P, unsigned Opcode,
447           bool Commutable = false, unsigned Flags = MachineInstr::NoFlags>
448 struct BinaryOp_match {
449   LHS_P L;
450   RHS_P R;
451 
452   BinaryOp_match(const LHS_P &LHS, const RHS_P &RHS) : L(LHS), R(RHS) {}
453   template <typename OpTy>
454   bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
455     const MachineInstr *TmpMI;
456     if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
457       if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 3) {
458         if ((!L.match(MRI, TmpMI->getOperand(1).getReg()) ||
459              !R.match(MRI, TmpMI->getOperand(2).getReg())) &&
460             // NOTE: When trying the alternative operand ordering
461             // with a commutative operation, it is imperative to always run
462             // the LHS sub-pattern  (i.e. `L`) before the RHS sub-pattern
463             // (i.e. `R`). Otherwise, m_DeferredReg/Type will not work as
464             // expected.
465             (!Commutable || !L.match(MRI, TmpMI->getOperand(2).getReg()) ||
466              !R.match(MRI, TmpMI->getOperand(1).getReg())))
467           return false;
468         return (TmpMI->getFlags() & Flags) == Flags;
469       }
470     }
471     return false;
472   }
473 };
474 
475 // Helper for (commutative) binary generic MI that checks Opcode.
476 template <typename LHS_P, typename RHS_P, bool Commutable = false>
477 struct BinaryOpc_match {
478   unsigned Opc;
479   LHS_P L;
480   RHS_P R;
481 
482   BinaryOpc_match(unsigned Opcode, const LHS_P &LHS, const RHS_P &RHS)
483       : Opc(Opcode), L(LHS), R(RHS) {}
484   template <typename OpTy>
485   bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
486     MachineInstr *TmpMI;
487     if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
488       if (TmpMI->getOpcode() == Opc && TmpMI->getNumDefs() == 1 &&
489           TmpMI->getNumOperands() == 3) {
490         return (L.match(MRI, TmpMI->getOperand(1).getReg()) &&
491                 R.match(MRI, TmpMI->getOperand(2).getReg())) ||
492                // NOTE: When trying the alternative operand ordering
493                // with a commutative operation, it is imperative to always run
494                // the LHS sub-pattern  (i.e. `L`) before the RHS sub-pattern
495                // (i.e. `R`). Otherwise, m_DeferredReg/Type will not work as
496                // expected.
497                (Commutable && (L.match(MRI, TmpMI->getOperand(2).getReg()) &&
498                                R.match(MRI, TmpMI->getOperand(1).getReg())));
499       }
500     }
501     return false;
502   }
503 };
504 
505 template <typename LHS, typename RHS>
506 inline BinaryOpc_match<LHS, RHS, false> m_BinOp(unsigned Opcode, const LHS &L,
507                                                 const RHS &R) {
508   return BinaryOpc_match<LHS, RHS, false>(Opcode, L, R);
509 }
510 
511 template <typename LHS, typename RHS>
512 inline BinaryOpc_match<LHS, RHS, true>
513 m_CommutativeBinOp(unsigned Opcode, const LHS &L, const RHS &R) {
514   return BinaryOpc_match<LHS, RHS, true>(Opcode, L, R);
515 }
516 
517 template <typename LHS, typename RHS>
518 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_ADD, true>
519 m_GAdd(const LHS &L, const RHS &R) {
520   return BinaryOp_match<LHS, RHS, TargetOpcode::G_ADD, true>(L, R);
521 }
522 
523 template <typename LHS, typename RHS>
524 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR, false>
525 m_GBuildVector(const LHS &L, const RHS &R) {
526   return BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR, false>(L, R);
527 }
528 
529 template <typename LHS, typename RHS>
530 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR_TRUNC, false>
531 m_GBuildVectorTrunc(const LHS &L, const RHS &R) {
532   return BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR_TRUNC, false>(L,
533                                                                              R);
534 }
535 
536 template <typename LHS, typename RHS>
537 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_PTR_ADD, false>
538 m_GPtrAdd(const LHS &L, const RHS &R) {
539   return BinaryOp_match<LHS, RHS, TargetOpcode::G_PTR_ADD, false>(L, R);
540 }
541 
542 template <typename LHS, typename RHS>
543 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SUB> m_GSub(const LHS &L,
544                                                             const RHS &R) {
545   return BinaryOp_match<LHS, RHS, TargetOpcode::G_SUB>(L, R);
546 }
547 
548 template <typename LHS, typename RHS>
549 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_MUL, true>
550 m_GMul(const LHS &L, const RHS &R) {
551   return BinaryOp_match<LHS, RHS, TargetOpcode::G_MUL, true>(L, R);
552 }
553 
554 template <typename LHS, typename RHS>
555 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FADD, true>
556 m_GFAdd(const LHS &L, const RHS &R) {
557   return BinaryOp_match<LHS, RHS, TargetOpcode::G_FADD, true>(L, R);
558 }
559 
560 template <typename LHS, typename RHS>
561 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FMUL, true>
562 m_GFMul(const LHS &L, const RHS &R) {
563   return BinaryOp_match<LHS, RHS, TargetOpcode::G_FMUL, true>(L, R);
564 }
565 
566 template <typename LHS, typename RHS>
567 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FSUB, false>
568 m_GFSub(const LHS &L, const RHS &R) {
569   return BinaryOp_match<LHS, RHS, TargetOpcode::G_FSUB, false>(L, R);
570 }
571 
572 template <typename LHS, typename RHS>
573 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_AND, true>
574 m_GAnd(const LHS &L, const RHS &R) {
575   return BinaryOp_match<LHS, RHS, TargetOpcode::G_AND, true>(L, R);
576 }
577 
578 template <typename LHS, typename RHS>
579 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_XOR, true>
580 m_GXor(const LHS &L, const RHS &R) {
581   return BinaryOp_match<LHS, RHS, TargetOpcode::G_XOR, true>(L, R);
582 }
583 
584 template <typename LHS, typename RHS>
585 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true> m_GOr(const LHS &L,
586                                                                 const RHS &R) {
587   return BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true>(L, R);
588 }
589 
590 template <typename LHS, typename RHS>
591 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true,
592                       MachineInstr::Disjoint>
593 m_GDisjointOr(const LHS &L, const RHS &R) {
594   return BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true,
595                         MachineInstr::Disjoint>(L, R);
596 }
597 
598 template <typename LHS, typename RHS>
599 inline auto m_GAddLike(const LHS &L, const RHS &R) {
600   return m_any_of(m_GAdd(L, R), m_GDisjointOr(L, R));
601 }
602 
603 template <typename LHS, typename RHS>
604 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SHL, false>
605 m_GShl(const LHS &L, const RHS &R) {
606   return BinaryOp_match<LHS, RHS, TargetOpcode::G_SHL, false>(L, R);
607 }
608 
609 template <typename LHS, typename RHS>
610 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_LSHR, false>
611 m_GLShr(const LHS &L, const RHS &R) {
612   return BinaryOp_match<LHS, RHS, TargetOpcode::G_LSHR, false>(L, R);
613 }
614 
615 template <typename LHS, typename RHS>
616 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_ASHR, false>
617 m_GAShr(const LHS &L, const RHS &R) {
618   return BinaryOp_match<LHS, RHS, TargetOpcode::G_ASHR, false>(L, R);
619 }
620 
621 template <typename LHS, typename RHS>
622 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SMAX, true>
623 m_GSMax(const LHS &L, const RHS &R) {
624   return BinaryOp_match<LHS, RHS, TargetOpcode::G_SMAX, true>(L, R);
625 }
626 
627 template <typename LHS, typename RHS>
628 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SMIN, true>
629 m_GSMin(const LHS &L, const RHS &R) {
630   return BinaryOp_match<LHS, RHS, TargetOpcode::G_SMIN, true>(L, R);
631 }
632 
633 template <typename LHS, typename RHS>
634 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_UMAX, true>
635 m_GUMax(const LHS &L, const RHS &R) {
636   return BinaryOp_match<LHS, RHS, TargetOpcode::G_UMAX, true>(L, R);
637 }
638 
639 template <typename LHS, typename RHS>
640 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_UMIN, true>
641 m_GUMin(const LHS &L, const RHS &R) {
642   return BinaryOp_match<LHS, RHS, TargetOpcode::G_UMIN, true>(L, R);
643 }
644 
645 // Helper for unary instructions (G_[ZSA]EXT/G_TRUNC) etc
646 template <typename SrcTy, unsigned Opcode> struct UnaryOp_match {
647   SrcTy L;
648 
649   UnaryOp_match(const SrcTy &LHS) : L(LHS) {}
650   template <typename OpTy>
651   bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
652     MachineInstr *TmpMI;
653     if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
654       if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 2) {
655         return L.match(MRI, TmpMI->getOperand(1).getReg());
656       }
657     }
658     return false;
659   }
660 };
661 
662 template <typename SrcTy>
663 inline UnaryOp_match<SrcTy, TargetOpcode::G_ANYEXT>
664 m_GAnyExt(const SrcTy &Src) {
665   return UnaryOp_match<SrcTy, TargetOpcode::G_ANYEXT>(Src);
666 }
667 
668 template <typename SrcTy>
669 inline UnaryOp_match<SrcTy, TargetOpcode::G_SEXT> m_GSExt(const SrcTy &Src) {
670   return UnaryOp_match<SrcTy, TargetOpcode::G_SEXT>(Src);
671 }
672 
673 template <typename SrcTy>
674 inline UnaryOp_match<SrcTy, TargetOpcode::G_ZEXT> m_GZExt(const SrcTy &Src) {
675   return UnaryOp_match<SrcTy, TargetOpcode::G_ZEXT>(Src);
676 }
677 
678 template <typename SrcTy>
679 inline UnaryOp_match<SrcTy, TargetOpcode::G_FPEXT> m_GFPExt(const SrcTy &Src) {
680   return UnaryOp_match<SrcTy, TargetOpcode::G_FPEXT>(Src);
681 }
682 
683 template <typename SrcTy>
684 inline UnaryOp_match<SrcTy, TargetOpcode::G_TRUNC> m_GTrunc(const SrcTy &Src) {
685   return UnaryOp_match<SrcTy, TargetOpcode::G_TRUNC>(Src);
686 }
687 
688 template <typename SrcTy>
689 inline UnaryOp_match<SrcTy, TargetOpcode::G_BITCAST>
690 m_GBitcast(const SrcTy &Src) {
691   return UnaryOp_match<SrcTy, TargetOpcode::G_BITCAST>(Src);
692 }
693 
694 template <typename SrcTy>
695 inline UnaryOp_match<SrcTy, TargetOpcode::G_PTRTOINT>
696 m_GPtrToInt(const SrcTy &Src) {
697   return UnaryOp_match<SrcTy, TargetOpcode::G_PTRTOINT>(Src);
698 }
699 
700 template <typename SrcTy>
701 inline UnaryOp_match<SrcTy, TargetOpcode::G_INTTOPTR>
702 m_GIntToPtr(const SrcTy &Src) {
703   return UnaryOp_match<SrcTy, TargetOpcode::G_INTTOPTR>(Src);
704 }
705 
706 template <typename SrcTy>
707 inline UnaryOp_match<SrcTy, TargetOpcode::G_FPTRUNC>
708 m_GFPTrunc(const SrcTy &Src) {
709   return UnaryOp_match<SrcTy, TargetOpcode::G_FPTRUNC>(Src);
710 }
711 
712 template <typename SrcTy>
713 inline UnaryOp_match<SrcTy, TargetOpcode::G_FABS> m_GFabs(const SrcTy &Src) {
714   return UnaryOp_match<SrcTy, TargetOpcode::G_FABS>(Src);
715 }
716 
717 template <typename SrcTy>
718 inline UnaryOp_match<SrcTy, TargetOpcode::G_FNEG> m_GFNeg(const SrcTy &Src) {
719   return UnaryOp_match<SrcTy, TargetOpcode::G_FNEG>(Src);
720 }
721 
722 template <typename SrcTy>
723 inline UnaryOp_match<SrcTy, TargetOpcode::COPY> m_Copy(SrcTy &&Src) {
724   return UnaryOp_match<SrcTy, TargetOpcode::COPY>(std::forward<SrcTy>(Src));
725 }
726 
727 template <typename SrcTy>
728 inline UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT> m_GFSqrt(const SrcTy &Src) {
729   return UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT>(Src);
730 }
731 
732 // General helper for generic MI compares, i.e. G_ICMP and G_FCMP
733 // TODO: Allow checking a specific predicate.
734 template <typename Pred_P, typename LHS_P, typename RHS_P, unsigned Opcode,
735           bool Commutable = false>
736 struct CompareOp_match {
737   Pred_P P;
738   LHS_P L;
739   RHS_P R;
740 
741   CompareOp_match(const Pred_P &Pred, const LHS_P &LHS, const RHS_P &RHS)
742       : P(Pred), L(LHS), R(RHS) {}
743 
744   template <typename OpTy>
745   bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
746     MachineInstr *TmpMI;
747     if (!mi_match(Op, MRI, m_MInstr(TmpMI)) || TmpMI->getOpcode() != Opcode)
748       return false;
749 
750     auto TmpPred =
751         static_cast<CmpInst::Predicate>(TmpMI->getOperand(1).getPredicate());
752     if (!P.match(MRI, TmpPred))
753       return false;
754     Register LHS = TmpMI->getOperand(2).getReg();
755     Register RHS = TmpMI->getOperand(3).getReg();
756     if (L.match(MRI, LHS) && R.match(MRI, RHS))
757       return true;
758     // NOTE: When trying the alternative operand ordering
759     // with a commutative operation, it is imperative to always run
760     // the LHS sub-pattern  (i.e. `L`) before the RHS sub-pattern
761     // (i.e. `R`). Otherwise, m_DeferredReg/Type will not work as expected.
762     if (Commutable && L.match(MRI, RHS) && R.match(MRI, LHS) &&
763         P.match(MRI, CmpInst::getSwappedPredicate(TmpPred)))
764       return true;
765     return false;
766   }
767 };
768 
769 template <typename LHS_P, typename Test_P, unsigned Opcode>
770 struct ClassifyOp_match {
771   LHS_P L;
772   Test_P T;
773 
774   ClassifyOp_match(const LHS_P &LHS, const Test_P &Tst) : L(LHS), T(Tst) {}
775 
776   template <typename OpTy>
777   bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
778     MachineInstr *TmpMI;
779     if (!mi_match(Op, MRI, m_MInstr(TmpMI)) || TmpMI->getOpcode() != Opcode)
780       return false;
781 
782     Register LHS = TmpMI->getOperand(1).getReg();
783     if (!L.match(MRI, LHS))
784       return false;
785 
786     FPClassTest TmpClass =
787         static_cast<FPClassTest>(TmpMI->getOperand(2).getImm());
788     if (T.match(MRI, TmpClass))
789       return true;
790 
791     return false;
792   }
793 };
794 
795 template <typename Pred, typename LHS, typename RHS>
796 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP>
797 m_GICmp(const Pred &P, const LHS &L, const RHS &R) {
798   return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP>(P, L, R);
799 }
800 
801 template <typename Pred, typename LHS, typename RHS>
802 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP>
803 m_GFCmp(const Pred &P, const LHS &L, const RHS &R) {
804   return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP>(P, L, R);
805 }
806 
807 /// G_ICMP matcher that also matches commuted compares.
808 /// E.g.
809 ///
810 /// m_c_GICmp(m_Pred(...), m_GAdd(...), m_GSub(...))
811 ///
812 /// Could match both of:
813 ///
814 /// icmp ugt (add x, y) (sub a, b)
815 /// icmp ult (sub a, b) (add x, y)
816 template <typename Pred, typename LHS, typename RHS>
817 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP, true>
818 m_c_GICmp(const Pred &P, const LHS &L, const RHS &R) {
819   return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP, true>(P, L, R);
820 }
821 
822 /// G_FCMP matcher that also matches commuted compares.
823 /// E.g.
824 ///
825 /// m_c_GFCmp(m_Pred(...), m_FAdd(...), m_GFMul(...))
826 ///
827 /// Could match both of:
828 ///
829 /// fcmp ogt (fadd x, y) (fmul a, b)
830 /// fcmp olt (fmul a, b) (fadd x, y)
831 template <typename Pred, typename LHS, typename RHS>
832 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP, true>
833 m_c_GFCmp(const Pred &P, const LHS &L, const RHS &R) {
834   return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP, true>(P, L, R);
835 }
836 
837 /// Matches the register and immediate used in a fpclass test
838 /// G_IS_FPCLASS %val, 96
839 template <typename LHS, typename Test>
840 inline ClassifyOp_match<LHS, Test, TargetOpcode::G_IS_FPCLASS>
841 m_GIsFPClass(const LHS &L, const Test &T) {
842   return ClassifyOp_match<LHS, Test, TargetOpcode::G_IS_FPCLASS>(L, T);
843 }
844 
845 // Helper for checking if a Reg is of specific type.
846 struct CheckType {
847   LLT Ty;
848   CheckType(const LLT Ty) : Ty(Ty) {}
849 
850   bool match(const MachineRegisterInfo &MRI, Register Reg) {
851     return MRI.getType(Reg) == Ty;
852   }
853 };
854 
855 inline CheckType m_SpecificType(LLT Ty) { return Ty; }
856 
857 template <typename Src0Ty, typename Src1Ty, typename Src2Ty, unsigned Opcode>
858 struct TernaryOp_match {
859   Src0Ty Src0;
860   Src1Ty Src1;
861   Src2Ty Src2;
862 
863   TernaryOp_match(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2)
864       : Src0(Src0), Src1(Src1), Src2(Src2) {}
865   template <typename OpTy>
866   bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
867     MachineInstr *TmpMI;
868     if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
869       if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 4) {
870         return (Src0.match(MRI, TmpMI->getOperand(1).getReg()) &&
871                 Src1.match(MRI, TmpMI->getOperand(2).getReg()) &&
872                 Src2.match(MRI, TmpMI->getOperand(3).getReg()));
873       }
874     }
875     return false;
876   }
877 };
878 template <typename Src0Ty, typename Src1Ty, typename Src2Ty>
879 inline TernaryOp_match<Src0Ty, Src1Ty, Src2Ty,
880                        TargetOpcode::G_INSERT_VECTOR_ELT>
881 m_GInsertVecElt(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2) {
882   return TernaryOp_match<Src0Ty, Src1Ty, Src2Ty,
883                          TargetOpcode::G_INSERT_VECTOR_ELT>(Src0, Src1, Src2);
884 }
885 
886 template <typename Src0Ty, typename Src1Ty, typename Src2Ty>
887 inline TernaryOp_match<Src0Ty, Src1Ty, Src2Ty, TargetOpcode::G_SELECT>
888 m_GISelect(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2) {
889   return TernaryOp_match<Src0Ty, Src1Ty, Src2Ty, TargetOpcode::G_SELECT>(
890       Src0, Src1, Src2);
891 }
892 
893 /// Matches a register negated by a G_SUB.
894 /// G_SUB 0, %negated_reg
895 template <typename SrcTy>
896 inline BinaryOp_match<SpecificConstantMatch, SrcTy, TargetOpcode::G_SUB>
897 m_Neg(const SrcTy &&Src) {
898   return m_GSub(m_ZeroInt(), Src);
899 }
900 
901 /// Matches a register not-ed by a G_XOR.
902 /// G_XOR %not_reg, -1
903 template <typename SrcTy>
904 inline BinaryOp_match<SrcTy, SpecificConstantMatch, TargetOpcode::G_XOR, true>
905 m_Not(const SrcTy &&Src) {
906   return m_GXor(Src, m_AllOnesInt());
907 }
908 
909 } // namespace MIPatternMatch
910 } // namespace llvm
911 
912 #endif
913