1326938Sdim//===-- RISCVISelDAGToDAG.cpp - A dag to dag inst selector for RISCV ------===// 2326938Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6326938Sdim// 7326938Sdim//===----------------------------------------------------------------------===// 8326938Sdim// 9326938Sdim// This file defines an instruction selector for the RISCV target. 10326938Sdim// 11326938Sdim//===----------------------------------------------------------------------===// 12326938Sdim 13344779Sdim#include "MCTargetDesc/RISCVMCTargetDesc.h" 14326938Sdim#include "RISCV.h" 15326938Sdim#include "RISCVTargetMachine.h" 16344779Sdim#include "Utils/RISCVMatInt.h" 17326938Sdim#include "llvm/CodeGen/MachineFrameInfo.h" 18326938Sdim#include "llvm/CodeGen/SelectionDAGISel.h" 19326938Sdim#include "llvm/Support/Debug.h" 20326938Sdim#include "llvm/Support/MathExtras.h" 21326938Sdim#include "llvm/Support/raw_ostream.h" 22326938Sdimusing namespace llvm; 23326938Sdim 24326938Sdim#define DEBUG_TYPE "riscv-isel" 25326938Sdim 26326938Sdim// RISCV-specific code to select RISCV machine instructions for 27326938Sdim// SelectionDAG operations. 28326938Sdimnamespace { 29326938Sdimclass RISCVDAGToDAGISel final : public SelectionDAGISel { 30360784Sdim const RISCVSubtarget *Subtarget = nullptr; 31326938Sdim 32326938Sdimpublic: 33326938Sdim explicit RISCVDAGToDAGISel(RISCVTargetMachine &TargetMachine) 34326938Sdim : SelectionDAGISel(TargetMachine) {} 35326938Sdim 36326938Sdim StringRef getPassName() const override { 37326938Sdim return "RISCV DAG->DAG Pattern Instruction Selection"; 38326938Sdim } 39326938Sdim 40326938Sdim bool runOnMachineFunction(MachineFunction &MF) override { 41326938Sdim Subtarget = &MF.getSubtarget<RISCVSubtarget>(); 42326938Sdim return SelectionDAGISel::runOnMachineFunction(MF); 43326938Sdim } 44326938Sdim 45341825Sdim void PostprocessISelDAG() override; 46341825Sdim 47326938Sdim void Select(SDNode *Node) override; 48326938Sdim 49341825Sdim bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 50341825Sdim std::vector<SDValue> &OutOps) override; 51341825Sdim 52326938Sdim bool SelectAddrFI(SDValue Addr, SDValue &Base); 53326938Sdim 54326938Sdim// Include the pieces autogenerated from the target description. 55326938Sdim#include "RISCVGenDAGISel.inc" 56341825Sdim 57341825Sdimprivate: 58341825Sdim void doPeepholeLoadStoreADDI(); 59326938Sdim}; 60326938Sdim} 61326938Sdim 62341825Sdimvoid RISCVDAGToDAGISel::PostprocessISelDAG() { 63341825Sdim doPeepholeLoadStoreADDI(); 64341825Sdim} 65341825Sdim 66344779Sdimstatic SDNode *selectImm(SelectionDAG *CurDAG, const SDLoc &DL, int64_t Imm, 67344779Sdim MVT XLenVT) { 68344779Sdim RISCVMatInt::InstSeq Seq; 69344779Sdim RISCVMatInt::generateInstSeq(Imm, XLenVT == MVT::i64, Seq); 70344779Sdim 71360784Sdim SDNode *Result = nullptr; 72344779Sdim SDValue SrcReg = CurDAG->getRegister(RISCV::X0, XLenVT); 73344779Sdim for (RISCVMatInt::Inst &Inst : Seq) { 74344779Sdim SDValue SDImm = CurDAG->getTargetConstant(Inst.Imm, DL, XLenVT); 75344779Sdim if (Inst.Opc == RISCV::LUI) 76344779Sdim Result = CurDAG->getMachineNode(RISCV::LUI, DL, XLenVT, SDImm); 77344779Sdim else 78344779Sdim Result = CurDAG->getMachineNode(Inst.Opc, DL, XLenVT, SrcReg, SDImm); 79344779Sdim 80344779Sdim // Only the first instruction has X0 as its source. 81344779Sdim SrcReg = SDValue(Result, 0); 82344779Sdim } 83344779Sdim 84344779Sdim return Result; 85344779Sdim} 86344779Sdim 87344779Sdim// Returns true if the Node is an ISD::AND with a constant argument. If so, 88344779Sdim// set Mask to that constant value. 89344779Sdimstatic bool isConstantMask(SDNode *Node, uint64_t &Mask) { 90344779Sdim if (Node->getOpcode() == ISD::AND && 91344779Sdim Node->getOperand(1).getOpcode() == ISD::Constant) { 92344779Sdim Mask = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 93344779Sdim return true; 94344779Sdim } 95344779Sdim return false; 96344779Sdim} 97344779Sdim 98326938Sdimvoid RISCVDAGToDAGISel::Select(SDNode *Node) { 99344779Sdim // If we have a custom node, we have already selected. 100326938Sdim if (Node->isMachineOpcode()) { 101341825Sdim LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n"); 102326938Sdim Node->setNodeId(-1); 103326938Sdim return; 104326938Sdim } 105326938Sdim 106326938Sdim // Instruction Selection not handled by the auto-generated tablegen selection 107326938Sdim // should be handled here. 108344779Sdim unsigned Opcode = Node->getOpcode(); 109344779Sdim MVT XLenVT = Subtarget->getXLenVT(); 110344779Sdim SDLoc DL(Node); 111326938Sdim EVT VT = Node->getValueType(0); 112344779Sdim 113344779Sdim switch (Opcode) { 114344779Sdim case ISD::Constant: { 115344779Sdim auto ConstNode = cast<ConstantSDNode>(Node); 116344779Sdim if (VT == XLenVT && ConstNode->isNullValue()) { 117326938Sdim SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node), 118326938Sdim RISCV::X0, XLenVT); 119326938Sdim ReplaceNode(Node, New.getNode()); 120326938Sdim return; 121326938Sdim } 122344779Sdim int64_t Imm = ConstNode->getSExtValue(); 123344779Sdim if (XLenVT == MVT::i64) { 124344779Sdim ReplaceNode(Node, selectImm(CurDAG, SDLoc(Node), Imm, XLenVT)); 125344779Sdim return; 126344779Sdim } 127344779Sdim break; 128326938Sdim } 129344779Sdim case ISD::FrameIndex: { 130326938Sdim SDValue Imm = CurDAG->getTargetConstant(0, DL, XLenVT); 131341825Sdim int FI = cast<FrameIndexSDNode>(Node)->getIndex(); 132326938Sdim SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); 133326938Sdim ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ADDI, DL, VT, TFI, Imm)); 134326938Sdim return; 135326938Sdim } 136344779Sdim case ISD::SRL: { 137344779Sdim if (!Subtarget->is64Bit()) 138344779Sdim break; 139344779Sdim SDValue Op0 = Node->getOperand(0); 140344779Sdim SDValue Op1 = Node->getOperand(1); 141344779Sdim uint64_t Mask; 142344779Sdim // Match (srl (and val, mask), imm) where the result would be a 143344779Sdim // zero-extended 32-bit integer. i.e. the mask is 0xffffffff or the result 144344779Sdim // is equivalent to this (SimplifyDemandedBits may have removed lower bits 145344779Sdim // from the mask that aren't necessary due to the right-shifting). 146344779Sdim if (Op1.getOpcode() == ISD::Constant && 147344779Sdim isConstantMask(Op0.getNode(), Mask)) { 148344779Sdim uint64_t ShAmt = cast<ConstantSDNode>(Op1.getNode())->getZExtValue(); 149326938Sdim 150344779Sdim if ((Mask | maskTrailingOnes<uint64_t>(ShAmt)) == 0xffffffff) { 151344779Sdim SDValue ShAmtVal = 152344779Sdim CurDAG->getTargetConstant(ShAmt, SDLoc(Node), XLenVT); 153344779Sdim CurDAG->SelectNodeTo(Node, RISCV::SRLIW, XLenVT, Op0.getOperand(0), 154344779Sdim ShAmtVal); 155344779Sdim return; 156344779Sdim } 157344779Sdim } 158353358Sdim break; 159344779Sdim } 160353358Sdim case RISCVISD::READ_CYCLE_WIDE: 161353358Sdim assert(!Subtarget->is64Bit() && "READ_CYCLE_WIDE is only used on riscv32"); 162353358Sdim 163353358Sdim ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ReadCycleWide, DL, MVT::i32, 164353358Sdim MVT::i32, MVT::Other, 165353358Sdim Node->getOperand(0))); 166353358Sdim return; 167344779Sdim } 168344779Sdim 169326938Sdim // Select the default instruction. 170326938Sdim SelectCode(Node); 171326938Sdim} 172326938Sdim 173341825Sdimbool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand( 174341825Sdim const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 175341825Sdim switch (ConstraintID) { 176341825Sdim case InlineAsm::Constraint_m: 177341825Sdim // We just support simple memory operands that have a single address 178341825Sdim // operand and need no special handling. 179341825Sdim OutOps.push_back(Op); 180341825Sdim return false; 181353358Sdim case InlineAsm::Constraint_A: 182353358Sdim OutOps.push_back(Op); 183353358Sdim return false; 184341825Sdim default: 185341825Sdim break; 186341825Sdim } 187341825Sdim 188341825Sdim return true; 189341825Sdim} 190341825Sdim 191326938Sdimbool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) { 192326938Sdim if (auto FIN = dyn_cast<FrameIndexSDNode>(Addr)) { 193326938Sdim Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getXLenVT()); 194326938Sdim return true; 195326938Sdim } 196326938Sdim return false; 197326938Sdim} 198326938Sdim 199341825Sdim// Merge an ADDI into the offset of a load/store instruction where possible. 200341825Sdim// (load (add base, off), 0) -> (load base, off) 201341825Sdim// (store val, (add base, off)) -> (store val, base, off) 202341825Sdimvoid RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() { 203341825Sdim SelectionDAG::allnodes_iterator Position(CurDAG->getRoot().getNode()); 204341825Sdim ++Position; 205341825Sdim 206341825Sdim while (Position != CurDAG->allnodes_begin()) { 207341825Sdim SDNode *N = &*--Position; 208341825Sdim // Skip dead nodes and any non-machine opcodes. 209341825Sdim if (N->use_empty() || !N->isMachineOpcode()) 210341825Sdim continue; 211341825Sdim 212341825Sdim int OffsetOpIdx; 213341825Sdim int BaseOpIdx; 214341825Sdim 215341825Sdim // Only attempt this optimisation for I-type loads and S-type stores. 216341825Sdim switch (N->getMachineOpcode()) { 217341825Sdim default: 218341825Sdim continue; 219341825Sdim case RISCV::LB: 220341825Sdim case RISCV::LH: 221341825Sdim case RISCV::LW: 222341825Sdim case RISCV::LBU: 223341825Sdim case RISCV::LHU: 224341825Sdim case RISCV::LWU: 225341825Sdim case RISCV::LD: 226341825Sdim case RISCV::FLW: 227341825Sdim case RISCV::FLD: 228341825Sdim BaseOpIdx = 0; 229341825Sdim OffsetOpIdx = 1; 230341825Sdim break; 231341825Sdim case RISCV::SB: 232341825Sdim case RISCV::SH: 233341825Sdim case RISCV::SW: 234341825Sdim case RISCV::SD: 235341825Sdim case RISCV::FSW: 236341825Sdim case RISCV::FSD: 237341825Sdim BaseOpIdx = 1; 238341825Sdim OffsetOpIdx = 2; 239341825Sdim break; 240341825Sdim } 241341825Sdim 242341825Sdim // Currently, the load/store offset must be 0 to be considered for this 243341825Sdim // peephole optimisation. 244341825Sdim if (!isa<ConstantSDNode>(N->getOperand(OffsetOpIdx)) || 245341825Sdim N->getConstantOperandVal(OffsetOpIdx) != 0) 246341825Sdim continue; 247341825Sdim 248341825Sdim SDValue Base = N->getOperand(BaseOpIdx); 249341825Sdim 250341825Sdim // If the base is an ADDI, we can merge it in to the load/store. 251341825Sdim if (!Base.isMachineOpcode() || Base.getMachineOpcode() != RISCV::ADDI) 252341825Sdim continue; 253341825Sdim 254341825Sdim SDValue ImmOperand = Base.getOperand(1); 255341825Sdim 256341825Sdim if (auto Const = dyn_cast<ConstantSDNode>(ImmOperand)) { 257341825Sdim ImmOperand = CurDAG->getTargetConstant( 258341825Sdim Const->getSExtValue(), SDLoc(ImmOperand), ImmOperand.getValueType()); 259341825Sdim } else if (auto GA = dyn_cast<GlobalAddressSDNode>(ImmOperand)) { 260341825Sdim ImmOperand = CurDAG->getTargetGlobalAddress( 261341825Sdim GA->getGlobal(), SDLoc(ImmOperand), ImmOperand.getValueType(), 262341825Sdim GA->getOffset(), GA->getTargetFlags()); 263341825Sdim } else { 264341825Sdim continue; 265341825Sdim } 266341825Sdim 267341825Sdim LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase: "); 268341825Sdim LLVM_DEBUG(Base->dump(CurDAG)); 269341825Sdim LLVM_DEBUG(dbgs() << "\nN: "); 270341825Sdim LLVM_DEBUG(N->dump(CurDAG)); 271341825Sdim LLVM_DEBUG(dbgs() << "\n"); 272341825Sdim 273341825Sdim // Modify the offset operand of the load/store. 274341825Sdim if (BaseOpIdx == 0) // Load 275341825Sdim CurDAG->UpdateNodeOperands(N, Base.getOperand(0), ImmOperand, 276341825Sdim N->getOperand(2)); 277341825Sdim else // Store 278341825Sdim CurDAG->UpdateNodeOperands(N, N->getOperand(0), Base.getOperand(0), 279341825Sdim ImmOperand, N->getOperand(3)); 280341825Sdim 281341825Sdim // The add-immediate may now be dead, in which case remove it. 282341825Sdim if (Base.getNode()->use_empty()) 283341825Sdim CurDAG->RemoveDeadNode(Base.getNode()); 284341825Sdim } 285341825Sdim} 286341825Sdim 287326938Sdim// This pass converts a legalized DAG into a RISCV-specific DAG, ready 288326938Sdim// for instruction scheduling. 289326938SdimFunctionPass *llvm::createRISCVISelDag(RISCVTargetMachine &TM) { 290326938Sdim return new RISCVDAGToDAGISel(TM); 291326938Sdim} 292