llvm/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp

//===- XtensaISelDAGToDAG.cpp - A dag to dag inst selector for Xtensa -----===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file defines an instruction selector for the Xtensa target.
//
//===----------------------------------------------------------------------===//

#include "Xtensa.h"
#include "XtensaTargetMachine.h"
#include "XtensaUtils.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

#define DEBUG_TYPE "xtensa-isel"

namespace {

class XtensaDAGToDAGISel : public SelectionDAGISel {
public:
  XtensaDAGToDAGISel(XtensaTargetMachine &TM, CodeGenOptLevel OptLevel)
      : SelectionDAGISel(TM, OptLevel) {}

  void Select(SDNode *Node) override;

  bool SelectInlineAsmMemoryOperand(const SDValue &Op,
                                    InlineAsm::ConstraintCode ConstraintID,
                                    std::vector<SDValue> &OutOps) override;

  // For load/store instructions generate (base+offset) pair from
  // memory address. The offset must be a multiple of scale argument.
  bool selectMemRegAddr(SDValue Addr, SDValue &Base, SDValue &Offset,
                        int Scale) {
    EVT ValTy = Addr.getValueType();

    // if Address is FI, get the TargetFrameIndex.
    if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {
      Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
      Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), ValTy);

      return true;
    }

    if (TM.isPositionIndependent()) {
      DiagnosticInfoUnsupported Diag(CurDAG->getMachineFunction().getFunction(),
                                     "PIC relocations are not supported ",
                                     Addr.getDebugLoc());
      CurDAG->getContext()->diagnose(Diag);
    }

    if ((Addr.getOpcode() == ISD::TargetExternalSymbol ||
         Addr.getOpcode() == ISD::TargetGlobalAddress))
      return false;

    // Addresses of the form FI+const
    bool Valid = false;
    if (CurDAG->isBaseWithConstantOffset(Addr)) {
      ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1));
      int64_t OffsetVal = CN->getSExtValue();

      Valid = isValidAddrOffset(Scale, OffsetVal);

      if (Valid) {
        // If the first operand is a FI, get the TargetFI Node.
        if (FrameIndexSDNode *FIN =
                dyn_cast<FrameIndexSDNode>(Addr.getOperand(0)))
          Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
        else
          Base = Addr.getOperand(0);

        Offset =
            CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), ValTy);
        return true;
      }
    }

    // Last case
    Base = Addr;
    Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), Addr.getValueType());
    return true;
  }

  bool selectMemRegAddrISH1(SDValue Addr, SDValue &Base, SDValue &Offset) {
    return selectMemRegAddr(Addr, Base, Offset, 1);
  }

  bool selectMemRegAddrISH2(SDValue Addr, SDValue &Base, SDValue &Offset) {
    return selectMemRegAddr(Addr, Base, Offset, 2);
  }

  bool selectMemRegAddrISH4(SDValue Addr, SDValue &Base, SDValue &Offset) {
    return selectMemRegAddr(Addr, Base, Offset, 4);
  }

// Include the pieces autogenerated from the target description.
#include "XtensaGenDAGISel.inc"
}; // namespace

class XtensaDAGToDAGISelLegacy : public SelectionDAGISelLegacy {
public:
  static char ID;

  XtensaDAGToDAGISelLegacy(XtensaTargetMachine &TM, CodeGenOptLevel OptLevel)
      : SelectionDAGISelLegacy(
            ID, std::make_unique<XtensaDAGToDAGISel>(TM, OptLevel)) {}

  StringRef getPassName() const override {
    return "Xtensa DAG->DAG Pattern Instruction Selection";
  }
};
} // end anonymous namespace

char XtensaDAGToDAGISelLegacy::ID = 0;

FunctionPass *llvm::createXtensaISelDag(XtensaTargetMachine &TM,
                                        CodeGenOptLevel OptLevel) {
  return new XtensaDAGToDAGISelLegacy(TM, OptLevel);
}

void XtensaDAGToDAGISel::Select(SDNode *Node) {
  SDLoc DL(Node);
  EVT VT = Node->getValueType(0);

  // If we have a custom node, we already have selected!
  if (Node->isMachineOpcode()) {
    Node->setNodeId(-1);
    return;
  }

  switch (Node->getOpcode()) {
  case ISD::SHL: {
    SDValue N0 = Node->getOperand(0);
    SDValue N1 = Node->getOperand(1);
    auto *C = dyn_cast<ConstantSDNode>(N1);
    // If C is constant in range [1..31] then we can generate SLLI
    // instruction using pattern matching, otherwise generate SLL.
    if (!C || C->isZero()) {
      SDNode *SSL = CurDAG->getMachineNode(Xtensa::SSL, DL, MVT::Glue, N1);
      SDNode *SLL =
          CurDAG->getMachineNode(Xtensa::SLL, DL, VT, N0, SDValue(SSL, 0));
      ReplaceNode(Node, SLL);
      return;
    }
    break;
  }
  case ISD::SRL: {
    SDValue N0 = Node->getOperand(0);
    SDValue N1 = Node->getOperand(1);
    auto *C = dyn_cast<ConstantSDNode>(N1);

    // If C is constant then we can generate SRLI
    // instruction using pattern matching or EXTUI, otherwise generate SRL.
    if (C) {
      if (isUInt<4>(C->getZExtValue()))
        break;
      unsigned ShAmt = C->getZExtValue();
      SDNode *EXTUI = CurDAG->getMachineNode(
          Xtensa::EXTUI, DL, VT, N0, CurDAG->getTargetConstant(ShAmt, DL, VT),
          CurDAG->getTargetConstant(32 - ShAmt, DL, VT));
      ReplaceNode(Node, EXTUI);
      return;
    }

    SDNode *SSR = CurDAG->getMachineNode(Xtensa::SSR, DL, MVT::Glue, N1);
    SDNode *SRL =
        CurDAG->getMachineNode(Xtensa::SRL, DL, VT, N0, SDValue(SSR, 0));
    ReplaceNode(Node, SRL);
    return;
  }
  case ISD::SRA: {
    SDValue N0 = Node->getOperand(0);
    SDValue N1 = Node->getOperand(1);
    auto *C = dyn_cast<ConstantSDNode>(N1);
    // If C is constant then we can generate SRAI
    // instruction using pattern matching, otherwise generate SRA.
    if (!C) {
      SDNode *SSR = CurDAG->getMachineNode(Xtensa::SSR, DL, MVT::Glue, N1);
      SDNode *SRA =
          CurDAG->getMachineNode(Xtensa::SRA, DL, VT, N0, SDValue(SSR, 0));
      ReplaceNode(Node, SRA);
      return;
    }
    break;
  }
  case XtensaISD::SRCL: {
    SDValue N0 = Node->getOperand(0);
    SDValue N1 = Node->getOperand(1);
    SDValue N2 = Node->getOperand(2);
    SDNode *SSL = CurDAG->getMachineNode(Xtensa::SSL, DL, MVT::Glue, N2);
    SDNode *SRC =
        CurDAG->getMachineNode(Xtensa::SRC, DL, VT, N0, N1, SDValue(SSL, 0));
    ReplaceNode(Node, SRC);
    return;
  }
  case XtensaISD::SRCR: {
    SDValue N0 = Node->getOperand(0);
    SDValue N1 = Node->getOperand(1);
    SDValue N2 = Node->getOperand(2);
    SDNode *SSR = CurDAG->getMachineNode(Xtensa::SSR, DL, MVT::Glue, N2);
    SDNode *SRC =
        CurDAG->getMachineNode(Xtensa::SRC, DL, VT, N0, N1, SDValue(SSR, 0));
    ReplaceNode(Node, SRC);
    return;
  }
  }

  SelectCode(Node);
}

bool XtensaDAGToDAGISel::SelectInlineAsmMemoryOperand(
    const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,
    std::vector<SDValue> &OutOps) {
  switch (ConstraintID) {
  default:
    llvm_unreachable("Unexpected asm memory constraint");
  case InlineAsm::ConstraintCode::m: {
    SDValue Base, Offset;

    selectMemRegAddr(Op, Base, Offset, 4);
    OutOps.push_back(Base);
    OutOps.push_back(Offset);

    return false;
  }
  }
  return false;
}