Path: blob/main/contrib/llvm-project/llvm/lib/Target/Sparc/SparcISelDAGToDAG.cpp
35268 views
//===-- SparcISelDAGToDAG.cpp - A dag to dag inst selector for Sparc ------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines an instruction selector for the SPARC target.9//10//===----------------------------------------------------------------------===//1112#include "SparcTargetMachine.h"13#include "llvm/CodeGen/MachineRegisterInfo.h"14#include "llvm/CodeGen/SelectionDAGISel.h"15#include "llvm/IR/Intrinsics.h"16#include "llvm/Support/Debug.h"17#include "llvm/Support/ErrorHandling.h"18#include "llvm/Support/raw_ostream.h"19using namespace llvm;2021#define DEBUG_TYPE "sparc-isel"22#define PASS_NAME "SPARC DAG->DAG Pattern Instruction Selection"2324//===----------------------------------------------------------------------===//25// Instruction Selector Implementation26//===----------------------------------------------------------------------===//2728//===--------------------------------------------------------------------===//29/// SparcDAGToDAGISel - SPARC specific code to select SPARC machine30/// instructions for SelectionDAG operations.31///32namespace {33class SparcDAGToDAGISel : public SelectionDAGISel {34/// Subtarget - Keep a pointer to the Sparc Subtarget around so that we can35/// make the right decision when generating code for different targets.36const SparcSubtarget *Subtarget = nullptr;3738public:39SparcDAGToDAGISel() = delete;4041explicit SparcDAGToDAGISel(SparcTargetMachine &tm) : SelectionDAGISel(tm) {}4243bool runOnMachineFunction(MachineFunction &MF) override {44Subtarget = &MF.getSubtarget<SparcSubtarget>();45return SelectionDAGISel::runOnMachineFunction(MF);46}4748void Select(SDNode *N) override;4950// Complex Pattern Selectors.51bool SelectADDRrr(SDValue N, SDValue &R1, SDValue &R2);52bool SelectADDRri(SDValue N, SDValue &Base, SDValue &Offset);5354/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for55/// inline asm expressions.56bool SelectInlineAsmMemoryOperand(const SDValue &Op,57InlineAsm::ConstraintCode ConstraintID,58std::vector<SDValue> &OutOps) override;5960// Include the pieces autogenerated from the target description.61#include "SparcGenDAGISel.inc"6263private:64SDNode* getGlobalBaseReg();65bool tryInlineAsm(SDNode *N);66};6768class SparcDAGToDAGISelLegacy : public SelectionDAGISelLegacy {69public:70static char ID;71explicit SparcDAGToDAGISelLegacy(SparcTargetMachine &tm)72: SelectionDAGISelLegacy(ID, std::make_unique<SparcDAGToDAGISel>(tm)) {}73};74} // end anonymous namespace7576char SparcDAGToDAGISelLegacy::ID = 0;7778INITIALIZE_PASS(SparcDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)7980SDNode* SparcDAGToDAGISel::getGlobalBaseReg() {81Register GlobalBaseReg = Subtarget->getInstrInfo()->getGlobalBaseReg(MF);82return CurDAG->getRegister(GlobalBaseReg,83TLI->getPointerTy(CurDAG->getDataLayout()))84.getNode();85}8687bool SparcDAGToDAGISel::SelectADDRri(SDValue Addr,88SDValue &Base, SDValue &Offset) {89if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {90Base = CurDAG->getTargetFrameIndex(91FIN->getIndex(), TLI->getPointerTy(CurDAG->getDataLayout()));92Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);93return true;94}95if (Addr.getOpcode() == ISD::TargetExternalSymbol ||96Addr.getOpcode() == ISD::TargetGlobalAddress ||97Addr.getOpcode() == ISD::TargetGlobalTLSAddress)98return false; // direct calls.99100if (Addr.getOpcode() == ISD::ADD) {101if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1))) {102if (isInt<13>(CN->getSExtValue())) {103if (FrameIndexSDNode *FIN =104dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) {105// Constant offset from frame ref.106Base = CurDAG->getTargetFrameIndex(107FIN->getIndex(), TLI->getPointerTy(CurDAG->getDataLayout()));108} else {109Base = Addr.getOperand(0);110}111Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr),112MVT::i32);113return true;114}115}116if (Addr.getOperand(0).getOpcode() == SPISD::Lo) {117Base = Addr.getOperand(1);118Offset = Addr.getOperand(0).getOperand(0);119return true;120}121if (Addr.getOperand(1).getOpcode() == SPISD::Lo) {122Base = Addr.getOperand(0);123Offset = Addr.getOperand(1).getOperand(0);124return true;125}126}127Base = Addr;128Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);129return true;130}131132bool SparcDAGToDAGISel::SelectADDRrr(SDValue Addr, SDValue &R1, SDValue &R2) {133if (Addr.getOpcode() == ISD::FrameIndex) return false;134if (Addr.getOpcode() == ISD::TargetExternalSymbol ||135Addr.getOpcode() == ISD::TargetGlobalAddress ||136Addr.getOpcode() == ISD::TargetGlobalTLSAddress)137return false; // direct calls.138139if (Addr.getOpcode() == ISD::ADD) {140if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1)))141if (isInt<13>(CN->getSExtValue()))142return false; // Let the reg+imm pattern catch this!143if (Addr.getOperand(0).getOpcode() == SPISD::Lo ||144Addr.getOperand(1).getOpcode() == SPISD::Lo)145return false; // Let the reg+imm pattern catch this!146R1 = Addr.getOperand(0);147R2 = Addr.getOperand(1);148return true;149}150151R1 = Addr;152R2 = CurDAG->getRegister(SP::G0, TLI->getPointerTy(CurDAG->getDataLayout()));153return true;154}155156157// Re-assemble i64 arguments split up in SelectionDAGBuilder's158// visitInlineAsm / GetRegistersForValue functions.159//160// Note: This function was copied from, and is essentially identical161// to ARMISelDAGToDAG::SelectInlineAsm. It is very unfortunate that162// such hacking-up is necessary; a rethink of how inline asm operands163// are handled may be in order to make doing this more sane.164//165// TODO: fix inline asm support so I can simply tell it that 'i64'166// inputs to asm need to be allocated to the IntPair register type,167// and have that work. Then, delete this function.168bool SparcDAGToDAGISel::tryInlineAsm(SDNode *N){169std::vector<SDValue> AsmNodeOperands;170InlineAsm::Flag Flag;171bool Changed = false;172unsigned NumOps = N->getNumOperands();173174// Normally, i64 data is bounded to two arbitrary GPRs for "%r"175// constraint. However, some instructions (e.g. ldd/std) require176// (even/even+1) GPRs.177178// So, here, we check for this case, and mutate the inlineasm to use179// a single IntPair register instead, which guarantees such even/odd180// placement.181182SDLoc dl(N);183SDValue Glue = N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue();184185SmallVector<bool, 8> OpChanged;186// Glue node will be appended late.187for(unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e; ++i) {188SDValue op = N->getOperand(i);189AsmNodeOperands.push_back(op);190191if (i < InlineAsm::Op_FirstOperand)192continue;193194if (const auto *C = dyn_cast<ConstantSDNode>(N->getOperand(i)))195Flag = InlineAsm::Flag(C->getZExtValue());196else197continue;198199// Immediate operands to inline asm in the SelectionDAG are modeled with200// two operands. The first is a constant of value InlineAsm::Kind::Imm, and201// the second is a constant with the value of the immediate. If we get here202// and we have a Kind::Imm, skip the next operand, and continue.203if (Flag.isImmKind()) {204SDValue op = N->getOperand(++i);205AsmNodeOperands.push_back(op);206continue;207}208209const unsigned NumRegs = Flag.getNumOperandRegisters();210if (NumRegs)211OpChanged.push_back(false);212213unsigned DefIdx = 0;214bool IsTiedToChangedOp = false;215// If it's a use that is tied with a previous def, it has no216// reg class constraint.217if (Changed && Flag.isUseOperandTiedToDef(DefIdx))218IsTiedToChangedOp = OpChanged[DefIdx];219220if (!Flag.isRegUseKind() && !Flag.isRegDefKind() &&221!Flag.isRegDefEarlyClobberKind())222continue;223224unsigned RC;225const bool HasRC = Flag.hasRegClassConstraint(RC);226if ((!IsTiedToChangedOp && (!HasRC || RC != SP::IntRegsRegClassID))227|| NumRegs != 2)228continue;229230assert((i+2 < NumOps) && "Invalid number of operands in inline asm");231SDValue V0 = N->getOperand(i+1);232SDValue V1 = N->getOperand(i+2);233Register Reg0 = cast<RegisterSDNode>(V0)->getReg();234Register Reg1 = cast<RegisterSDNode>(V1)->getReg();235SDValue PairedReg;236MachineRegisterInfo &MRI = MF->getRegInfo();237238if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {239// Replace the two GPRs with 1 GPRPair and copy values from GPRPair to240// the original GPRs.241242Register GPVR = MRI.createVirtualRegister(&SP::IntPairRegClass);243PairedReg = CurDAG->getRegister(GPVR, MVT::v2i32);244SDValue Chain = SDValue(N,0);245246SDNode *GU = N->getGluedUser();247SDValue RegCopy = CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::v2i32,248Chain.getValue(1));249250// Extract values from a GPRPair reg and copy to the original GPR reg.251SDValue Sub0 = CurDAG->getTargetExtractSubreg(SP::sub_even, dl, MVT::i32,252RegCopy);253SDValue Sub1 = CurDAG->getTargetExtractSubreg(SP::sub_odd, dl, MVT::i32,254RegCopy);255SDValue T0 = CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0,256RegCopy.getValue(1));257SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));258259// Update the original glue user.260std::vector<SDValue> Ops(GU->op_begin(), GU->op_end()-1);261Ops.push_back(T1.getValue(1));262CurDAG->UpdateNodeOperands(GU, Ops);263} else {264// For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a265// GPRPair and then pass the GPRPair to the inline asm.266SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];267268// As REG_SEQ doesn't take RegisterSDNode, we copy them first.269SDValue T0 = CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32,270Chain.getValue(1));271SDValue T1 = CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32,272T0.getValue(1));273SDValue Pair = SDValue(274CurDAG->getMachineNode(275TargetOpcode::REG_SEQUENCE, dl, MVT::v2i32,276{277CurDAG->getTargetConstant(SP::IntPairRegClassID, dl,278MVT::i32),279T0,280CurDAG->getTargetConstant(SP::sub_even, dl, MVT::i32),281T1,282CurDAG->getTargetConstant(SP::sub_odd, dl, MVT::i32),283}),2840);285286// Copy REG_SEQ into a GPRPair-typed VR and replace the original two287// i32 VRs of inline asm with it.288Register GPVR = MRI.createVirtualRegister(&SP::IntPairRegClass);289PairedReg = CurDAG->getRegister(GPVR, MVT::v2i32);290Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));291292AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;293Glue = Chain.getValue(1);294}295296Changed = true;297298if(PairedReg.getNode()) {299OpChanged[OpChanged.size() -1 ] = true;300Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/);301if (IsTiedToChangedOp)302Flag.setMatchingOp(DefIdx);303else304Flag.setRegClass(SP::IntPairRegClassID);305// Replace the current flag.306AsmNodeOperands[AsmNodeOperands.size() -1] = CurDAG->getTargetConstant(307Flag, dl, MVT::i32);308// Add the new register node and skip the original two GPRs.309AsmNodeOperands.push_back(PairedReg);310// Skip the next two GPRs.311i += 2;312}313}314315if (Glue.getNode())316AsmNodeOperands.push_back(Glue);317if (!Changed)318return false;319320SelectInlineAsmMemoryOperands(AsmNodeOperands, SDLoc(N));321322SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N),323CurDAG->getVTList(MVT::Other, MVT::Glue), AsmNodeOperands);324New->setNodeId(-1);325ReplaceNode(N, New.getNode());326return true;327}328329void SparcDAGToDAGISel::Select(SDNode *N) {330SDLoc dl(N);331if (N->isMachineOpcode()) {332N->setNodeId(-1);333return; // Already selected.334}335336switch (N->getOpcode()) {337default: break;338case ISD::INLINEASM:339case ISD::INLINEASM_BR: {340if (tryInlineAsm(N))341return;342break;343}344case SPISD::GLOBAL_BASE_REG:345ReplaceNode(N, getGlobalBaseReg());346return;347348case ISD::SDIV:349case ISD::UDIV: {350// sdivx / udivx handle 64-bit divides.351if (N->getValueType(0) == MVT::i64)352break;353// FIXME: should use a custom expander to expose the SRA to the dag.354SDValue DivLHS = N->getOperand(0);355SDValue DivRHS = N->getOperand(1);356357// Set the Y register to the high-part.358SDValue TopPart;359if (N->getOpcode() == ISD::SDIV) {360TopPart = SDValue(CurDAG->getMachineNode(SP::SRAri, dl, MVT::i32, DivLHS,361CurDAG->getTargetConstant(31, dl, MVT::i32)),3620);363} else {364TopPart = CurDAG->getRegister(SP::G0, MVT::i32);365}366TopPart = CurDAG->getCopyToReg(CurDAG->getEntryNode(), dl, SP::Y, TopPart,367SDValue())368.getValue(1);369370// FIXME: Handle div by immediate.371unsigned Opcode = N->getOpcode() == ISD::SDIV ? SP::SDIVrr : SP::UDIVrr;372CurDAG->SelectNodeTo(N, Opcode, MVT::i32, DivLHS, DivRHS, TopPart);373return;374}375}376377SelectCode(N);378}379380381/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for382/// inline asm expressions.383bool SparcDAGToDAGISel::SelectInlineAsmMemoryOperand(384const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,385std::vector<SDValue> &OutOps) {386SDValue Op0, Op1;387switch (ConstraintID) {388default: return true;389case InlineAsm::ConstraintCode::o:390case InlineAsm::ConstraintCode::m: // memory391if (!SelectADDRrr(Op, Op0, Op1))392SelectADDRri(Op, Op0, Op1);393break;394}395396OutOps.push_back(Op0);397OutOps.push_back(Op1);398return false;399}400401/// createSparcISelDag - This pass converts a legalized DAG into a402/// SPARC-specific DAG, ready for instruction scheduling.403///404FunctionPass *llvm::createSparcISelDag(SparcTargetMachine &TM) {405return new SparcDAGToDAGISelLegacy(TM);406}407408409