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/CodeGen/GlobalISel/Utils.h"
18 #include "llvm/CodeGen/MachineRegisterInfo.h"
19 #include "llvm/IR/InstrTypes.h"
20
21 namespace llvm {
22 namespace MIPatternMatch {
23
24 template <typename Reg, typename Pattern>
mi_match(Reg R,const MachineRegisterInfo & MRI,Pattern && P)25 [[nodiscard]] bool mi_match(Reg R, const MachineRegisterInfo &MRI,
26 Pattern &&P) {
27 return P.match(MRI, R);
28 }
29
30 template <typename Pattern>
mi_match(MachineInstr & MI,const MachineRegisterInfo & MRI,Pattern && P)31 [[nodiscard]] bool mi_match(MachineInstr &MI, const MachineRegisterInfo &MRI,
32 Pattern &&P) {
33 return P.match(MRI, &MI);
34 }
35
36 // TODO: Extend for N use.
37 template <typename SubPatternT> struct OneUse_match {
38 SubPatternT SubPat;
OneUse_matchOneUse_match39 OneUse_match(const SubPatternT &SP) : SubPat(SP) {}
40
matchOneUse_match41 bool match(const MachineRegisterInfo &MRI, Register Reg) {
42 return MRI.hasOneUse(Reg) && SubPat.match(MRI, Reg);
43 }
44 };
45
46 template <typename SubPat>
m_OneUse(const SubPat & SP)47 inline OneUse_match<SubPat> m_OneUse(const SubPat &SP) {
48 return SP;
49 }
50
51 template <typename SubPatternT> struct OneNonDBGUse_match {
52 SubPatternT SubPat;
OneNonDBGUse_matchOneNonDBGUse_match53 OneNonDBGUse_match(const SubPatternT &SP) : SubPat(SP) {}
54
matchOneNonDBGUse_match55 bool match(const MachineRegisterInfo &MRI, Register Reg) {
56 return MRI.hasOneNonDBGUse(Reg) && SubPat.match(MRI, Reg);
57 }
58 };
59
60 template <typename SubPat>
m_OneNonDBGUse(const SubPat & SP)61 inline OneNonDBGUse_match<SubPat> m_OneNonDBGUse(const SubPat &SP) {
62 return SP;
63 }
64
65 template <typename ConstT>
66 inline std::optional<ConstT> matchConstant(Register,
67 const MachineRegisterInfo &);
68
69 template <>
matchConstant(Register Reg,const MachineRegisterInfo & MRI)70 inline std::optional<APInt> matchConstant(Register Reg,
71 const MachineRegisterInfo &MRI) {
72 return getIConstantVRegVal(Reg, MRI);
73 }
74
75 template <>
matchConstant(Register Reg,const MachineRegisterInfo & MRI)76 inline std::optional<int64_t> matchConstant(Register Reg,
77 const MachineRegisterInfo &MRI) {
78 return getIConstantVRegSExtVal(Reg, MRI);
79 }
80
81 template <typename ConstT> struct ConstantMatch {
82 ConstT &CR;
ConstantMatchConstantMatch83 ConstantMatch(ConstT &C) : CR(C) {}
matchConstantMatch84 bool match(const MachineRegisterInfo &MRI, Register Reg) {
85 if (auto MaybeCst = matchConstant<ConstT>(Reg, MRI)) {
86 CR = *MaybeCst;
87 return true;
88 }
89 return false;
90 }
91 };
92
m_ICst(APInt & Cst)93 inline ConstantMatch<APInt> m_ICst(APInt &Cst) {
94 return ConstantMatch<APInt>(Cst);
95 }
m_ICst(int64_t & Cst)96 inline ConstantMatch<int64_t> m_ICst(int64_t &Cst) {
97 return ConstantMatch<int64_t>(Cst);
98 }
99
100 template <typename ConstT>
101 inline std::optional<ConstT> matchConstantSplat(Register,
102 const MachineRegisterInfo &);
103
104 template <>
matchConstantSplat(Register Reg,const MachineRegisterInfo & MRI)105 inline std::optional<APInt> matchConstantSplat(Register Reg,
106 const MachineRegisterInfo &MRI) {
107 return getIConstantSplatVal(Reg, MRI);
108 }
109
110 template <>
111 inline std::optional<int64_t>
matchConstantSplat(Register Reg,const MachineRegisterInfo & MRI)112 matchConstantSplat(Register Reg, const MachineRegisterInfo &MRI) {
113 return getIConstantSplatSExtVal(Reg, MRI);
114 }
115
116 template <typename ConstT> struct ICstOrSplatMatch {
117 ConstT &CR;
ICstOrSplatMatchICstOrSplatMatch118 ICstOrSplatMatch(ConstT &C) : CR(C) {}
matchICstOrSplatMatch119 bool match(const MachineRegisterInfo &MRI, Register Reg) {
120 if (auto MaybeCst = matchConstant<ConstT>(Reg, MRI)) {
121 CR = *MaybeCst;
122 return true;
123 }
124
125 if (auto MaybeCstSplat = matchConstantSplat<ConstT>(Reg, MRI)) {
126 CR = *MaybeCstSplat;
127 return true;
128 }
129
130 return false;
131 };
132 };
133
m_ICstOrSplat(APInt & Cst)134 inline ICstOrSplatMatch<APInt> m_ICstOrSplat(APInt &Cst) {
135 return ICstOrSplatMatch<APInt>(Cst);
136 }
137
m_ICstOrSplat(int64_t & Cst)138 inline ICstOrSplatMatch<int64_t> m_ICstOrSplat(int64_t &Cst) {
139 return ICstOrSplatMatch<int64_t>(Cst);
140 }
141
142 struct GCstAndRegMatch {
143 std::optional<ValueAndVReg> &ValReg;
GCstAndRegMatchGCstAndRegMatch144 GCstAndRegMatch(std::optional<ValueAndVReg> &ValReg) : ValReg(ValReg) {}
matchGCstAndRegMatch145 bool match(const MachineRegisterInfo &MRI, Register Reg) {
146 ValReg = getIConstantVRegValWithLookThrough(Reg, MRI);
147 return ValReg ? true : false;
148 }
149 };
150
m_GCst(std::optional<ValueAndVReg> & ValReg)151 inline GCstAndRegMatch m_GCst(std::optional<ValueAndVReg> &ValReg) {
152 return GCstAndRegMatch(ValReg);
153 }
154
155 struct GFCstAndRegMatch {
156 std::optional<FPValueAndVReg> &FPValReg;
GFCstAndRegMatchGFCstAndRegMatch157 GFCstAndRegMatch(std::optional<FPValueAndVReg> &FPValReg)
158 : FPValReg(FPValReg) {}
matchGFCstAndRegMatch159 bool match(const MachineRegisterInfo &MRI, Register Reg) {
160 FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI);
161 return FPValReg ? true : false;
162 }
163 };
164
m_GFCst(std::optional<FPValueAndVReg> & FPValReg)165 inline GFCstAndRegMatch m_GFCst(std::optional<FPValueAndVReg> &FPValReg) {
166 return GFCstAndRegMatch(FPValReg);
167 }
168
169 struct GFCstOrSplatGFCstMatch {
170 std::optional<FPValueAndVReg> &FPValReg;
GFCstOrSplatGFCstMatchGFCstOrSplatGFCstMatch171 GFCstOrSplatGFCstMatch(std::optional<FPValueAndVReg> &FPValReg)
172 : FPValReg(FPValReg) {}
matchGFCstOrSplatGFCstMatch173 bool match(const MachineRegisterInfo &MRI, Register Reg) {
174 return (FPValReg = getFConstantSplat(Reg, MRI)) ||
175 (FPValReg = getFConstantVRegValWithLookThrough(Reg, MRI));
176 };
177 };
178
179 inline GFCstOrSplatGFCstMatch
m_GFCstOrSplat(std::optional<FPValueAndVReg> & FPValReg)180 m_GFCstOrSplat(std::optional<FPValueAndVReg> &FPValReg) {
181 return GFCstOrSplatGFCstMatch(FPValReg);
182 }
183
184 /// Matcher for a specific constant value.
185 struct SpecificConstantMatch {
186 int64_t RequestedVal;
SpecificConstantMatchSpecificConstantMatch187 SpecificConstantMatch(int64_t RequestedVal) : RequestedVal(RequestedVal) {}
matchSpecificConstantMatch188 bool match(const MachineRegisterInfo &MRI, Register Reg) {
189 int64_t MatchedVal;
190 return mi_match(Reg, MRI, m_ICst(MatchedVal)) && MatchedVal == RequestedVal;
191 }
192 };
193
194 /// Matches a constant equal to \p RequestedValue.
m_SpecificICst(int64_t RequestedValue)195 inline SpecificConstantMatch m_SpecificICst(int64_t RequestedValue) {
196 return SpecificConstantMatch(RequestedValue);
197 }
198
199 /// Matcher for a specific constant splat.
200 struct SpecificConstantSplatMatch {
201 int64_t RequestedVal;
SpecificConstantSplatMatchSpecificConstantSplatMatch202 SpecificConstantSplatMatch(int64_t RequestedVal)
203 : RequestedVal(RequestedVal) {}
matchSpecificConstantSplatMatch204 bool match(const MachineRegisterInfo &MRI, Register Reg) {
205 return isBuildVectorConstantSplat(Reg, MRI, RequestedVal,
206 /* AllowUndef */ false);
207 }
208 };
209
210 /// Matches a constant splat of \p RequestedValue.
m_SpecificICstSplat(int64_t RequestedValue)211 inline SpecificConstantSplatMatch m_SpecificICstSplat(int64_t RequestedValue) {
212 return SpecificConstantSplatMatch(RequestedValue);
213 }
214
215 /// Matcher for a specific constant or constant splat.
216 struct SpecificConstantOrSplatMatch {
217 int64_t RequestedVal;
SpecificConstantOrSplatMatchSpecificConstantOrSplatMatch218 SpecificConstantOrSplatMatch(int64_t RequestedVal)
219 : RequestedVal(RequestedVal) {}
matchSpecificConstantOrSplatMatch220 bool match(const MachineRegisterInfo &MRI, Register Reg) {
221 int64_t MatchedVal;
222 if (mi_match(Reg, MRI, m_ICst(MatchedVal)) && MatchedVal == RequestedVal)
223 return true;
224 return isBuildVectorConstantSplat(Reg, MRI, RequestedVal,
225 /* AllowUndef */ false);
226 }
227 };
228
229 /// Matches a \p RequestedValue constant or a constant splat of \p
230 /// RequestedValue.
231 inline SpecificConstantOrSplatMatch
m_SpecificICstOrSplat(int64_t RequestedValue)232 m_SpecificICstOrSplat(int64_t RequestedValue) {
233 return SpecificConstantOrSplatMatch(RequestedValue);
234 }
235
236 ///{
237 /// Convenience matchers for specific integer values.
m_ZeroInt()238 inline SpecificConstantMatch m_ZeroInt() { return SpecificConstantMatch(0); }
m_AllOnesInt()239 inline SpecificConstantMatch m_AllOnesInt() {
240 return SpecificConstantMatch(-1);
241 }
242 ///}
243
244 /// Matcher for a specific register.
245 struct SpecificRegisterMatch {
246 Register RequestedReg;
SpecificRegisterMatchSpecificRegisterMatch247 SpecificRegisterMatch(Register RequestedReg) : RequestedReg(RequestedReg) {}
matchSpecificRegisterMatch248 bool match(const MachineRegisterInfo &MRI, Register Reg) {
249 return Reg == RequestedReg;
250 }
251 };
252
253 /// Matches a register only if it is equal to \p RequestedReg.
m_SpecificReg(Register RequestedReg)254 inline SpecificRegisterMatch m_SpecificReg(Register RequestedReg) {
255 return SpecificRegisterMatch(RequestedReg);
256 }
257
258 // TODO: Rework this for different kinds of MachineOperand.
259 // Currently assumes the Src for a match is a register.
260 // We might want to support taking in some MachineOperands and call getReg on
261 // that.
262
263 struct operand_type_match {
matchoperand_type_match264 bool match(const MachineRegisterInfo &MRI, Register Reg) { return true; }
matchoperand_type_match265 bool match(const MachineRegisterInfo &MRI, MachineOperand *MO) {
266 return MO->isReg();
267 }
268 };
269
m_Reg()270 inline operand_type_match m_Reg() { return operand_type_match(); }
271
272 /// Matching combinators.
273 template <typename... Preds> struct And {
274 template <typename MatchSrc>
matchAnd275 bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
276 return true;
277 }
278 };
279
280 template <typename Pred, typename... Preds>
281 struct And<Pred, Preds...> : And<Preds...> {
282 Pred P;
283 And(Pred &&p, Preds &&... preds)
284 : And<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {
285 }
286 template <typename MatchSrc>
287 bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
288 return P.match(MRI, src) && And<Preds...>::match(MRI, src);
289 }
290 };
291
292 template <typename... Preds> struct Or {
293 template <typename MatchSrc>
294 bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
295 return false;
296 }
297 };
298
299 template <typename Pred, typename... Preds>
300 struct Or<Pred, Preds...> : Or<Preds...> {
301 Pred P;
302 Or(Pred &&p, Preds &&... preds)
303 : Or<Preds...>(std::forward<Preds>(preds)...), P(std::forward<Pred>(p)) {}
304 template <typename MatchSrc>
305 bool match(const MachineRegisterInfo &MRI, MatchSrc &&src) {
306 return P.match(MRI, src) || Or<Preds...>::match(MRI, src);
307 }
308 };
309
310 template <typename... Preds> And<Preds...> m_all_of(Preds &&... preds) {
311 return And<Preds...>(std::forward<Preds>(preds)...);
312 }
313
314 template <typename... Preds> Or<Preds...> m_any_of(Preds &&... preds) {
315 return Or<Preds...>(std::forward<Preds>(preds)...);
316 }
317
318 template <typename BindTy> struct bind_helper {
319 static bool bind(const MachineRegisterInfo &MRI, BindTy &VR, BindTy &V) {
320 VR = V;
321 return true;
322 }
323 };
324
325 template <> struct bind_helper<MachineInstr *> {
326 static bool bind(const MachineRegisterInfo &MRI, MachineInstr *&MI,
327 Register Reg) {
328 MI = MRI.getVRegDef(Reg);
329 if (MI)
330 return true;
331 return false;
332 }
333 static bool bind(const MachineRegisterInfo &MRI, MachineInstr *&MI,
334 MachineInstr *Inst) {
335 MI = Inst;
336 return MI;
337 }
338 };
339
340 template <> struct bind_helper<LLT> {
341 static bool bind(const MachineRegisterInfo &MRI, LLT Ty, Register Reg) {
342 Ty = MRI.getType(Reg);
343 if (Ty.isValid())
344 return true;
345 return false;
346 }
347 };
348
349 template <> struct bind_helper<const ConstantFP *> {
350 static bool bind(const MachineRegisterInfo &MRI, const ConstantFP *&F,
351 Register Reg) {
352 F = getConstantFPVRegVal(Reg, MRI);
353 if (F)
354 return true;
355 return false;
356 }
357 };
358
359 template <typename Class> struct bind_ty {
360 Class &VR;
361
362 bind_ty(Class &V) : VR(V) {}
363
364 template <typename ITy> bool match(const MachineRegisterInfo &MRI, ITy &&V) {
365 return bind_helper<Class>::bind(MRI, VR, V);
366 }
367 };
368
369 inline bind_ty<Register> m_Reg(Register &R) { return R; }
370 inline bind_ty<MachineInstr *> m_MInstr(MachineInstr *&MI) { return MI; }
371 inline bind_ty<LLT> m_Type(LLT Ty) { return Ty; }
372 inline bind_ty<CmpInst::Predicate> m_Pred(CmpInst::Predicate &P) { return P; }
373 inline operand_type_match m_Pred() { return operand_type_match(); }
374
375 struct ImplicitDefMatch {
376 bool match(const MachineRegisterInfo &MRI, Register Reg) {
377 MachineInstr *TmpMI;
378 if (mi_match(Reg, MRI, m_MInstr(TmpMI)))
379 return TmpMI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF;
380 return false;
381 }
382 };
383
384 inline ImplicitDefMatch m_GImplicitDef() { return ImplicitDefMatch(); }
385
386 // Helper for matching G_FCONSTANT
387 inline bind_ty<const ConstantFP *> m_GFCst(const ConstantFP *&C) { return C; }
388
389 // General helper for all the binary generic MI such as G_ADD/G_SUB etc
390 template <typename LHS_P, typename RHS_P, unsigned Opcode,
391 bool Commutable = false>
392 struct BinaryOp_match {
393 LHS_P L;
394 RHS_P R;
395
396 BinaryOp_match(const LHS_P &LHS, const RHS_P &RHS) : L(LHS), R(RHS) {}
397 template <typename OpTy>
398 bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
399 MachineInstr *TmpMI;
400 if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
401 if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 3) {
402 return (L.match(MRI, TmpMI->getOperand(1).getReg()) &&
403 R.match(MRI, TmpMI->getOperand(2).getReg())) ||
404 (Commutable && (R.match(MRI, TmpMI->getOperand(1).getReg()) &&
405 L.match(MRI, TmpMI->getOperand(2).getReg())));
406 }
407 }
408 return false;
409 }
410 };
411
412 // Helper for (commutative) binary generic MI that checks Opcode.
413 template <typename LHS_P, typename RHS_P, bool Commutable = false>
414 struct BinaryOpc_match {
415 unsigned Opc;
416 LHS_P L;
417 RHS_P R;
418
419 BinaryOpc_match(unsigned Opcode, const LHS_P &LHS, const RHS_P &RHS)
420 : Opc(Opcode), L(LHS), R(RHS) {}
421 template <typename OpTy>
422 bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
423 MachineInstr *TmpMI;
424 if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
425 if (TmpMI->getOpcode() == Opc && TmpMI->getNumDefs() == 1 &&
426 TmpMI->getNumOperands() == 3) {
427 return (L.match(MRI, TmpMI->getOperand(1).getReg()) &&
428 R.match(MRI, TmpMI->getOperand(2).getReg())) ||
429 (Commutable && (R.match(MRI, TmpMI->getOperand(1).getReg()) &&
430 L.match(MRI, TmpMI->getOperand(2).getReg())));
431 }
432 }
433 return false;
434 }
435 };
436
437 template <typename LHS, typename RHS>
438 inline BinaryOpc_match<LHS, RHS, false> m_BinOp(unsigned Opcode, const LHS &L,
439 const RHS &R) {
440 return BinaryOpc_match<LHS, RHS, false>(Opcode, L, R);
441 }
442
443 template <typename LHS, typename RHS>
444 inline BinaryOpc_match<LHS, RHS, true>
445 m_CommutativeBinOp(unsigned Opcode, const LHS &L, const RHS &R) {
446 return BinaryOpc_match<LHS, RHS, true>(Opcode, L, R);
447 }
448
449 template <typename LHS, typename RHS>
450 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_ADD, true>
451 m_GAdd(const LHS &L, const RHS &R) {
452 return BinaryOp_match<LHS, RHS, TargetOpcode::G_ADD, true>(L, R);
453 }
454
455 template <typename LHS, typename RHS>
456 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR, false>
457 m_GBuildVector(const LHS &L, const RHS &R) {
458 return BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR, false>(L, R);
459 }
460
461 template <typename LHS, typename RHS>
462 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR_TRUNC, false>
463 m_GBuildVectorTrunc(const LHS &L, const RHS &R) {
464 return BinaryOp_match<LHS, RHS, TargetOpcode::G_BUILD_VECTOR_TRUNC, false>(L,
465 R);
466 }
467
468 template <typename LHS, typename RHS>
469 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_PTR_ADD, false>
470 m_GPtrAdd(const LHS &L, const RHS &R) {
471 return BinaryOp_match<LHS, RHS, TargetOpcode::G_PTR_ADD, false>(L, R);
472 }
473
474 template <typename LHS, typename RHS>
475 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SUB> m_GSub(const LHS &L,
476 const RHS &R) {
477 return BinaryOp_match<LHS, RHS, TargetOpcode::G_SUB>(L, R);
478 }
479
480 template <typename LHS, typename RHS>
481 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_MUL, true>
482 m_GMul(const LHS &L, const RHS &R) {
483 return BinaryOp_match<LHS, RHS, TargetOpcode::G_MUL, true>(L, R);
484 }
485
486 template <typename LHS, typename RHS>
487 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FADD, true>
488 m_GFAdd(const LHS &L, const RHS &R) {
489 return BinaryOp_match<LHS, RHS, TargetOpcode::G_FADD, true>(L, R);
490 }
491
492 template <typename LHS, typename RHS>
493 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FMUL, true>
494 m_GFMul(const LHS &L, const RHS &R) {
495 return BinaryOp_match<LHS, RHS, TargetOpcode::G_FMUL, true>(L, R);
496 }
497
498 template <typename LHS, typename RHS>
499 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_FSUB, false>
500 m_GFSub(const LHS &L, const RHS &R) {
501 return BinaryOp_match<LHS, RHS, TargetOpcode::G_FSUB, false>(L, R);
502 }
503
504 template <typename LHS, typename RHS>
505 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_AND, true>
506 m_GAnd(const LHS &L, const RHS &R) {
507 return BinaryOp_match<LHS, RHS, TargetOpcode::G_AND, true>(L, R);
508 }
509
510 template <typename LHS, typename RHS>
511 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_XOR, true>
512 m_GXor(const LHS &L, const RHS &R) {
513 return BinaryOp_match<LHS, RHS, TargetOpcode::G_XOR, true>(L, R);
514 }
515
516 template <typename LHS, typename RHS>
517 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true> m_GOr(const LHS &L,
518 const RHS &R) {
519 return BinaryOp_match<LHS, RHS, TargetOpcode::G_OR, true>(L, R);
520 }
521
522 template <typename LHS, typename RHS>
523 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SHL, false>
524 m_GShl(const LHS &L, const RHS &R) {
525 return BinaryOp_match<LHS, RHS, TargetOpcode::G_SHL, false>(L, R);
526 }
527
528 template <typename LHS, typename RHS>
529 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_LSHR, false>
530 m_GLShr(const LHS &L, const RHS &R) {
531 return BinaryOp_match<LHS, RHS, TargetOpcode::G_LSHR, false>(L, R);
532 }
533
534 template <typename LHS, typename RHS>
535 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_ASHR, false>
536 m_GAShr(const LHS &L, const RHS &R) {
537 return BinaryOp_match<LHS, RHS, TargetOpcode::G_ASHR, false>(L, R);
538 }
539
540 template <typename LHS, typename RHS>
541 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SMAX, false>
542 m_GSMax(const LHS &L, const RHS &R) {
543 return BinaryOp_match<LHS, RHS, TargetOpcode::G_SMAX, false>(L, R);
544 }
545
546 template <typename LHS, typename RHS>
547 inline BinaryOp_match<LHS, RHS, TargetOpcode::G_SMIN, false>
548 m_GSMin(const LHS &L, const RHS &R) {
549 return BinaryOp_match<LHS, RHS, TargetOpcode::G_SMIN, false>(L, R);
550 }
551
552 // Helper for unary instructions (G_[ZSA]EXT/G_TRUNC) etc
553 template <typename SrcTy, unsigned Opcode> struct UnaryOp_match {
554 SrcTy L;
555
556 UnaryOp_match(const SrcTy &LHS) : L(LHS) {}
557 template <typename OpTy>
558 bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
559 MachineInstr *TmpMI;
560 if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
561 if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 2) {
562 return L.match(MRI, TmpMI->getOperand(1).getReg());
563 }
564 }
565 return false;
566 }
567 };
568
569 template <typename SrcTy>
570 inline UnaryOp_match<SrcTy, TargetOpcode::G_ANYEXT>
571 m_GAnyExt(const SrcTy &Src) {
572 return UnaryOp_match<SrcTy, TargetOpcode::G_ANYEXT>(Src);
573 }
574
575 template <typename SrcTy>
576 inline UnaryOp_match<SrcTy, TargetOpcode::G_SEXT> m_GSExt(const SrcTy &Src) {
577 return UnaryOp_match<SrcTy, TargetOpcode::G_SEXT>(Src);
578 }
579
580 template <typename SrcTy>
581 inline UnaryOp_match<SrcTy, TargetOpcode::G_ZEXT> m_GZExt(const SrcTy &Src) {
582 return UnaryOp_match<SrcTy, TargetOpcode::G_ZEXT>(Src);
583 }
584
585 template <typename SrcTy>
586 inline UnaryOp_match<SrcTy, TargetOpcode::G_FPEXT> m_GFPExt(const SrcTy &Src) {
587 return UnaryOp_match<SrcTy, TargetOpcode::G_FPEXT>(Src);
588 }
589
590 template <typename SrcTy>
591 inline UnaryOp_match<SrcTy, TargetOpcode::G_TRUNC> m_GTrunc(const SrcTy &Src) {
592 return UnaryOp_match<SrcTy, TargetOpcode::G_TRUNC>(Src);
593 }
594
595 template <typename SrcTy>
596 inline UnaryOp_match<SrcTy, TargetOpcode::G_BITCAST>
597 m_GBitcast(const SrcTy &Src) {
598 return UnaryOp_match<SrcTy, TargetOpcode::G_BITCAST>(Src);
599 }
600
601 template <typename SrcTy>
602 inline UnaryOp_match<SrcTy, TargetOpcode::G_PTRTOINT>
603 m_GPtrToInt(const SrcTy &Src) {
604 return UnaryOp_match<SrcTy, TargetOpcode::G_PTRTOINT>(Src);
605 }
606
607 template <typename SrcTy>
608 inline UnaryOp_match<SrcTy, TargetOpcode::G_INTTOPTR>
609 m_GIntToPtr(const SrcTy &Src) {
610 return UnaryOp_match<SrcTy, TargetOpcode::G_INTTOPTR>(Src);
611 }
612
613 template <typename SrcTy>
614 inline UnaryOp_match<SrcTy, TargetOpcode::G_FPTRUNC>
615 m_GFPTrunc(const SrcTy &Src) {
616 return UnaryOp_match<SrcTy, TargetOpcode::G_FPTRUNC>(Src);
617 }
618
619 template <typename SrcTy>
620 inline UnaryOp_match<SrcTy, TargetOpcode::G_FABS> m_GFabs(const SrcTy &Src) {
621 return UnaryOp_match<SrcTy, TargetOpcode::G_FABS>(Src);
622 }
623
624 template <typename SrcTy>
625 inline UnaryOp_match<SrcTy, TargetOpcode::G_FNEG> m_GFNeg(const SrcTy &Src) {
626 return UnaryOp_match<SrcTy, TargetOpcode::G_FNEG>(Src);
627 }
628
629 template <typename SrcTy>
630 inline UnaryOp_match<SrcTy, TargetOpcode::COPY> m_Copy(SrcTy &&Src) {
631 return UnaryOp_match<SrcTy, TargetOpcode::COPY>(std::forward<SrcTy>(Src));
632 }
633
634 template <typename SrcTy>
635 inline UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT> m_GFSqrt(const SrcTy &Src) {
636 return UnaryOp_match<SrcTy, TargetOpcode::G_FSQRT>(Src);
637 }
638
639 // General helper for generic MI compares, i.e. G_ICMP and G_FCMP
640 // TODO: Allow checking a specific predicate.
641 template <typename Pred_P, typename LHS_P, typename RHS_P, unsigned Opcode,
642 bool Commutable = false>
643 struct CompareOp_match {
644 Pred_P P;
645 LHS_P L;
646 RHS_P R;
647
648 CompareOp_match(const Pred_P &Pred, const LHS_P &LHS, const RHS_P &RHS)
649 : P(Pred), L(LHS), R(RHS) {}
650
651 template <typename OpTy>
652 bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
653 MachineInstr *TmpMI;
654 if (!mi_match(Op, MRI, m_MInstr(TmpMI)) || TmpMI->getOpcode() != Opcode)
655 return false;
656
657 auto TmpPred =
658 static_cast<CmpInst::Predicate>(TmpMI->getOperand(1).getPredicate());
659 if (!P.match(MRI, TmpPred))
660 return false;
661 Register LHS = TmpMI->getOperand(2).getReg();
662 Register RHS = TmpMI->getOperand(3).getReg();
663 if (L.match(MRI, LHS) && R.match(MRI, RHS))
664 return true;
665 if (Commutable && L.match(MRI, RHS) && R.match(MRI, LHS) &&
666 P.match(MRI, CmpInst::getSwappedPredicate(TmpPred)))
667 return true;
668 return false;
669 }
670 };
671
672 template <typename Pred, typename LHS, typename RHS>
673 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP>
674 m_GICmp(const Pred &P, const LHS &L, const RHS &R) {
675 return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP>(P, L, R);
676 }
677
678 template <typename Pred, typename LHS, typename RHS>
679 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP>
680 m_GFCmp(const Pred &P, const LHS &L, const RHS &R) {
681 return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP>(P, L, R);
682 }
683
684 /// G_ICMP matcher that also matches commuted compares.
685 /// E.g.
686 ///
687 /// m_c_GICmp(m_Pred(...), m_GAdd(...), m_GSub(...))
688 ///
689 /// Could match both of:
690 ///
691 /// icmp ugt (add x, y) (sub a, b)
692 /// icmp ult (sub a, b) (add x, y)
693 template <typename Pred, typename LHS, typename RHS>
694 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP, true>
695 m_c_GICmp(const Pred &P, const LHS &L, const RHS &R) {
696 return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_ICMP, true>(P, L, R);
697 }
698
699 /// G_FCMP matcher that also matches commuted compares.
700 /// E.g.
701 ///
702 /// m_c_GFCmp(m_Pred(...), m_FAdd(...), m_GFMul(...))
703 ///
704 /// Could match both of:
705 ///
706 /// fcmp ogt (fadd x, y) (fmul a, b)
707 /// fcmp olt (fmul a, b) (fadd x, y)
708 template <typename Pred, typename LHS, typename RHS>
709 inline CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP, true>
710 m_c_GFCmp(const Pred &P, const LHS &L, const RHS &R) {
711 return CompareOp_match<Pred, LHS, RHS, TargetOpcode::G_FCMP, true>(P, L, R);
712 }
713
714 // Helper for checking if a Reg is of specific type.
715 struct CheckType {
716 LLT Ty;
717 CheckType(const LLT Ty) : Ty(Ty) {}
718
719 bool match(const MachineRegisterInfo &MRI, Register Reg) {
720 return MRI.getType(Reg) == Ty;
721 }
722 };
723
724 inline CheckType m_SpecificType(LLT Ty) { return Ty; }
725
726 template <typename Src0Ty, typename Src1Ty, typename Src2Ty, unsigned Opcode>
727 struct TernaryOp_match {
728 Src0Ty Src0;
729 Src1Ty Src1;
730 Src2Ty Src2;
731
732 TernaryOp_match(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2)
733 : Src0(Src0), Src1(Src1), Src2(Src2) {}
734 template <typename OpTy>
735 bool match(const MachineRegisterInfo &MRI, OpTy &&Op) {
736 MachineInstr *TmpMI;
737 if (mi_match(Op, MRI, m_MInstr(TmpMI))) {
738 if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 4) {
739 return (Src0.match(MRI, TmpMI->getOperand(1).getReg()) &&
740 Src1.match(MRI, TmpMI->getOperand(2).getReg()) &&
741 Src2.match(MRI, TmpMI->getOperand(3).getReg()));
742 }
743 }
744 return false;
745 }
746 };
747 template <typename Src0Ty, typename Src1Ty, typename Src2Ty>
748 inline TernaryOp_match<Src0Ty, Src1Ty, Src2Ty,
749 TargetOpcode::G_INSERT_VECTOR_ELT>
750 m_GInsertVecElt(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2) {
751 return TernaryOp_match<Src0Ty, Src1Ty, Src2Ty,
752 TargetOpcode::G_INSERT_VECTOR_ELT>(Src0, Src1, Src2);
753 }
754
755 template <typename Src0Ty, typename Src1Ty, typename Src2Ty>
756 inline TernaryOp_match<Src0Ty, Src1Ty, Src2Ty, TargetOpcode::G_SELECT>
757 m_GISelect(const Src0Ty &Src0, const Src1Ty &Src1, const Src2Ty &Src2) {
758 return TernaryOp_match<Src0Ty, Src1Ty, Src2Ty, TargetOpcode::G_SELECT>(
759 Src0, Src1, Src2);
760 }
761
762 /// Matches a register negated by a G_SUB.
763 /// G_SUB 0, %negated_reg
764 template <typename SrcTy>
765 inline BinaryOp_match<SpecificConstantMatch, SrcTy, TargetOpcode::G_SUB>
766 m_Neg(const SrcTy &&Src) {
767 return m_GSub(m_ZeroInt(), Src);
768 }
769
770 /// Matches a register not-ed by a G_XOR.
771 /// G_XOR %not_reg, -1
772 template <typename SrcTy>
773 inline BinaryOp_match<SrcTy, SpecificConstantMatch, TargetOpcode::G_XOR, true>
774 m_Not(const SrcTy &&Src) {
775 return m_GXor(Src, m_AllOnesInt());
776 }
777
778 } // namespace MIPatternMatch
779 } // namespace llvm
780
781 #endif
782