xref: /llvm-project/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h (revision fd93a5e3c06a90e931c645948aa73ee9894699d7)
1 //===- VPlanPatternMatch.h - Match on VPValues and recipes ------*- 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 //
9 // This file provides a simple and efficient mechanism for performing general
10 // tree-based pattern matches on the VPlan values and recipes, based on
11 // LLVM's IR pattern matchers.
12 //
13 // Currently it provides generic matchers for unary and binary VPInstructions,
14 // and specialized matchers like m_Not, m_ActiveLaneMask, m_BranchOnCond,
15 // m_BranchOnCount to match specific VPInstructions.
16 // TODO: Add missing matchers for additional opcodes and recipes as needed.
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #ifndef LLVM_TRANSFORM_VECTORIZE_VPLANPATTERNMATCH_H
21 #define LLVM_TRANSFORM_VECTORIZE_VPLANPATTERNMATCH_H
22 
23 #include "VPlan.h"
24 
25 namespace llvm {
26 namespace VPlanPatternMatch {
27 
28 template <typename Val, typename Pattern> bool match(Val *V, const Pattern &P) {
29   return const_cast<Pattern &>(P).match(V);
30 }
31 
32 template <typename Class> struct class_match {
33   template <typename ITy> bool match(ITy *V) { return isa<Class>(V); }
34 };
35 
36 /// Match an arbitrary VPValue and ignore it.
37 inline class_match<VPValue> m_VPValue() { return class_match<VPValue>(); }
38 
39 template <typename Class> struct bind_ty {
40   Class *&VR;
41 
42   bind_ty(Class *&V) : VR(V) {}
43 
44   template <typename ITy> bool match(ITy *V) {
45     if (auto *CV = dyn_cast<Class>(V)) {
46       VR = CV;
47       return true;
48     }
49     return false;
50   }
51 };
52 
53 /// Match a specified integer value or vector of all elements of that
54 /// value.
55 struct specific_intval {
56   APInt Val;
57 
58   specific_intval(APInt V) : Val(std::move(V)) {}
59 
60   bool match(VPValue *VPV) {
61     if (!VPV->isLiveIn())
62       return false;
63     Value *V = VPV->getLiveInIRValue();
64     const auto *CI = dyn_cast<ConstantInt>(V);
65     if (!CI && V->getType()->isVectorTy())
66       if (const auto *C = dyn_cast<Constant>(V))
67         CI = dyn_cast_or_null<ConstantInt>(
68             C->getSplatValue(/*UndefsAllowed=*/false));
69 
70     return CI && APInt::isSameValue(CI->getValue(), Val);
71   }
72 };
73 
74 inline specific_intval m_SpecificInt(uint64_t V) {
75   return specific_intval(APInt(64, V));
76 }
77 
78 /// Matching combinators
79 template <typename LTy, typename RTy> struct match_combine_or {
80   LTy L;
81   RTy R;
82 
83   match_combine_or(const LTy &Left, const RTy &Right) : L(Left), R(Right) {}
84 
85   template <typename ITy> bool match(ITy *V) {
86     if (L.match(V))
87       return true;
88     if (R.match(V))
89       return true;
90     return false;
91   }
92 };
93 
94 template <typename LTy, typename RTy>
95 inline match_combine_or<LTy, RTy> m_CombineOr(const LTy &L, const RTy &R) {
96   return match_combine_or<LTy, RTy>(L, R);
97 }
98 
99 /// Match a VPValue, capturing it if we match.
100 inline bind_ty<VPValue> m_VPValue(VPValue *&V) { return V; }
101 
102 namespace detail {
103 
104 /// A helper to match an opcode against multiple recipe types.
105 template <unsigned Opcode, typename...> struct MatchRecipeAndOpcode {};
106 
107 template <unsigned Opcode, typename RecipeTy>
108 struct MatchRecipeAndOpcode<Opcode, RecipeTy> {
109   static bool match(const VPRecipeBase *R) {
110     auto *DefR = dyn_cast<RecipeTy>(R);
111     return DefR && DefR->getOpcode() == Opcode;
112   }
113 };
114 
115 template <unsigned Opcode, typename RecipeTy, typename... RecipeTys>
116 struct MatchRecipeAndOpcode<Opcode, RecipeTy, RecipeTys...> {
117   static bool match(const VPRecipeBase *R) {
118     return MatchRecipeAndOpcode<Opcode, RecipeTy>::match(R) ||
119            MatchRecipeAndOpcode<Opcode, RecipeTys...>::match(R);
120   }
121 };
122 } // namespace detail
123 
124 template <typename Op0_t, unsigned Opcode, typename... RecipeTys>
125 struct UnaryRecipe_match {
126   Op0_t Op0;
127 
128   UnaryRecipe_match(Op0_t Op0) : Op0(Op0) {}
129 
130   bool match(const VPValue *V) {
131     auto *DefR = V->getDefiningRecipe();
132     return DefR && match(DefR);
133   }
134 
135   bool match(const VPRecipeBase *R) {
136     if (!detail::MatchRecipeAndOpcode<Opcode, RecipeTys...>::match(R))
137       return false;
138     assert(R->getNumOperands() == 1 &&
139            "recipe with matched opcode does not have 1 operands");
140     return Op0.match(R->getOperand(0));
141   }
142 };
143 
144 template <typename Op0_t, unsigned Opcode>
145 using UnaryVPInstruction_match =
146     UnaryRecipe_match<Op0_t, Opcode, VPInstruction>;
147 
148 template <typename Op0_t, unsigned Opcode>
149 using AllUnaryRecipe_match =
150     UnaryRecipe_match<Op0_t, Opcode, VPWidenRecipe, VPReplicateRecipe,
151                       VPWidenCastRecipe, VPInstruction>;
152 
153 template <typename Op0_t, typename Op1_t, unsigned Opcode,
154           typename... RecipeTys>
155 struct BinaryRecipe_match {
156   Op0_t Op0;
157   Op1_t Op1;
158 
159   BinaryRecipe_match(Op0_t Op0, Op1_t Op1) : Op0(Op0), Op1(Op1) {}
160 
161   bool match(const VPValue *V) {
162     auto *DefR = V->getDefiningRecipe();
163     return DefR && match(DefR);
164   }
165 
166   bool match(const VPSingleDefRecipe *R) {
167     return match(static_cast<const VPRecipeBase *>(R));
168   }
169 
170   bool match(const VPRecipeBase *R) {
171     if (!detail::MatchRecipeAndOpcode<Opcode, RecipeTys...>::match(R))
172       return false;
173     assert(R->getNumOperands() == 2 &&
174            "recipe with matched opcode does not have 2 operands");
175     return Op0.match(R->getOperand(0)) && Op1.match(R->getOperand(1));
176   }
177 };
178 
179 template <typename Op0_t, typename Op1_t, unsigned Opcode>
180 using BinaryVPInstruction_match =
181     BinaryRecipe_match<Op0_t, Op1_t, Opcode, VPInstruction>;
182 
183 template <typename Op0_t, typename Op1_t, unsigned Opcode>
184 using AllBinaryRecipe_match =
185     BinaryRecipe_match<Op0_t, Op1_t, Opcode, VPWidenRecipe, VPReplicateRecipe,
186                        VPWidenCastRecipe, VPInstruction>;
187 
188 template <unsigned Opcode, typename Op0_t>
189 inline UnaryVPInstruction_match<Op0_t, Opcode>
190 m_VPInstruction(const Op0_t &Op0) {
191   return UnaryVPInstruction_match<Op0_t, Opcode>(Op0);
192 }
193 
194 template <unsigned Opcode, typename Op0_t, typename Op1_t>
195 inline BinaryVPInstruction_match<Op0_t, Op1_t, Opcode>
196 m_VPInstruction(const Op0_t &Op0, const Op1_t &Op1) {
197   return BinaryVPInstruction_match<Op0_t, Op1_t, Opcode>(Op0, Op1);
198 }
199 
200 template <typename Op0_t>
201 inline UnaryVPInstruction_match<Op0_t, VPInstruction::Not>
202 m_Not(const Op0_t &Op0) {
203   return m_VPInstruction<VPInstruction::Not>(Op0);
204 }
205 
206 template <typename Op0_t>
207 inline UnaryVPInstruction_match<Op0_t, VPInstruction::BranchOnCond>
208 m_BranchOnCond(const Op0_t &Op0) {
209   return m_VPInstruction<VPInstruction::BranchOnCond>(Op0);
210 }
211 
212 template <typename Op0_t, typename Op1_t>
213 inline BinaryVPInstruction_match<Op0_t, Op1_t, VPInstruction::ActiveLaneMask>
214 m_ActiveLaneMask(const Op0_t &Op0, const Op1_t &Op1) {
215   return m_VPInstruction<VPInstruction::ActiveLaneMask>(Op0, Op1);
216 }
217 
218 template <typename Op0_t, typename Op1_t>
219 inline BinaryVPInstruction_match<Op0_t, Op1_t, VPInstruction::BranchOnCount>
220 m_BranchOnCount(const Op0_t &Op0, const Op1_t &Op1) {
221   return m_VPInstruction<VPInstruction::BranchOnCount>(Op0, Op1);
222 }
223 
224 template <unsigned Opcode, typename Op0_t>
225 inline AllUnaryRecipe_match<Op0_t, Opcode> m_Unary(const Op0_t &Op0) {
226   return AllUnaryRecipe_match<Op0_t, Opcode>(Op0);
227 }
228 
229 template <typename Op0_t>
230 inline AllUnaryRecipe_match<Op0_t, Instruction::Trunc>
231 m_Trunc(const Op0_t &Op0) {
232   return m_Unary<Instruction::Trunc, Op0_t>(Op0);
233 }
234 
235 template <typename Op0_t>
236 inline AllUnaryRecipe_match<Op0_t, Instruction::ZExt> m_ZExt(const Op0_t &Op0) {
237   return m_Unary<Instruction::ZExt, Op0_t>(Op0);
238 }
239 
240 template <typename Op0_t>
241 inline AllUnaryRecipe_match<Op0_t, Instruction::SExt> m_SExt(const Op0_t &Op0) {
242   return m_Unary<Instruction::SExt, Op0_t>(Op0);
243 }
244 
245 template <typename Op0_t>
246 inline match_combine_or<AllUnaryRecipe_match<Op0_t, Instruction::ZExt>,
247                         AllUnaryRecipe_match<Op0_t, Instruction::SExt>>
248 m_ZExtOrSExt(const Op0_t &Op0) {
249   return m_CombineOr(m_ZExt(Op0), m_SExt(Op0));
250 }
251 
252 template <unsigned Opcode, typename Op0_t, typename Op1_t>
253 inline AllBinaryRecipe_match<Op0_t, Op1_t, Opcode> m_Binary(const Op0_t &Op0,
254                                                             const Op1_t &Op1) {
255   return AllBinaryRecipe_match<Op0_t, Op1_t, Opcode>(Op0, Op1);
256 }
257 
258 template <typename Op0_t, typename Op1_t>
259 inline AllBinaryRecipe_match<Op0_t, Op1_t, Instruction::Mul>
260 m_Mul(const Op0_t &Op0, const Op1_t &Op1) {
261   return m_Binary<Instruction::Mul, Op0_t, Op1_t>(Op0, Op1);
262 }
263 
264 } // namespace VPlanPatternMatch
265 } // namespace llvm
266 
267 #endif
268