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> |
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> |
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; |
39 | OneUse_match(const SubPatternT &SP) : SubPat(SP) {} |
40 | |
41 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
42 | return MRI.hasOneUse(RegNo: Reg) && SubPat.match(MRI, Reg); |
43 | } |
44 | }; |
45 | |
46 | template <typename SubPat> |
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; |
53 | OneNonDBGUse_match(const SubPatternT &SP) : SubPat(SP) {} |
54 | |
55 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
56 | return MRI.hasOneNonDBGUse(RegNo: Reg) && SubPat.match(MRI, Reg); |
57 | } |
58 | }; |
59 | |
60 | template <typename SubPat> |
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 <> |
70 | inline std::optional<APInt> matchConstant(Register Reg, |
71 | const MachineRegisterInfo &MRI) { |
72 | return getIConstantVRegVal(VReg: Reg, MRI); |
73 | } |
74 | |
75 | template <> |
76 | inline std::optional<int64_t> matchConstant(Register Reg, |
77 | const MachineRegisterInfo &MRI) { |
78 | return getIConstantVRegSExtVal(VReg: Reg, MRI); |
79 | } |
80 | |
81 | template <typename ConstT> struct ConstantMatch { |
82 | ConstT &CR; |
83 | ConstantMatch(ConstT &C) : CR(C) {} |
84 | 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 | |
93 | inline ConstantMatch<APInt> m_ICst(APInt &Cst) { |
94 | return ConstantMatch<APInt>(Cst); |
95 | } |
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 <> |
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> |
112 | matchConstantSplat(Register Reg, const MachineRegisterInfo &MRI) { |
113 | return getIConstantSplatSExtVal(Reg, MRI); |
114 | } |
115 | |
116 | template <typename ConstT> struct ICstOrSplatMatch { |
117 | ConstT &CR; |
118 | ICstOrSplatMatch(ConstT &C) : CR(C) {} |
119 | 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 | |
134 | inline ICstOrSplatMatch<APInt> m_ICstOrSplat(APInt &Cst) { |
135 | return ICstOrSplatMatch<APInt>(Cst); |
136 | } |
137 | |
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; |
144 | GCstAndRegMatch(std::optional<ValueAndVReg> &ValReg) : ValReg(ValReg) {} |
145 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
146 | ValReg = getIConstantVRegValWithLookThrough(VReg: Reg, MRI); |
147 | return ValReg ? true : false; |
148 | } |
149 | }; |
150 | |
151 | inline GCstAndRegMatch m_GCst(std::optional<ValueAndVReg> &ValReg) { |
152 | return GCstAndRegMatch(ValReg); |
153 | } |
154 | |
155 | struct GFCstAndRegMatch { |
156 | std::optional<FPValueAndVReg> &FPValReg; |
157 | GFCstAndRegMatch(std::optional<FPValueAndVReg> &FPValReg) |
158 | : FPValReg(FPValReg) {} |
159 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
160 | FPValReg = getFConstantVRegValWithLookThrough(VReg: Reg, MRI); |
161 | return FPValReg ? true : false; |
162 | } |
163 | }; |
164 | |
165 | inline GFCstAndRegMatch m_GFCst(std::optional<FPValueAndVReg> &FPValReg) { |
166 | return GFCstAndRegMatch(FPValReg); |
167 | } |
168 | |
169 | struct GFCstOrSplatGFCstMatch { |
170 | std::optional<FPValueAndVReg> &FPValReg; |
171 | GFCstOrSplatGFCstMatch(std::optional<FPValueAndVReg> &FPValReg) |
172 | : FPValReg(FPValReg) {} |
173 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
174 | return (FPValReg = getFConstantSplat(VReg: Reg, MRI)) || |
175 | (FPValReg = getFConstantVRegValWithLookThrough(VReg: Reg, MRI)); |
176 | }; |
177 | }; |
178 | |
179 | inline GFCstOrSplatGFCstMatch |
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; |
187 | SpecificConstantMatch(int64_t RequestedVal) : RequestedVal(RequestedVal) {} |
188 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
189 | int64_t MatchedVal; |
190 | return mi_match(R: Reg, MRI, P: m_ICst(Cst&: MatchedVal)) && MatchedVal == RequestedVal; |
191 | } |
192 | }; |
193 | |
194 | /// Matches a constant equal to \p 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; |
202 | SpecificConstantSplatMatch(int64_t RequestedVal) |
203 | : RequestedVal(RequestedVal) {} |
204 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
205 | return isBuildVectorConstantSplat(Reg, MRI, SplatValue: RequestedVal, |
206 | /* AllowUndef */ AllowUndef: false); |
207 | } |
208 | }; |
209 | |
210 | /// Matches a constant splat of \p 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; |
218 | SpecificConstantOrSplatMatch(int64_t RequestedVal) |
219 | : RequestedVal(RequestedVal) {} |
220 | bool match(const MachineRegisterInfo &MRI, Register Reg) { |
221 | int64_t MatchedVal; |
222 | if (mi_match(R: Reg, MRI, P: m_ICst(Cst&: MatchedVal)) && MatchedVal == RequestedVal) |
223 | return true; |
224 | return isBuildVectorConstantSplat(Reg, MRI, SplatValue: RequestedVal, |
225 | /* AllowUndef */ AllowUndef: false); |
226 | } |
227 | }; |
228 | |
229 | /// Matches a \p RequestedValue constant or a constant splat of \p |
230 | /// RequestedValue. |
231 | inline SpecificConstantOrSplatMatch |
232 | m_SpecificICstOrSplat(int64_t RequestedValue) { |
233 | return SpecificConstantOrSplatMatch(RequestedValue); |
234 | } |
235 | |
236 | ///{ |
237 | /// Convenience matchers for specific integer values. |
238 | inline SpecificConstantMatch m_ZeroInt() { return SpecificConstantMatch(0); } |
239 | inline SpecificConstantMatch m_AllOnesInt() { |
240 | return SpecificConstantMatch(-1); |
241 | } |
242 | ///} |
243 | |
244 | /// Matcher for a specific register. |
245 | struct SpecificRegisterMatch { |
246 | Register RequestedReg; |
247 | SpecificRegisterMatch(Register RequestedReg) : RequestedReg(RequestedReg) {} |
248 | 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. |
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 { |
264 | bool match(const MachineRegisterInfo &MRI, Register Reg) { return true; } |
265 | bool match(const MachineRegisterInfo &MRI, MachineOperand *MO) { |
266 | return MO->isReg(); |
267 | } |
268 | }; |
269 | |
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> |
275 | 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(VReg: 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(R: Reg, MRI, P: m_MInstr(MI&: 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(MI&: TmpMI))) { |
401 | if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 3) { |
402 | return (L.match(MRI, TmpMI->getOperand(i: 1).getReg()) && |
403 | R.match(MRI, TmpMI->getOperand(i: 2).getReg())) || |
404 | (Commutable && (R.match(MRI, TmpMI->getOperand(i: 1).getReg()) && |
405 | L.match(MRI, TmpMI->getOperand(i: 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(MI&: TmpMI))) { |
425 | if (TmpMI->getOpcode() == Opc && TmpMI->getNumDefs() == 1 && |
426 | TmpMI->getNumOperands() == 3) { |
427 | return (L.match(MRI, TmpMI->getOperand(i: 1).getReg()) && |
428 | R.match(MRI, TmpMI->getOperand(i: 2).getReg())) || |
429 | (Commutable && (R.match(MRI, TmpMI->getOperand(i: 1).getReg()) && |
430 | L.match(MRI, TmpMI->getOperand(i: 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(MI&: TmpMI))) { |
561 | if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 2) { |
562 | return L.match(MRI, TmpMI->getOperand(i: 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(MI&: TmpMI)) || TmpMI->getOpcode() != Opcode) |
655 | return false; |
656 | |
657 | auto TmpPred = |
658 | static_cast<CmpInst::Predicate>(TmpMI->getOperand(i: 1).getPredicate()); |
659 | if (!P.match(MRI, TmpPred)) |
660 | return false; |
661 | Register LHS = TmpMI->getOperand(i: 2).getReg(); |
662 | Register RHS = TmpMI->getOperand(i: 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(MI&: TmpMI))) { |
738 | if (TmpMI->getOpcode() == Opcode && TmpMI->getNumOperands() == 4) { |
739 | return (Src0.match(MRI, TmpMI->getOperand(i: 1).getReg()) && |
740 | Src1.match(MRI, TmpMI->getOperand(i: 2).getReg()) && |
741 | Src2.match(MRI, TmpMI->getOperand(i: 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 | |