aboutsummaryrefslogtreecommitdiff
path: root/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h
blob: b229411c814874a1556717a8da2cb87bece9e582 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
//===- llvm/CodeGen/GlobalISel/LegalizerInfo.h ------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
/// Interface for Targets to specify which operations they can successfully
/// select and how the others should be expanded most efficiently.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H
#define LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/LowLevelTypeImpl.h"
#include "llvm/Target/TargetOpcodes.h"
#include <cstdint>
#include <cassert>
#include <tuple>
#include <utility>

namespace llvm {

class MachineInstr;
class MachineIRBuilder;
class MachineRegisterInfo;

/// Legalization is decided based on an instruction's opcode, which type slot
/// we're considering, and what the existing type is. These aspects are gathered
/// together for convenience in the InstrAspect class.
struct InstrAspect {
  unsigned Opcode;
  unsigned Idx = 0;
  LLT Type;

  InstrAspect(unsigned Opcode, LLT Type) : Opcode(Opcode), Type(Type) {}
  InstrAspect(unsigned Opcode, unsigned Idx, LLT Type)
      : Opcode(Opcode), Idx(Idx), Type(Type) {}

  bool operator==(const InstrAspect &RHS) const {
    return Opcode == RHS.Opcode && Idx == RHS.Idx && Type == RHS.Type;
  }
};

class LegalizerInfo {
public:
  enum LegalizeAction : std::uint8_t {
    /// The operation is expected to be selectable directly by the target, and
    /// no transformation is necessary.
    Legal,

    /// The operation should be synthesized from multiple instructions acting on
    /// a narrower scalar base-type. For example a 64-bit add might be
    /// implemented in terms of 32-bit add-with-carry.
    NarrowScalar,

    /// The operation should be implemented in terms of a wider scalar
    /// base-type. For example a <2 x s8> add could be implemented as a <2
    /// x s32> add (ignoring the high bits).
    WidenScalar,

    /// The (vector) operation should be implemented by splitting it into
    /// sub-vectors where the operation is legal. For example a <8 x s64> add
    /// might be implemented as 4 separate <2 x s64> adds.
    FewerElements,

    /// The (vector) operation should be implemented by widening the input
    /// vector and ignoring the lanes added by doing so. For example <2 x i8> is
    /// rarely legal, but you might perform an <8 x i8> and then only look at
    /// the first two results.
    MoreElements,

    /// The operation itself must be expressed in terms of simpler actions on
    /// this target. E.g. a SREM replaced by an SDIV and subtraction.
    Lower,

    /// The operation should be implemented as a call to some kind of runtime
    /// support library. For example this usually happens on machines that don't
    /// support floating-point operations natively.
    Libcall,

    /// The target wants to do something special with this combination of
    /// operand and type. A callback will be issued when it is needed.
    Custom,

    /// This operation is completely unsupported on the target. A programming
    /// error has occurred.
    Unsupported,

    /// Sentinel value for when no action was found in the specified table.
    NotFound,
  };

  LegalizerInfo();
  virtual ~LegalizerInfo() = default;

  /// Compute any ancillary tables needed to quickly decide how an operation
  /// should be handled. This must be called after all "set*Action"methods but
  /// before any query is made or incorrect results may be returned.
  void computeTables();

  static bool needsLegalizingToDifferentSize(const LegalizeAction Action) {
    switch (Action) {
    case NarrowScalar:
    case WidenScalar:
    case FewerElements:
    case MoreElements:
    case Unsupported:
      return true;
    default:
      return false;
    }
  }

  /// More friendly way to set an action for common types that have an LLT
  /// representation.
  void setAction(const InstrAspect &Aspect, LegalizeAction Action) {
    TablesInitialized = false;
    unsigned Opcode = Aspect.Opcode - FirstOp;
    if (Actions[Opcode].size() <= Aspect.Idx)
      Actions[Opcode].resize(Aspect.Idx + 1);
    Actions[Aspect.Opcode - FirstOp][Aspect.Idx][Aspect.Type] = Action;
  }

  /// If an operation on a given vector type (say <M x iN>) isn't explicitly
  /// specified, we proceed in 2 stages. First we legalize the underlying scalar
  /// (so that there's at least one legal vector with that scalar), then we
  /// adjust the number of elements in the vector so that it is legal. The
  /// desired action in the first step is controlled by this function.
  void setScalarInVectorAction(unsigned Opcode, LLT ScalarTy,
                               LegalizeAction Action) {
    assert(!ScalarTy.isVector());
    ScalarInVectorActions[std::make_pair(Opcode, ScalarTy)] = Action;
  }

  /// Determine what action should be taken to legalize the given generic
  /// instruction opcode, type-index and type. Requires computeTables to have
  /// been called.
  ///
  /// \returns a pair consisting of the kind of legalization that should be
  /// performed and the destination type.
  std::pair<LegalizeAction, LLT> getAction(const InstrAspect &Aspect) const;

  /// Determine what action should be taken to legalize the given generic
  /// instruction.
  ///
  /// \returns a tuple consisting of the LegalizeAction that should be
  /// performed, the type-index it should be performed on and the destination
  /// type.
  std::tuple<LegalizeAction, unsigned, LLT>
  getAction(const MachineInstr &MI, const MachineRegisterInfo &MRI) const;

  /// Iterate the given function (typically something like doubling the width)
  /// on Ty until we find a legal type for this operation.
  Optional<LLT> findLegalizableSize(const InstrAspect &Aspect,
                                    function_ref<LLT(LLT)> NextType) const {
    if (Aspect.Idx >= Actions[Aspect.Opcode - FirstOp].size())
      return None;

    LegalizeAction Action;
    const TypeMap &Map = Actions[Aspect.Opcode - FirstOp][Aspect.Idx];
    LLT Ty = Aspect.Type;
    do {
      Ty = NextType(Ty);
      auto ActionIt = Map.find(Ty);
      if (ActionIt == Map.end()) {
        auto DefaultIt = DefaultActions.find(Aspect.Opcode);
        if (DefaultIt == DefaultActions.end())
          return None;
        Action = DefaultIt->second;
      } else
        Action = ActionIt->second;
    } while (needsLegalizingToDifferentSize(Action));
    return Ty;
  }

  /// Find what type it's actually OK to perform the given operation on, given
  /// the general approach we've decided to take.
  Optional<LLT> findLegalType(const InstrAspect &Aspect, LegalizeAction Action) const;

  std::pair<LegalizeAction, LLT> findLegalAction(const InstrAspect &Aspect,
                                                 LegalizeAction Action) const {
    auto LegalType = findLegalType(Aspect, Action);
    if (!LegalType)
      return std::make_pair(LegalizeAction::Unsupported, LLT());
    return std::make_pair(Action, *LegalType);
  }

  /// Find the specified \p Aspect in the primary (explicitly set) Actions
  /// table. Returns either the action the target requested or NotFound if there
  /// was no setAction call.
  LegalizeAction findInActions(const InstrAspect &Aspect) const {
    if (Aspect.Opcode < FirstOp || Aspect.Opcode > LastOp)
      return NotFound;
    if (Aspect.Idx >= Actions[Aspect.Opcode - FirstOp].size())
      return NotFound;
    const TypeMap &Map = Actions[Aspect.Opcode - FirstOp][Aspect.Idx];
    auto ActionIt =  Map.find(Aspect.Type);
    if (ActionIt == Map.end())
      return NotFound;

    return ActionIt->second;
  }

  bool isLegal(const MachineInstr &MI, const MachineRegisterInfo &MRI) const;

  virtual bool legalizeCustom(MachineInstr &MI,
                              MachineRegisterInfo &MRI,
                              MachineIRBuilder &MIRBuilder) const;

private:
  static const int FirstOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_START;
  static const int LastOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;

  using TypeMap = DenseMap<LLT, LegalizeAction>;
  using SIVActionMap = DenseMap<std::pair<unsigned, LLT>, LegalizeAction>;

  SmallVector<TypeMap, 1> Actions[LastOp - FirstOp + 1];
  SIVActionMap ScalarInVectorActions;
  DenseMap<std::pair<unsigned, LLT>, uint16_t> MaxLegalVectorElts;
  DenseMap<unsigned, LegalizeAction> DefaultActions;

  bool TablesInitialized = false;
};

} // end namespace llvm

#endif // LLVM_CODEGEN_GLOBALISEL_LEGALIZERINFO_H