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