//===- 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;
}