//===-- HexagonISelDAGToDAG.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
//
//===----------------------------------------------------------------------===//
// Hexagon specific code to select Hexagon machine instructions for
// SelectionDAG operations.
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_HEXAGON_HEXAGONISELDAGTODAG_H
#define LLVM_LIB_TARGET_HEXAGON_HEXAGONISELDAGTODAG_H

#include "HexagonSubtarget.h"
#include "HexagonTargetMachine.h"
#include "llvm/CodeGen/SelectionDAG.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/Support/CodeGen.h"

#include <vector>

namespace llvm {
class MachineFunction;
class HexagonInstrInfo;
class HexagonRegisterInfo;

class HexagonDAGToDAGISel : public SelectionDAGISel {
  const HexagonSubtarget *HST;
  const HexagonInstrInfo *HII;
  const HexagonRegisterInfo *HRI;
public:
  static char ID;

  HexagonDAGToDAGISel() = delete;

  explicit HexagonDAGToDAGISel(HexagonTargetMachine &tm,
                               CodeGenOptLevel OptLevel)
      : SelectionDAGISel(ID, tm, OptLevel), HST(nullptr), HII(nullptr),
        HRI(nullptr) {}

  bool runOnMachineFunction(MachineFunction &MF) override {
    // Reset the subtarget each time through.
    HST = &MF.getSubtarget<HexagonSubtarget>();
    HII = HST->getInstrInfo();
    HRI = HST->getRegisterInfo();
    SelectionDAGISel::runOnMachineFunction(MF);
    updateAligna();
    return true;
  }

  bool ComplexPatternFuncMutatesDAG() const override {
    return true;
  }
  void PreprocessISelDAG() override;
  void emitFunctionEntryCode() override;

  void Select(SDNode *N) override;

  // Complex Pattern Selectors.
  inline bool SelectAddrGA(SDValue &N, SDValue &R);
  inline bool SelectAddrGP(SDValue &N, SDValue &R);
  inline bool SelectAnyImm(SDValue &N, SDValue &R);
  inline bool SelectAnyInt(SDValue &N, SDValue &R);
  bool SelectAnyImmediate(SDValue &N, SDValue &R, Align Alignment);
  bool SelectGlobalAddress(SDValue &N, SDValue &R, bool UseGP, Align Alignment);
  bool SelectAddrFI(SDValue &N, SDValue &R);
  bool DetectUseSxtw(SDValue &N, SDValue &R);

  inline bool SelectAnyImm0(SDValue &N, SDValue &R);
  inline bool SelectAnyImm1(SDValue &N, SDValue &R);
  inline bool SelectAnyImm2(SDValue &N, SDValue &R);
  inline bool SelectAnyImm3(SDValue &N, SDValue &R);

  // Generate a machine instruction node corresponding to the circ/brev
  // load intrinsic.
  MachineSDNode *LoadInstrForLoadIntrinsic(SDNode *IntN);
  // Given the circ/brev load intrinsic and the already generated machine
  // instruction, generate the appropriate store (that is a part of the
  // intrinsic's functionality).
  SDNode *StoreInstrForLoadIntrinsic(MachineSDNode *LoadN, SDNode *IntN);

  void SelectFrameIndex(SDNode *N);
  /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
  /// inline asm expressions.
  bool SelectInlineAsmMemoryOperand(const SDValue &Op,
                                    InlineAsm::ConstraintCode ConstraintID,
                                    std::vector<SDValue> &OutOps) override;
  bool tryLoadOfLoadIntrinsic(LoadSDNode *N);
  bool SelectBrevLdIntrinsic(SDNode *IntN);
  bool SelectNewCircIntrinsic(SDNode *IntN);
  void SelectLoad(SDNode *N);
  void SelectIndexedLoad(LoadSDNode *LD, const SDLoc &dl);
  void SelectIndexedStore(StoreSDNode *ST, const SDLoc &dl);
  void SelectStore(SDNode *N);
  void SelectSHL(SDNode *N);
  void SelectIntrinsicWChain(SDNode *N);
  void SelectIntrinsicWOChain(SDNode *N);
  void SelectExtractSubvector(SDNode *N);
  void SelectConstant(SDNode *N);
  void SelectConstantFP(SDNode *N);
  void SelectV65Gather(SDNode *N);
  void SelectV65GatherPred(SDNode *N);
  void SelectHVXDualOutput(SDNode *N);
  void SelectAddSubCarry(SDNode *N);
  void SelectVAlign(SDNode *N);
  void SelectVAlignAddr(SDNode *N);
  void SelectTypecast(SDNode *N);
  void SelectP2D(SDNode *N);
  void SelectD2P(SDNode *N);
  void SelectQ2V(SDNode *N);
  void SelectV2Q(SDNode *N);

  // Include the declarations autogenerated from the selection patterns.
  #define GET_DAGISEL_DECL
  #include "HexagonGenDAGISel.inc"

private:
  // This is really only to get access to ReplaceNode (which is a protected
  // member). Any other members used by HvxSelector can be moved around to
  // make them accessible).
  friend struct HvxSelector;

  SDValue selectUndef(const SDLoc &dl, MVT ResTy) {
    SDNode *U = CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, dl, ResTy);
    return SDValue(U, 0);
  }

  bool keepsLowBits(const SDValue &Val, unsigned NumBits, SDValue &Src);
  bool isAlignedMemNode(const MemSDNode *N) const;
  bool isSmallStackStore(const StoreSDNode *N) const;
  bool isPositiveHalfWord(const SDNode *N) const;
  bool hasOneUse(const SDNode *N) const;

  // DAG preprocessing functions.
  void PreprocessHvxISelDAG();
  void ppSimplifyOrSelect0(std::vector<SDNode*> &&Nodes);
  void ppAddrReorderAddShl(std::vector<SDNode*> &&Nodes);
  void ppAddrRewriteAndSrl(std::vector<SDNode*> &&Nodes);
  void ppHoistZextI1(std::vector<SDNode*> &&Nodes);
  void ppHvxShuffleOfShuffle(std::vector<SDNode*> &&Nodes);

  void SelectHvxExtractSubvector(SDNode *N);
  void SelectHvxShuffle(SDNode *N);
  void SelectHvxRor(SDNode *N);
  void SelectHvxVAlign(SDNode *N);

  // Function postprocessing.
  void updateAligna();

  SmallDenseMap<SDNode *,int> RootWeights;
  SmallDenseMap<SDNode *,int> RootHeights;
  SmallDenseMap<const Value *,int> GAUsesInFunction;
  int getWeight(SDNode *N);
  int getHeight(SDNode *N);
  SDValue getMultiplierForSHL(SDNode *N);
  SDValue factorOutPowerOf2(SDValue V, unsigned Power);
  unsigned getUsesInFunction(const Value *V);
  SDValue balanceSubTree(SDNode *N, bool Factorize = false);
  void rebalanceAddressTrees();
}; // end HexagonDAGToDAGISel
}

#endif // LLVM_LIB_TARGET_HEXAGON_HEXAGONISELDAGTODAG_H
