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