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