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