406 lines · cpp
1//===-- SparcISelDAGToDAG.cpp - A dag to dag inst selector for Sparc ------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines an instruction selector for the SPARC target.10//11//===----------------------------------------------------------------------===//12 13#include "SparcSelectionDAGInfo.h"14#include "SparcTargetMachine.h"15#include "llvm/CodeGen/MachineRegisterInfo.h"16#include "llvm/CodeGen/SelectionDAGISel.h"17#include "llvm/Support/ErrorHandling.h"18using namespace llvm;19 20#define DEBUG_TYPE "sparc-isel"21#define PASS_NAME "SPARC DAG->DAG Pattern Instruction Selection"22 23//===----------------------------------------------------------------------===//24// Instruction Selector Implementation25//===----------------------------------------------------------------------===//26 27//===--------------------------------------------------------------------===//28/// SparcDAGToDAGISel - SPARC specific code to select SPARC machine29/// instructions for SelectionDAG operations.30///31namespace {32class SparcDAGToDAGISel : public SelectionDAGISel {33 /// Subtarget - Keep a pointer to the Sparc Subtarget around so that we can34 /// make the right decision when generating code for different targets.35 const SparcSubtarget *Subtarget = nullptr;36 37public:38 SparcDAGToDAGISel() = delete;39 40 explicit SparcDAGToDAGISel(SparcTargetMachine &tm) : SelectionDAGISel(tm) {}41 42 bool runOnMachineFunction(MachineFunction &MF) override {43 Subtarget = &MF.getSubtarget<SparcSubtarget>();44 return SelectionDAGISel::runOnMachineFunction(MF);45 }46 47 void Select(SDNode *N) override;48 49 // Complex Pattern Selectors.50 bool SelectADDRrr(SDValue N, SDValue &R1, SDValue &R2);51 bool SelectADDRri(SDValue N, SDValue &Base, SDValue &Offset);52 53 /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for54 /// inline asm expressions.55 bool SelectInlineAsmMemoryOperand(const SDValue &Op,56 InlineAsm::ConstraintCode ConstraintID,57 std::vector<SDValue> &OutOps) override;58 59 // Include the pieces autogenerated from the target description.60#include "SparcGenDAGISel.inc"61 62private:63 SDNode* getGlobalBaseReg();64 bool tryInlineAsm(SDNode *N);65};66 67class SparcDAGToDAGISelLegacy : public SelectionDAGISelLegacy {68public:69 static char ID;70 explicit SparcDAGToDAGISelLegacy(SparcTargetMachine &tm)71 : SelectionDAGISelLegacy(ID, std::make_unique<SparcDAGToDAGISel>(tm)) {}72};73} // end anonymous namespace74 75char SparcDAGToDAGISelLegacy::ID = 0;76 77INITIALIZE_PASS(SparcDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)78 79SDNode* SparcDAGToDAGISel::getGlobalBaseReg() {80 Register GlobalBaseReg = Subtarget->getInstrInfo()->getGlobalBaseReg(MF);81 return CurDAG->getRegister(GlobalBaseReg,82 TLI->getPointerTy(CurDAG->getDataLayout()))83 .getNode();84}85 86bool SparcDAGToDAGISel::SelectADDRri(SDValue Addr,87 SDValue &Base, SDValue &Offset) {88 if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {89 Base = CurDAG->getTargetFrameIndex(90 FIN->getIndex(), TLI->getPointerTy(CurDAG->getDataLayout()));91 Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);92 return true;93 }94 if (Addr.getOpcode() == ISD::TargetExternalSymbol ||95 Addr.getOpcode() == ISD::TargetGlobalAddress ||96 Addr.getOpcode() == ISD::TargetGlobalTLSAddress)97 return false; // direct calls.98 99 if (Addr.getOpcode() == ISD::ADD) {100 if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1))) {101 if (isInt<13>(CN->getSExtValue())) {102 if (FrameIndexSDNode *FIN =103 dyn_cast<FrameIndexSDNode>(Addr.getOperand(0))) {104 // Constant offset from frame ref.105 Base = CurDAG->getTargetFrameIndex(106 FIN->getIndex(), TLI->getPointerTy(CurDAG->getDataLayout()));107 } else {108 Base = Addr.getOperand(0);109 }110 Offset = CurDAG->getSignedTargetConstant(CN->getSExtValue(),111 SDLoc(Addr), MVT::i32);112 return true;113 }114 }115 if (Addr.getOperand(0).getOpcode() == SPISD::Lo) {116 Base = Addr.getOperand(1);117 Offset = Addr.getOperand(0).getOperand(0);118 return true;119 }120 if (Addr.getOperand(1).getOpcode() == SPISD::Lo) {121 Base = Addr.getOperand(0);122 Offset = Addr.getOperand(1).getOperand(0);123 return true;124 }125 }126 Base = Addr;127 Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), MVT::i32);128 return true;129}130 131bool SparcDAGToDAGISel::SelectADDRrr(SDValue Addr, SDValue &R1, SDValue &R2) {132 if (Addr.getOpcode() == ISD::FrameIndex) return false;133 if (Addr.getOpcode() == ISD::TargetExternalSymbol ||134 Addr.getOpcode() == ISD::TargetGlobalAddress ||135 Addr.getOpcode() == ISD::TargetGlobalTLSAddress)136 return false; // direct calls.137 138 if (Addr.getOpcode() == ISD::ADD) {139 if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Addr.getOperand(1)))140 if (isInt<13>(CN->getSExtValue()))141 return false; // Let the reg+imm pattern catch this!142 if (Addr.getOperand(0).getOpcode() == SPISD::Lo ||143 Addr.getOperand(1).getOpcode() == SPISD::Lo)144 return false; // Let the reg+imm pattern catch this!145 R1 = Addr.getOperand(0);146 R2 = Addr.getOperand(1);147 return true;148 }149 150 R1 = Addr;151 R2 = CurDAG->getRegister(SP::G0, TLI->getPointerTy(CurDAG->getDataLayout()));152 return true;153}154 155 156// Re-assemble i64 arguments split up in SelectionDAGBuilder's157// visitInlineAsm / GetRegistersForValue functions.158//159// Note: This function was copied from, and is essentially identical160// to ARMISelDAGToDAG::SelectInlineAsm. It is very unfortunate that161// such hacking-up is necessary; a rethink of how inline asm operands162// are handled may be in order to make doing this more sane.163//164// TODO: fix inline asm support so I can simply tell it that 'i64'165// inputs to asm need to be allocated to the IntPair register type,166// and have that work. Then, delete this function.167bool SparcDAGToDAGISel::tryInlineAsm(SDNode *N){168 std::vector<SDValue> AsmNodeOperands;169 InlineAsm::Flag Flag;170 bool Changed = false;171 unsigned NumOps = N->getNumOperands();172 173 // Normally, i64 data is bounded to two arbitrary GPRs for "%r"174 // constraint. However, some instructions (e.g. ldd/std) require175 // (even/even+1) GPRs.176 177 // So, here, we check for this case, and mutate the inlineasm to use178 // a single IntPair register instead, which guarantees such even/odd179 // placement.180 181 SDLoc dl(N);182 SDValue Glue = N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue();183 184 SmallVector<bool, 8> OpChanged;185 // Glue node will be appended late.186 for(unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e; ++i) {187 SDValue op = N->getOperand(i);188 AsmNodeOperands.push_back(op);189 190 if (i < InlineAsm::Op_FirstOperand)191 continue;192 193 if (const auto *C = dyn_cast<ConstantSDNode>(N->getOperand(i)))194 Flag = InlineAsm::Flag(C->getZExtValue());195 else196 continue;197 198 // Immediate operands to inline asm in the SelectionDAG are modeled with199 // two operands. The first is a constant of value InlineAsm::Kind::Imm, and200 // the second is a constant with the value of the immediate. If we get here201 // and we have a Kind::Imm, skip the next operand, and continue.202 if (Flag.isImmKind()) {203 SDValue op = N->getOperand(++i);204 AsmNodeOperands.push_back(op);205 continue;206 }207 208 const unsigned NumRegs = Flag.getNumOperandRegisters();209 if (NumRegs)210 OpChanged.push_back(false);211 212 unsigned DefIdx = 0;213 bool IsTiedToChangedOp = false;214 // If it's a use that is tied with a previous def, it has no215 // reg class constraint.216 if (Changed && Flag.isUseOperandTiedToDef(DefIdx))217 IsTiedToChangedOp = OpChanged[DefIdx];218 219 if (!Flag.isRegUseKind() && !Flag.isRegDefKind() &&220 !Flag.isRegDefEarlyClobberKind())221 continue;222 223 unsigned RC;224 const bool HasRC = Flag.hasRegClassConstraint(RC);225 if ((!IsTiedToChangedOp && (!HasRC || RC != SP::IntRegsRegClassID))226 || NumRegs != 2)227 continue;228 229 assert((i+2 < NumOps) && "Invalid number of operands in inline asm");230 SDValue V0 = N->getOperand(i+1);231 SDValue V1 = N->getOperand(i+2);232 Register Reg0 = cast<RegisterSDNode>(V0)->getReg();233 Register Reg1 = cast<RegisterSDNode>(V1)->getReg();234 SDValue PairedReg;235 MachineRegisterInfo &MRI = MF->getRegInfo();236 237 if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {238 // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to239 // the original GPRs.240 241 Register GPVR = MRI.createVirtualRegister(&SP::IntPairRegClass);242 PairedReg = CurDAG->getRegister(GPVR, MVT::v2i32);243 SDValue Chain = SDValue(N,0);244 245 SDNode *GU = N->getGluedUser();246 SDValue RegCopy = CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::v2i32,247 Chain.getValue(1));248 249 // Extract values from a GPRPair reg and copy to the original GPR reg.250 SDValue Sub0 = CurDAG->getTargetExtractSubreg(SP::sub_even, dl, MVT::i32,251 RegCopy);252 SDValue Sub1 = CurDAG->getTargetExtractSubreg(SP::sub_odd, dl, MVT::i32,253 RegCopy);254 SDValue T0 = CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0,255 RegCopy.getValue(1));256 SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));257 258 // Update the original glue user.259 std::vector<SDValue> Ops(GU->op_begin(), GU->op_end()-1);260 Ops.push_back(T1.getValue(1));261 CurDAG->UpdateNodeOperands(GU, Ops);262 } else {263 // For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a264 // GPRPair and then pass the GPRPair to the inline asm.265 SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];266 267 // As REG_SEQ doesn't take RegisterSDNode, we copy them first.268 SDValue T0 = CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32,269 Chain.getValue(1));270 SDValue T1 = CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32,271 T0.getValue(1));272 SDValue Pair = SDValue(273 CurDAG->getMachineNode(274 TargetOpcode::REG_SEQUENCE, dl, MVT::v2i32,275 {276 CurDAG->getTargetConstant(SP::IntPairRegClassID, dl,277 MVT::i32),278 T0,279 CurDAG->getTargetConstant(SP::sub_even, dl, MVT::i32),280 T1,281 CurDAG->getTargetConstant(SP::sub_odd, dl, MVT::i32),282 }),283 0);284 285 // Copy REG_SEQ into a GPRPair-typed VR and replace the original two286 // i32 VRs of inline asm with it.287 Register GPVR = MRI.createVirtualRegister(&SP::IntPairRegClass);288 PairedReg = CurDAG->getRegister(GPVR, MVT::v2i32);289 Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));290 291 AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;292 Glue = Chain.getValue(1);293 }294 295 Changed = true;296 297 if(PairedReg.getNode()) {298 OpChanged[OpChanged.size() -1 ] = true;299 Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/);300 if (IsTiedToChangedOp)301 Flag.setMatchingOp(DefIdx);302 else303 Flag.setRegClass(SP::IntPairRegClassID);304 // Replace the current flag.305 AsmNodeOperands[AsmNodeOperands.size() -1] = CurDAG->getTargetConstant(306 Flag, dl, MVT::i32);307 // Add the new register node and skip the original two GPRs.308 AsmNodeOperands.push_back(PairedReg);309 // Skip the next two GPRs.310 i += 2;311 }312 }313 314 if (Glue.getNode())315 AsmNodeOperands.push_back(Glue);316 if (!Changed)317 return false;318 319 SelectInlineAsmMemoryOperands(AsmNodeOperands, SDLoc(N));320 321 SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N),322 CurDAG->getVTList(MVT::Other, MVT::Glue), AsmNodeOperands);323 New->setNodeId(-1);324 ReplaceNode(N, New.getNode());325 return true;326}327 328void SparcDAGToDAGISel::Select(SDNode *N) {329 SDLoc dl(N);330 if (N->isMachineOpcode()) {331 N->setNodeId(-1);332 return; // Already selected.333 }334 335 switch (N->getOpcode()) {336 default: break;337 case ISD::INLINEASM:338 case ISD::INLINEASM_BR: {339 if (tryInlineAsm(N))340 return;341 break;342 }343 case SPISD::GLOBAL_BASE_REG:344 ReplaceNode(N, getGlobalBaseReg());345 return;346 347 case ISD::SDIV:348 case ISD::UDIV: {349 // sdivx / udivx handle 64-bit divides.350 if (N->getValueType(0) == MVT::i64)351 break;352 // FIXME: should use a custom expander to expose the SRA to the dag.353 SDValue DivLHS = N->getOperand(0);354 SDValue DivRHS = N->getOperand(1);355 356 // Set the Y register to the high-part.357 SDValue TopPart;358 if (N->getOpcode() == ISD::SDIV) {359 TopPart = SDValue(CurDAG->getMachineNode(SP::SRAri, dl, MVT::i32, DivLHS,360 CurDAG->getTargetConstant(31, dl, MVT::i32)),361 0);362 } else {363 TopPart = CurDAG->getRegister(SP::G0, MVT::i32);364 }365 TopPart = CurDAG->getCopyToReg(CurDAG->getEntryNode(), dl, SP::Y, TopPart,366 SDValue())367 .getValue(1);368 369 // FIXME: Handle div by immediate.370 unsigned Opcode = N->getOpcode() == ISD::SDIV ? SP::SDIVrr : SP::UDIVrr;371 CurDAG->SelectNodeTo(N, Opcode, MVT::i32, DivLHS, DivRHS, TopPart);372 return;373 }374 }375 376 SelectCode(N);377}378 379 380/// SelectInlineAsmMemoryOperand - Implement addressing mode selection for381/// inline asm expressions.382bool SparcDAGToDAGISel::SelectInlineAsmMemoryOperand(383 const SDValue &Op, InlineAsm::ConstraintCode ConstraintID,384 std::vector<SDValue> &OutOps) {385 SDValue Op0, Op1;386 switch (ConstraintID) {387 default: return true;388 case InlineAsm::ConstraintCode::o:389 case InlineAsm::ConstraintCode::m: // memory390 if (!SelectADDRrr(Op, Op0, Op1))391 SelectADDRri(Op, Op0, Op1);392 break;393 }394 395 OutOps.push_back(Op0);396 OutOps.push_back(Op1);397 return false;398}399 400/// createSparcISelDag - This pass converts a legalized DAG into a401/// SPARC-specific DAG, ready for instruction scheduling.402///403FunctionPass *llvm::createSparcISelDag(SparcTargetMachine &TM) {404 return new SparcDAGToDAGISelLegacy(TM);405}406