//===- 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 "MCTargetDesc/XtensaMCTargetDesc.h" #include "Xtensa.h" #include "XtensaTargetMachine.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 { const XtensaSubtarget *Subtarget = nullptr; public: explicit XtensaDAGToDAGISel(XtensaTargetMachine &TM, CodeGenOptLevel OptLevel) : SelectionDAGISel(TM, OptLevel) {} bool runOnMachineFunction(MachineFunction &MF) override { Subtarget = &MF.getSubtarget(); return SelectionDAGISel::runOnMachineFunction(MF); } void Select(SDNode *Node) override; bool SelectInlineAsmMemoryOperand(const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, std::vector &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(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(Addr.getOperand(1)); int64_t OffsetVal = CN->getSExtValue(); Valid = Xtensa::isValidAddrOffset(Scale, OffsetVal); if (Valid) { // If the first operand is a FI, get the TargetFI Node. if (FrameIndexSDNode *FIN = dyn_cast(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(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(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(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(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 &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; }