//===- llvm/CodeGen/GlobalISel/LegacyLegalizerInfo.h ------------*- C++ -*-===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
/// \file
 
/// Interface for Targets to specify which operations they can successfully
 
/// select and how the others should be expanded most efficiently.
 
/// This implementation has been deprecated for a long time but it still in use
 
/// in a few places.
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H
 
#define LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H
 
 
 
#include "llvm/ADT/DenseMap.h"
 
#include "llvm/CodeGen/TargetOpcodes.h"
 
#include "llvm/Support/LowLevelTypeImpl.h"
 
#include <unordered_map>
 
 
 
namespace llvm {
 
struct LegalityQuery;
 
 
 
namespace LegacyLegalizeActions {
 
enum LegacyLegalizeAction : 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,
 
 
 
  /// Perform the operation on a different, but equivalently sized type.
 
  Bitcast,
 
 
 
  /// 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,
 
};
 
} // end namespace LegacyLegalizeActions
 
raw_ostream &operator<<(raw_ostream &OS,
 
                        LegacyLegalizeActions::LegacyLegalizeAction Action);
 
 
 
/// 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;
 
  }
 
};
 
 
 
/// The result of a query. It either indicates a final answer of Legal or
 
/// Unsupported or describes an action that must be taken to make an operation
 
/// more legal.
 
struct LegacyLegalizeActionStep {
 
  /// The action to take or the final answer.
 
  LegacyLegalizeActions::LegacyLegalizeAction Action;
 
  /// If describing an action, the type index to change. Otherwise zero.
 
  unsigned TypeIdx;
 
  /// If describing an action, the new type for TypeIdx. Otherwise LLT{}.
 
  LLT NewType;
 
 
 
  LegacyLegalizeActionStep(LegacyLegalizeActions::LegacyLegalizeAction Action,
 
                           unsigned TypeIdx, const LLT NewType)
 
      : Action(Action), TypeIdx(TypeIdx), NewType(NewType) {}
 
 
 
  bool operator==(const LegacyLegalizeActionStep &RHS) const {
 
    return std::tie(Action, TypeIdx, NewType) ==
 
        std::tie(RHS.Action, RHS.TypeIdx, RHS.NewType);
 
  }
 
};
 
 
 
 
 
class LegacyLegalizerInfo {
 
public:
 
  using SizeAndAction =
 
      std::pair<uint16_t, LegacyLegalizeActions::LegacyLegalizeAction>;
 
  using SizeAndActionsVec = std::vector<SizeAndAction>;
 
  using SizeChangeStrategy =
 
      std::function<SizeAndActionsVec(const SizeAndActionsVec &v)>;
 
 
 
  LegacyLegalizerInfo();
 
 
 
  static bool needsLegalizingToDifferentSize(
 
      const LegacyLegalizeActions::LegacyLegalizeAction Action) {
 
    using namespace LegacyLegalizeActions;
 
    switch (Action) {
 
    case NarrowScalar:
 
    case WidenScalar:
 
    case FewerElements:
 
    case MoreElements:
 
    case Unsupported:
 
      return true;
 
    default:
 
      return false;
 
    }
 
  }
 
 
 
  /// 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();
 
 
 
  /// More friendly way to set an action for common types that have an LLT
 
  /// representation.
 
  /// The LegacyLegalizeAction must be one for which
 
  /// NeedsLegalizingToDifferentSize returns false.
 
  void setAction(const InstrAspect &Aspect,
 
                 LegacyLegalizeActions::LegacyLegalizeAction Action) {
 
    assert(!needsLegalizingToDifferentSize(Action));
 
    TablesInitialized = false;
 
    const unsigned OpcodeIdx = Aspect.Opcode - FirstOp;
 
    if (SpecifiedActions[OpcodeIdx].size() <= Aspect.Idx)
 
      SpecifiedActions[OpcodeIdx].resize(Aspect.Idx + 1);
 
    SpecifiedActions[OpcodeIdx][Aspect.Idx][Aspect.Type] = Action;
 
  }
 
 
 
  /// The setAction calls record the non-size-changing legalization actions
 
  /// to take on specificly-sized types. The SizeChangeStrategy defines what
 
  /// to do when the size of the type needs to be changed to reach a legally
 
  /// sized type (i.e., one that was defined through a setAction call).
 
  /// e.g.
 
  /// setAction ({G_ADD, 0, LLT::scalar(32)}, Legal);
 
  /// setLegalizeScalarToDifferentSizeStrategy(
 
  ///   G_ADD, 0, widenToLargerTypesAndNarrowToLargest);
 
  /// will end up defining getAction({G_ADD, 0, T}) to return the following
 
  /// actions for different scalar types T:
 
  ///  LLT::scalar(1)..LLT::scalar(31): {WidenScalar, 0, LLT::scalar(32)}
 
  ///  LLT::scalar(32):                 {Legal, 0, LLT::scalar(32)}
 
  ///  LLT::scalar(33)..:               {NarrowScalar, 0, LLT::scalar(32)}
 
  ///
 
  /// If no SizeChangeAction gets defined, through this function,
 
  /// the default is unsupportedForDifferentSizes.
 
  void setLegalizeScalarToDifferentSizeStrategy(const unsigned Opcode,
 
                                                const unsigned TypeIdx,
 
                                                SizeChangeStrategy S) {
 
    const unsigned OpcodeIdx = Opcode - FirstOp;
 
    if (ScalarSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
 
      ScalarSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
 
    ScalarSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
 
  }
 
 
 
  /// See also setLegalizeScalarToDifferentSizeStrategy.
 
  /// This function allows to set the SizeChangeStrategy for vector elements.
 
  void setLegalizeVectorElementToDifferentSizeStrategy(const unsigned Opcode,
 
                                                       const unsigned TypeIdx,
 
                                                       SizeChangeStrategy S) {
 
    const unsigned OpcodeIdx = Opcode - FirstOp;
 
    if (VectorElementSizeChangeStrategies[OpcodeIdx].size() <= TypeIdx)
 
      VectorElementSizeChangeStrategies[OpcodeIdx].resize(TypeIdx + 1);
 
    VectorElementSizeChangeStrategies[OpcodeIdx][TypeIdx] = S;
 
  }
 
 
 
  /// A SizeChangeStrategy for the common case where legalization for a
 
  /// particular operation consists of only supporting a specific set of type
 
  /// sizes. E.g.
 
  ///   setAction ({G_DIV, 0, LLT::scalar(32)}, Legal);
 
  ///   setAction ({G_DIV, 0, LLT::scalar(64)}, Legal);
 
  ///   setLegalizeScalarToDifferentSizeStrategy(
 
  ///     G_DIV, 0, unsupportedForDifferentSizes);
 
  /// will result in getAction({G_DIV, 0, T}) to return Legal for s32 and s64,
 
  /// and Unsupported for all other scalar types T.
 
  static SizeAndActionsVec
 
  unsupportedForDifferentSizes(const SizeAndActionsVec &v) {
 
    using namespace LegacyLegalizeActions;
 
    return increaseToLargerTypesAndDecreaseToLargest(v, Unsupported,
 
                                                     Unsupported);
 
  }
 
 
 
  /// A SizeChangeStrategy for the common case where legalization for a
 
  /// particular operation consists of widening the type to a large legal type,
 
  /// unless there is no such type and then instead it should be narrowed to the
 
  /// largest legal type.
 
  static SizeAndActionsVec
 
  widenToLargerTypesAndNarrowToLargest(const SizeAndActionsVec &v) {
 
    using namespace LegacyLegalizeActions;
 
    assert(v.size() > 0 &&
 
           "At least one size that can be legalized towards is needed"
 
           " for this SizeChangeStrategy");
 
    return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
 
                                                     NarrowScalar);
 
  }
 
 
 
  static SizeAndActionsVec
 
  widenToLargerTypesUnsupportedOtherwise(const SizeAndActionsVec &v) {
 
    using namespace LegacyLegalizeActions;
 
    return increaseToLargerTypesAndDecreaseToLargest(v, WidenScalar,
 
                                                     Unsupported);
 
  }
 
 
 
  static SizeAndActionsVec
 
  narrowToSmallerAndUnsupportedIfTooSmall(const SizeAndActionsVec &v) {
 
    using namespace LegacyLegalizeActions;
 
    return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
 
                                                       Unsupported);
 
  }
 
 
 
  static SizeAndActionsVec
 
  narrowToSmallerAndWidenToSmallest(const SizeAndActionsVec &v) {
 
    using namespace LegacyLegalizeActions;
 
    assert(v.size() > 0 &&
 
           "At least one size that can be legalized towards is needed"
 
           " for this SizeChangeStrategy");
 
    return decreaseToSmallerTypesAndIncreaseToSmallest(v, NarrowScalar,
 
                                                       WidenScalar);
 
  }
 
 
 
  /// A SizeChangeStrategy for the common case where legalization for a
 
  /// particular vector operation consists of having more elements in the
 
  /// vector, to a type that is legal. Unless there is no such type and then
 
  /// instead it should be legalized towards the widest vector that's still
 
  /// legal. E.g.
 
  ///   setAction({G_ADD, LLT::vector(8, 8)}, Legal);
 
  ///   setAction({G_ADD, LLT::vector(16, 8)}, Legal);
 
  ///   setAction({G_ADD, LLT::vector(2, 32)}, Legal);
 
  ///   setAction({G_ADD, LLT::vector(4, 32)}, Legal);
 
  ///   setLegalizeVectorElementToDifferentSizeStrategy(
 
  ///     G_ADD, 0, moreToWiderTypesAndLessToWidest);
 
  /// will result in the following getAction results:
 
  ///   * getAction({G_ADD, LLT::vector(8,8)}) returns
 
  ///       (Legal, vector(8,8)).
 
  ///   * getAction({G_ADD, LLT::vector(9,8)}) returns
 
  ///       (MoreElements, vector(16,8)).
 
  ///   * getAction({G_ADD, LLT::vector(8,32)}) returns
 
  ///       (FewerElements, vector(4,32)).
 
  static SizeAndActionsVec
 
  moreToWiderTypesAndLessToWidest(const SizeAndActionsVec &v) {
 
    using namespace LegacyLegalizeActions;
 
    return increaseToLargerTypesAndDecreaseToLargest(v, MoreElements,
 
                                                     FewerElements);
 
  }
 
 
 
  /// Helper function to implement many typical SizeChangeStrategy functions.
 
  static SizeAndActionsVec increaseToLargerTypesAndDecreaseToLargest(
 
      const SizeAndActionsVec &v,
 
      LegacyLegalizeActions::LegacyLegalizeAction IncreaseAction,
 
      LegacyLegalizeActions::LegacyLegalizeAction DecreaseAction);
 
  /// Helper function to implement many typical SizeChangeStrategy functions.
 
  static SizeAndActionsVec decreaseToSmallerTypesAndIncreaseToSmallest(
 
      const SizeAndActionsVec &v,
 
      LegacyLegalizeActions::LegacyLegalizeAction DecreaseAction,
 
      LegacyLegalizeActions::LegacyLegalizeAction IncreaseAction);
 
 
 
  LegacyLegalizeActionStep getAction(const LegalityQuery &Query) const;
 
 
 
  unsigned getOpcodeIdxForOpcode(unsigned Opcode) const;
 
 
 
private:
 
  /// 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<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
 
  getAspectAction(const InstrAspect &Aspect) const;
 
 
 
  /// The SizeAndActionsVec is a representation mapping between all natural
 
  /// numbers and an Action. The natural number represents the bit size of
 
  /// the InstrAspect. For example, for a target with native support for 32-bit
 
  /// and 64-bit additions, you'd express that as:
 
  /// setScalarAction(G_ADD, 0,
 
  ///           {{1, WidenScalar},  // bit sizes [ 1, 31[
 
  ///            {32, Legal},       // bit sizes [32, 33[
 
  ///            {33, WidenScalar}, // bit sizes [33, 64[
 
  ///            {64, Legal},       // bit sizes [64, 65[
 
  ///            {65, NarrowScalar} // bit sizes [65, +inf[
 
  ///           });
 
  /// It may be that only 64-bit pointers are supported on your target:
 
  /// setPointerAction(G_PTR_ADD, 0, LLT:pointer(1),
 
  ///           {{1, Unsupported},  // bit sizes [ 1, 63[
 
  ///            {64, Legal},       // bit sizes [64, 65[
 
  ///            {65, Unsupported}, // bit sizes [65, +inf[
 
  ///           });
 
  void setScalarAction(const unsigned Opcode, const unsigned TypeIndex,
 
                       const SizeAndActionsVec &SizeAndActions) {
 
    const unsigned OpcodeIdx = Opcode - FirstOp;
 
    SmallVector<SizeAndActionsVec, 1> &Actions = ScalarActions[OpcodeIdx];
 
    setActions(TypeIndex, Actions, SizeAndActions);
 
  }
 
  void setPointerAction(const unsigned Opcode, const unsigned TypeIndex,
 
                        const unsigned AddressSpace,
 
                        const SizeAndActionsVec &SizeAndActions) {
 
    const unsigned OpcodeIdx = Opcode - FirstOp;
 
    if (AddrSpace2PointerActions[OpcodeIdx].find(AddressSpace) ==
 
        AddrSpace2PointerActions[OpcodeIdx].end())
 
      AddrSpace2PointerActions[OpcodeIdx][AddressSpace] = {{}};
 
    SmallVector<SizeAndActionsVec, 1> &Actions =
 
        AddrSpace2PointerActions[OpcodeIdx].find(AddressSpace)->second;
 
    setActions(TypeIndex, Actions, SizeAndActions);
 
  }
 
 
 
  /// 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(const unsigned Opcode, const unsigned TypeIndex,
 
                               const SizeAndActionsVec &SizeAndActions) {
 
    unsigned OpcodeIdx = Opcode - FirstOp;
 
    SmallVector<SizeAndActionsVec, 1> &Actions =
 
        ScalarInVectorActions[OpcodeIdx];
 
    setActions(TypeIndex, Actions, SizeAndActions);
 
  }
 
 
 
  /// See also setScalarInVectorAction.
 
  /// This function let's you specify the number of elements in a vector that
 
  /// are legal for a legal element size.
 
  void setVectorNumElementAction(const unsigned Opcode,
 
                                 const unsigned TypeIndex,
 
                                 const unsigned ElementSize,
 
                                 const SizeAndActionsVec &SizeAndActions) {
 
    const unsigned OpcodeIdx = Opcode - FirstOp;
 
    if (NumElements2Actions[OpcodeIdx].find(ElementSize) ==
 
        NumElements2Actions[OpcodeIdx].end())
 
      NumElements2Actions[OpcodeIdx][ElementSize] = {{}};
 
    SmallVector<SizeAndActionsVec, 1> &Actions =
 
        NumElements2Actions[OpcodeIdx].find(ElementSize)->second;
 
    setActions(TypeIndex, Actions, SizeAndActions);
 
  }
 
 
 
  /// A partial SizeAndActionsVec potentially doesn't cover all bit sizes,
 
  /// i.e. it's OK if it doesn't start from size 1.
 
  static void checkPartialSizeAndActionsVector(const SizeAndActionsVec& v) {
 
    using namespace LegacyLegalizeActions;
 
#ifndef NDEBUG
 
    // The sizes should be in increasing order
 
    int prev_size = -1;
 
    for(auto SizeAndAction: v) {
 
      assert(SizeAndAction.first > prev_size);
 
      prev_size = SizeAndAction.first;
 
    }
 
    // - for every Widen action, there should be a larger bitsize that
 
    //   can be legalized towards (e.g. Legal, Lower, Libcall or Custom
 
    //   action).
 
    // - for every Narrow action, there should be a smaller bitsize that
 
    //   can be legalized towards.
 
    int SmallestNarrowIdx = -1;
 
    int LargestWidenIdx = -1;
 
    int SmallestLegalizableToSameSizeIdx = -1;
 
    int LargestLegalizableToSameSizeIdx = -1;
 
    for(size_t i=0; i<v.size(); ++i) {
 
      switch (v[i].second) {
 
        case FewerElements:
 
        case NarrowScalar:
 
          if (SmallestNarrowIdx == -1)
 
            SmallestNarrowIdx = i;
 
          break;
 
        case WidenScalar:
 
        case MoreElements:
 
          LargestWidenIdx = i;
 
          break;
 
        case Unsupported:
 
          break;
 
        default:
 
          if (SmallestLegalizableToSameSizeIdx == -1)
 
            SmallestLegalizableToSameSizeIdx = i;
 
          LargestLegalizableToSameSizeIdx = i;
 
      }
 
    }
 
    if (SmallestNarrowIdx != -1) {
 
      assert(SmallestLegalizableToSameSizeIdx != -1);
 
      assert(SmallestNarrowIdx > SmallestLegalizableToSameSizeIdx);
 
    }
 
    if (LargestWidenIdx != -1)
 
      assert(LargestWidenIdx < LargestLegalizableToSameSizeIdx);
 
#endif
 
  }
 
 
 
  /// A full SizeAndActionsVec must cover all bit sizes, i.e. must start with
 
  /// from size 1.
 
  static void checkFullSizeAndActionsVector(const SizeAndActionsVec& v) {
 
#ifndef NDEBUG
 
    // Data structure invariant: The first bit size must be size 1.
 
    assert(v.size() >= 1);
 
    assert(v[0].first == 1);
 
    checkPartialSizeAndActionsVector(v);
 
#endif
 
  }
 
 
 
  /// Sets actions for all bit sizes on a particular generic opcode, type
 
  /// index and scalar or pointer type.
 
  void setActions(unsigned TypeIndex,
 
                  SmallVector<SizeAndActionsVec, 1> &Actions,
 
                  const SizeAndActionsVec &SizeAndActions) {
 
    checkFullSizeAndActionsVector(SizeAndActions);
 
    if (Actions.size() <= TypeIndex)
 
      Actions.resize(TypeIndex + 1);
 
    Actions[TypeIndex] = SizeAndActions;
 
  }
 
 
 
  static SizeAndAction findAction(const SizeAndActionsVec &Vec,
 
                                  const uint32_t Size);
 
 
 
  /// Returns the next action needed to get the scalar or pointer type closer
 
  /// to being legal
 
  /// E.g. findLegalAction({G_REM, 13}) should return
 
  /// (WidenScalar, 32). After that, findLegalAction({G_REM, 32}) will
 
  /// probably be called, which should return (Lower, 32).
 
  /// This is assuming the setScalarAction on G_REM was something like:
 
  /// setScalarAction(G_REM, 0,
 
  ///           {{1, WidenScalar},  // bit sizes [ 1, 31[
 
  ///            {32, Lower},       // bit sizes [32, 33[
 
  ///            {33, NarrowScalar} // bit sizes [65, +inf[
 
  ///           });
 
  std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
 
  findScalarLegalAction(const InstrAspect &Aspect) const;
 
 
 
  /// Returns the next action needed towards legalizing the vector type.
 
  std::pair<LegacyLegalizeActions::LegacyLegalizeAction, LLT>
 
  findVectorLegalAction(const InstrAspect &Aspect) const;
 
 
 
  static const int FirstOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_START;
 
  static const int LastOp = TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
 
 
 
  // Data structures used temporarily during construction of legality data:
 
  using TypeMap = DenseMap<LLT, LegacyLegalizeActions::LegacyLegalizeAction>;
 
  SmallVector<TypeMap, 1> SpecifiedActions[LastOp - FirstOp + 1];
 
  SmallVector<SizeChangeStrategy, 1>
 
      ScalarSizeChangeStrategies[LastOp - FirstOp + 1];
 
  SmallVector<SizeChangeStrategy, 1>
 
      VectorElementSizeChangeStrategies[LastOp - FirstOp + 1];
 
  bool TablesInitialized = false;
 
 
 
  // Data structures used by getAction:
 
  SmallVector<SizeAndActionsVec, 1> ScalarActions[LastOp - FirstOp + 1];
 
  SmallVector<SizeAndActionsVec, 1> ScalarInVectorActions[LastOp - FirstOp + 1];
 
  std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
 
      AddrSpace2PointerActions[LastOp - FirstOp + 1];
 
  std::unordered_map<uint16_t, SmallVector<SizeAndActionsVec, 1>>
 
      NumElements2Actions[LastOp - FirstOp + 1];
 
};
 
 
 
} // end namespace llvm
 
 
 
#endif // LLVM_CODEGEN_GLOBALISEL_LEGACYLEGALIZERINFO_H