xref: /freebsd/contrib/llvm-project/llvm/include/llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.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 /// Interface for Targets to specify which operations they can successfully
10 /// select and how the others should be expanded most efficiently.
11 /// This implementation has been deprecated for a long time but it still in use
12 /// in a few places.
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H
16 #define LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H
17 
18 #include "llvm/ADT/DenseMap.h"
19 #include "llvm/CodeGen/TargetOpcodes.h"
20 #include "llvm/CodeGenTypes/LowLevelType.h"
21 #include "llvm/Support/Compiler.h"
22 #include <unordered_map>
23 #include <vector>
24 
25 namespace llvm {
26 struct LegalityQuery;
27 
28 namespace LegacyLegalizeActions {
29 enum LegacyLegalizeAction : std::uint8_t {
30   /// The operation is expected to be selectable directly by the target, and
31   /// no transformation is necessary.
32   Legal,
33 
34   /// The operation should be synthesized from multiple instructions acting on
35   /// a narrower scalar base-type. For example a 64-bit add might be
36   /// implemented in terms of 32-bit add-with-carry.
37   NarrowScalar,
38 
39   /// The operation should be implemented in terms of a wider scalar
40   /// base-type. For example a <2 x s8> add could be implemented as a <2
41   /// x s32> add (ignoring the high bits).
42   WidenScalar,
43 
44   /// The (vector) operation should be implemented by splitting it into
45   /// sub-vectors where the operation is legal. For example a <8 x s64> add
46   /// might be implemented as 4 separate <2 x s64> adds.
47   FewerElements,
48 
49   /// The (vector) operation should be implemented by widening the input
50   /// vector and ignoring the lanes added by doing so. For example <2 x i8> is
51   /// rarely legal, but you might perform an <8 x i8> and then only look at
52   /// the first two results.
53   MoreElements,
54 
55   /// Perform the operation on a different, but equivalently sized type.
56   Bitcast,
57 
58   /// The operation itself must be expressed in terms of simpler actions on
59   /// this target. E.g. a SREM replaced by an SDIV and subtraction.
60   Lower,
61 
62   /// The operation should be implemented as a call to some kind of runtime
63   /// support library. For example this usually happens on machines that don't
64   /// support floating-point operations natively.
65   Libcall,
66 
67   /// The target wants to do something special with this combination of
68   /// operand and type. A callback will be issued when it is needed.
69   Custom,
70 
71   /// This operation is completely unsupported on the target. A programming
72   /// error has occurred.
73   Unsupported,
74 
75   /// Sentinel value for when no action was found in the specified table.
76   NotFound,
77 };
78 } // end namespace LegacyLegalizeActions
79 LLVM_ABI raw_ostream &
80 operator<<(raw_ostream &OS, LegacyLegalizeActions::LegacyLegalizeAction Action);
81 
82 /// Legalization is decided based on an instruction's opcode, which type slot
83 /// we're considering, and what the existing type is. These aspects are gathered
84 /// together for convenience in the InstrAspect class.
85 struct InstrAspect {
86   unsigned Opcode;
87   unsigned Idx = 0;
88   LLT Type;
89 
InstrAspectInstrAspect90   InstrAspect(unsigned Opcode, LLT Type) : Opcode(Opcode), Type(Type) {}
InstrAspectInstrAspect91   InstrAspect(unsigned Opcode, unsigned Idx, LLT Type)
92       : Opcode(Opcode), Idx(Idx), Type(Type) {}
93 
94   bool operator==(const InstrAspect &RHS) const {
95     return Opcode == RHS.Opcode && Idx == RHS.Idx && Type == RHS.Type;
96   }
97 };
98 
99 /// The result of a query. It either indicates a final answer of Legal or
100 /// Unsupported or describes an action that must be taken to make an operation
101 /// more legal.
102 struct LegacyLegalizeActionStep {
103   /// The action to take or the final answer.
104   LegacyLegalizeActions::LegacyLegalizeAction Action;
105   /// If describing an action, the type index to change. Otherwise zero.
106   unsigned TypeIdx;
107   /// If describing an action, the new type for TypeIdx. Otherwise LLT{}.
108   LLT NewType;
109 
LegacyLegalizeActionStepLegacyLegalizeActionStep110   LegacyLegalizeActionStep(LegacyLegalizeActions::LegacyLegalizeAction Action,
111                            unsigned TypeIdx, const LLT NewType)
112       : Action(Action), TypeIdx(TypeIdx), NewType(NewType) {}
113 
114   bool operator==(const LegacyLegalizeActionStep &RHS) const {
115     return std::tie(Action, TypeIdx, NewType) ==
116         std::tie(RHS.Action, RHS.TypeIdx, RHS.NewType);
117   }
118 };
119 
120 
121 class LegacyLegalizerInfo {
122 public:
123   using SizeAndAction =
124       std::pair<uint16_t, LegacyLegalizeActions::LegacyLegalizeAction>;
125   using SizeAndActionsVec = std::vector<SizeAndAction>;
126   using SizeChangeStrategy =
127       std::function<SizeAndActionsVec(const SizeAndActionsVec &v)>;
128 
129   LLVM_ABI LegacyLegalizerInfo();
130 
needsLegalizingToDifferentSize(const LegacyLegalizeActions::LegacyLegalizeAction Action)131   static bool needsLegalizingToDifferentSize(
132       const LegacyLegalizeActions::LegacyLegalizeAction Action) {
133     using namespace LegacyLegalizeActions;
134     switch (Action) {
135     case NarrowScalar:
136     case WidenScalar:
137     case FewerElements:
138     case MoreElements:
139     case Unsupported:
140       return true;
141     default:
142       return false;
143     }
144   }
145 
146   /// Compute any ancillary tables needed to quickly decide how an operation
147   /// should be handled. This must be called after all "set*Action"methods but
148   /// before any query is made or incorrect results may be returned.
149   LLVM_ABI void computeTables();
150 
151   /// More friendly way to set an action for common types that have an LLT
152   /// representation.
153   /// The LegacyLegalizeAction must be one for which
154   /// NeedsLegalizingToDifferentSize returns false.
setAction(const InstrAspect & Aspect,LegacyLegalizeActions::LegacyLegalizeAction Action)155   void setAction(const InstrAspect &Aspect,
156                  LegacyLegalizeActions::LegacyLegalizeAction Action) {
157     assert(!needsLegalizingToDifferentSize(Action));
158     TablesInitialized = false;
159     const unsigned OpcodeIdx = Aspect.Opcode - FirstOp;
160     if (SpecifiedActions[OpcodeIdx].size() <= Aspect.Idx)
161       SpecifiedActions[OpcodeIdx].resize(Aspect.Idx + 1);
162     SpecifiedActions[OpcodeIdx][Aspect.Idx][Aspect.Type] = Action;
163   }
164 
165   /// The setAction calls record the non-size-changing legalization actions
166   /// to take on specificly-sized types. The SizeChangeStrategy defines what
167   /// to do when the size of the type needs to be changed to reach a legally
168   /// sized type (i.e., one that was defined through a setAction call).
169   /// e.g.
170   /// setAction ({G_ADD, 0, LLT::scalar(32)}, Legal);
171   /// setLegalizeScalarToDifferentSizeStrategy(
172   ///   G_ADD, 0, widenToLargerTypesAndNarrowToLargest);
173   /// will end up defining getAction({G_ADD, 0, T}) to return the following
174   /// actions for different scalar types T:
175   ///  LLT::scalar(1)..LLT::scalar(31): {WidenScalar, 0, LLT::scalar(32)}
176   ///  LLT::scalar(32):                 {Legal, 0, LLT::scalar(32)}
177   ///  LLT::scalar(33)..:               {NarrowScalar, 0, LLT::scalar(32)}
178   ///
179   /// If no SizeChangeAction gets defined, through this function,
180   /// the default is unsupportedForDifferentSizes.
setLegalizeScalarToDifferentSizeStrategy(const unsigned Opcode,const unsigned TypeIdx,SizeChangeStrategy S)181   void setLegalizeScalarToDifferentSizeStrategy(const unsigned Opcode,
182                                                 const unsigned TypeIdx,
183                                                 SizeChangeStrategy S) {
184     const unsigned OpcodeIdx = Opcode - FirstOp;
185     if (ScalarSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
186       ScalarSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
187     ScalarSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
188   }
189 
190   /// See also setLegalizeScalarToDifferentSizeStrategy.
191   /// This function allows to set the SizeChangeStrategy for vector elements.
setLegalizeVectorElementToDifferentSizeStrategy(const unsigned Opcode,const unsigned TypeIdx,SizeChangeStrategy S)192   void setLegalizeVectorElementToDifferentSizeStrategy(const unsigned Opcode,
193                                                        const unsigned TypeIdx,
194                                                        SizeChangeStrategy S) {
195     const unsigned OpcodeIdx = Opcode - FirstOp;
196     if (VectorElementSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
197       VectorElementSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
198     VectorElementSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
199   }
200 
201   /// A SizeChangeStrategy for the common case where legalization for a
202   /// particular operation consists of only supporting a specific set of type
203   /// sizes. E.g.
204   ///   setAction ({G_DIV, 0, LLT::scalar(32)}, Legal);
205   ///   setAction ({G_DIV, 0, LLT::scalar(64)}, Legal);
206   ///   setLegalizeScalarToDifferentSizeStrategy(
207   ///     G_DIV, 0, unsupportedForDifferentSizes);
208   /// will result in getAction({G_DIV, 0, T}) to return Legal for s32 and s64,
209   /// and Unsupported for all other scalar types T.
210   static SizeAndActionsVec
unsupportedForDifferentSizes(const SizeAndActionsVec & v)211   unsupportedForDifferentSizes(const SizeAndActionsVec &v) {
212     using namespace LegacyLegalizeActions;
213     return increaseToLargerTypesAndDecreaseToLargest(v, Unsupported,
214                                                      Unsupported);
215   }
216 
217   /// A SizeChangeStrategy for the common case where legalization for a
218   /// particular operation consists of widening the type to a large legal type,
219   /// unless there is no such type and then instead it should be narrowed to the
220   /// largest legal type.
221   static SizeAndActionsVec
widenToLargerTypesAndNarrowToLargest(const SizeAndActionsVec & v)222   widenToLargerTypesAndNarrowToLargest(const SizeAndActionsVec &v) {
223     using namespace LegacyLegalizeActions;
224     assert(v.size() > 0 &&
225            "At least one size that can be legalized towards is needed"
226            " for this SizeChangeStrategy");
227     return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
228                                                      NarrowScalar);
229   }
230 
231   static SizeAndActionsVec
widenToLargerTypesUnsupportedOtherwise(const SizeAndActionsVec & v)232   widenToLargerTypesUnsupportedOtherwise(const SizeAndActionsVec &v) {
233     using namespace LegacyLegalizeActions;
234     return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
235                                                      Unsupported);
236   }
237 
238   static SizeAndActionsVec
narrowToSmallerAndUnsupportedIfTooSmall(const SizeAndActionsVec & v)239   narrowToSmallerAndUnsupportedIfTooSmall(const SizeAndActionsVec &v) {
240     using namespace LegacyLegalizeActions;
241     return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
242                                                        Unsupported);
243   }
244 
245   /// A SizeChangeStrategy for the common case where legalization for a
246   /// particular vector operation consists of having more elements in the
247   /// vector, to a type that is legal. Unless there is no such type and then
248   /// instead it should be legalized towards the widest vector that's still
249   /// legal. E.g.
250   ///   setAction({G_ADD, LLT::vector(8, 8)}, Legal);
251   ///   setAction({G_ADD, LLT::vector(16, 8)}, Legal);
252   ///   setAction({G_ADD, LLT::vector(2, 32)}, Legal);
253   ///   setAction({G_ADD, LLT::vector(4, 32)}, Legal);
254   ///   setLegalizeVectorElementToDifferentSizeStrategy(
255   ///     G_ADD, 0, moreToWiderTypesAndLessToWidest);
256   /// will result in the following getAction results:
257   ///   * getAction({G_ADD, LLT::vector(8,8)}) returns
258   ///       (Legal, vector(8,8)).
259   ///   * getAction({G_ADD, LLT::vector(9,8)}) returns
260   ///       (MoreElements, vector(16,8)).
261   ///   * getAction({G_ADD, LLT::vector(8,32)}) returns
262   ///       (FewerElements, vector(4,32)).
263   static SizeAndActionsVec
moreToWiderTypesAndLessToWidest(const SizeAndActionsVec & v)264   moreToWiderTypesAndLessToWidest(const SizeAndActionsVec &v) {
265     using namespace LegacyLegalizeActions;
266     return increaseToLargerTypesAndDecreaseToLargest(v, MoreElements,
267                                                      FewerElements);
268   }
269 
270   /// Helper function to implement many typical SizeChangeStrategy functions.
271   LLVM_ABI static SizeAndActionsVec increaseToLargerTypesAndDecreaseToLargest(
272       const SizeAndActionsVec &v,
273       LegacyLegalizeActions::LegacyLegalizeAction IncreaseAction,
274       LegacyLegalizeActions::LegacyLegalizeAction DecreaseAction);
275   /// Helper function to implement many typical SizeChangeStrategy functions.
276   LLVM_ABI static SizeAndActionsVec decreaseToSmallerTypesAndIncreaseToSmallest(
277       const SizeAndActionsVec &v,
278       LegacyLegalizeActions::LegacyLegalizeAction DecreaseAction,
279       LegacyLegalizeActions::LegacyLegalizeAction IncreaseAction);
280 
281   LLVM_ABI LegacyLegalizeActionStep getAction(const LegalityQuery &Query) const;
282 
283   LLVM_ABI unsigned getOpcodeIdxForOpcode(unsigned Opcode) const;
284 
285 private:
286   /// Determine what action should be taken to legalize the given generic
287   /// instruction opcode, type-index and type. Requires computeTables to have
288   /// been called.
289   ///
290   /// \returns a pair consisting of the kind of legalization that should be
291   /// performed and the destination type.
292   std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
293   getAspectAction(const InstrAspect &Aspect) const;
294 
295   /// The SizeAndActionsVec is a representation mapping between all natural
296   /// numbers and an Action. The natural number represents the bit size of
297   /// the InstrAspect. For example, for a target with native support for 32-bit
298   /// and 64-bit additions, you'd express that as:
299   /// setScalarAction(G_ADD, 0,
300   ///           {{1, WidenScalar},  // bit sizes [ 1, 31[
301   ///            {32, Legal},       // bit sizes [32, 33[
302   ///            {33, WidenScalar}, // bit sizes [33, 64[
303   ///            {64, Legal},       // bit sizes [64, 65[
304   ///            {65, NarrowScalar} // bit sizes [65, +inf[
305   ///           });
306   /// It may be that only 64-bit pointers are supported on your target:
307   /// setPointerAction(G_PTR_ADD, 0, LLT:pointer(1),
308   ///           {{1, Unsupported},  // bit sizes [ 1, 63[
309   ///            {64, Legal},       // bit sizes [64, 65[
310   ///            {65, Unsupported}, // bit sizes [65, +inf[
311   ///           });
setScalarAction(const unsigned Opcode,const unsigned TypeIndex,const SizeAndActionsVec & SizeAndActions)312   void setScalarAction(const unsigned Opcode, const unsigned TypeIndex,
313                        const SizeAndActionsVec &SizeAndActions) {
314     const unsigned OpcodeIdx = Opcode - FirstOp;
315     SmallVector<SizeAndActionsVec, 1> &Actions = ScalarActions[OpcodeIdx];
316     setActions(TypeIndex, Actions, SizeAndActions);
317   }
setPointerAction(const unsigned Opcode,const unsigned TypeIndex,const unsigned AddressSpace,const SizeAndActionsVec & SizeAndActions)318   void setPointerAction(const unsigned Opcode, const unsigned TypeIndex,
319                         const unsigned AddressSpace,
320                         const SizeAndActionsVec &SizeAndActions) {
321     const unsigned OpcodeIdx = Opcode - FirstOp;
322     SmallVector<SizeAndActionsVec, 1> &Actions =
323         AddrSpace2PointerActions[OpcodeIdx][AddressSpace];
324     setActions(TypeIndex, Actions, SizeAndActions);
325   }
326 
327   /// If an operation on a given vector type (say <M x iN>) isn't explicitly
328   /// specified, we proceed in 2 stages. First we legalize the underlying scalar
329   /// (so that there's at least one legal vector with that scalar), then we
330   /// adjust the number of elements in the vector so that it is legal. The
331   /// desired action in the first step is controlled by this function.
setScalarInVectorAction(const unsigned Opcode,const unsigned TypeIndex,const SizeAndActionsVec & SizeAndActions)332   void setScalarInVectorAction(const unsigned Opcode, const unsigned TypeIndex,
333                                const SizeAndActionsVec &SizeAndActions) {
334     unsigned OpcodeIdx = Opcode - FirstOp;
335     SmallVector<SizeAndActionsVec, 1> &Actions =
336         ScalarInVectorActions[OpcodeIdx];
337     setActions(TypeIndex, Actions, SizeAndActions);
338   }
339 
340   /// See also setScalarInVectorAction.
341   /// This function let's you specify the number of elements in a vector that
342   /// are legal for a legal element size.
setVectorNumElementAction(const unsigned Opcode,const unsigned TypeIndex,const unsigned ElementSize,const SizeAndActionsVec & SizeAndActions)343   void setVectorNumElementAction(const unsigned Opcode,
344                                  const unsigned TypeIndex,
345                                  const unsigned ElementSize,
346                                  const SizeAndActionsVec &SizeAndActions) {
347     const unsigned OpcodeIdx = Opcode - FirstOp;
348     SmallVector<SizeAndActionsVec, 1> &Actions =
349         NumElements2Actions[OpcodeIdx][ElementSize];
350     setActions(TypeIndex, Actions, SizeAndActions);
351   }
352 
353   /// A partial SizeAndActionsVec potentially doesn't cover all bit sizes,
354   /// i.e. it's OK if it doesn't start from size 1.
checkPartialSizeAndActionsVector(const SizeAndActionsVec & v)355   static void checkPartialSizeAndActionsVector(const SizeAndActionsVec& v) {
356     using namespace LegacyLegalizeActions;
357 #ifndef NDEBUG
358     // The sizes should be in increasing order
359     int prev_size = -1;
360     for(auto SizeAndAction: v) {
361       assert(SizeAndAction.first > prev_size);
362       prev_size = SizeAndAction.first;
363     }
364     // - for every Widen action, there should be a larger bitsize that
365     //   can be legalized towards (e.g. Legal, Lower, Libcall or Custom
366     //   action).
367     // - for every Narrow action, there should be a smaller bitsize that
368     //   can be legalized towards.
369     int SmallestNarrowIdx = -1;
370     int LargestWidenIdx = -1;
371     int SmallestLegalizableToSameSizeIdx = -1;
372     int LargestLegalizableToSameSizeIdx = -1;
373     for(size_t i=0; i<v.size(); ++i) {
374       switch (v[i].second) {
375         case FewerElements:
376         case NarrowScalar:
377           if (SmallestNarrowIdx == -1)
378             SmallestNarrowIdx = i;
379           break;
380         case WidenScalar:
381         case MoreElements:
382           LargestWidenIdx = i;
383           break;
384         case Unsupported:
385           break;
386         default:
387           if (SmallestLegalizableToSameSizeIdx == -1)
388             SmallestLegalizableToSameSizeIdx = i;
389           LargestLegalizableToSameSizeIdx = i;
390       }
391     }
392     if (SmallestNarrowIdx != -1) {
393       assert(SmallestLegalizableToSameSizeIdx != -1);
394       assert(SmallestNarrowIdx > SmallestLegalizableToSameSizeIdx);
395     }
396     if (LargestWidenIdx != -1)
397       assert(LargestWidenIdx < LargestLegalizableToSameSizeIdx);
398 #endif
399   }
400 
401   /// A full SizeAndActionsVec must cover all bit sizes, i.e. must start with
402   /// from size 1.
checkFullSizeAndActionsVector(const SizeAndActionsVec & v)403   static void checkFullSizeAndActionsVector(const SizeAndActionsVec& v) {
404 #ifndef NDEBUG
405     // Data structure invariant: The first bit size must be size 1.
406     assert(v.size() >= 1);
407     assert(v[0].first == 1);
408     checkPartialSizeAndActionsVector(v);
409 #endif
410   }
411 
412   /// Sets actions for all bit sizes on a particular generic opcode, type
413   /// index and scalar or pointer type.
setActions(unsigned TypeIndex,SmallVector<SizeAndActionsVec,1> & Actions,const SizeAndActionsVec & SizeAndActions)414   void setActions(unsigned TypeIndex,
415                   SmallVector<SizeAndActionsVec, 1> &Actions,
416                   const SizeAndActionsVec &SizeAndActions) {
417     checkFullSizeAndActionsVector(SizeAndActions);
418     if (Actions.size() <= TypeIndex)
419       Actions.resize(TypeIndex + 1);
420     Actions[TypeIndex] = SizeAndActions;
421   }
422 
423   static SizeAndAction findAction(const SizeAndActionsVec &Vec,
424                                   const uint32_t Size);
425 
426   /// Returns the next action needed to get the scalar or pointer type closer
427   /// to being legal
428   /// E.g. findLegalAction({G_REM, 13}) should return
429   /// (WidenScalar, 32). After that, findLegalAction({G_REM, 32}) will
430   /// probably be called, which should return (Lower, 32).
431   /// This is assuming the setScalarAction on G_REM was something like:
432   /// setScalarAction(G_REM, 0,
433   ///           {{1, WidenScalar},  // bit sizes [ 1, 31[
434   ///            {32, Lower},       // bit sizes [32, 33[
435   ///            {33, NarrowScalar} // bit sizes [65, +inf[
436   ///           });
437   std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
438   findScalarLegalAction(const InstrAspect &Aspect) const;
439 
440   /// Returns the next action needed towards legalizing the vector type.
441   std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
442   findVectorLegalAction(const InstrAspect &Aspect) const;
443 
444   static const int FirstOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_START;
445   static const int LastOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
446 
447   // Data structures used temporarily during construction of legality data:
448   using TypeMap = DenseMap<LLT, LegacyLegalizeActions::LegacyLegalizeAction>;
449   SmallVector<TypeMap, 1> SpecifiedActions[LastOp - FirstOp + 1];
450   SmallVector<SizeChangeStrategy, 1>
451       ScalarSizeChangeStrategies[LastOp - FirstOp + 1];
452   SmallVector<SizeChangeStrategy, 1>
453       VectorElementSizeChangeStrategies[LastOp - FirstOp + 1];
454   bool TablesInitialized = false;
455 
456   // Data structures used by getAction:
457   SmallVector<SizeAndActionsVec, 1> ScalarActions[LastOp - FirstOp + 1];
458   SmallVector<SizeAndActionsVec, 1> ScalarInVectorActions[LastOp - FirstOp + 1];
459   std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
460       AddrSpace2PointerActions[LastOp - FirstOp + 1];
461   std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
462       NumElements2Actions[LastOp - FirstOp + 1];
463 };
464 
465 } // end namespace llvm
466 
467 #endif // LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H
468