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 unsigned 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 unsigned Flag, Kind; 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 (ConstantSDNode *C = dyn_cast<ConstantSDNode>(N->getOperand(i))) { 190 Flag = C->getZExtValue(); 191 Kind = InlineAsm::getKind(Flag); 192 } 193 else 194 continue; 195 196 // Immediate operands to inline asm in the SelectionDAG are modeled with 197 // two operands. The first is a constant of value InlineAsm::Kind_Imm, and 198 // the second is a constant with the value of the immediate. If we get here 199 // and we have a Kind_Imm, skip the next operand, and continue. 200 if (Kind == InlineAsm::Kind_Imm) { 201 SDValue op = N->getOperand(++i); 202 AsmNodeOperands.push_back(op); 203 continue; 204 } 205 206 unsigned NumRegs = InlineAsm::getNumOperandRegisters(Flag); 207 if (NumRegs) 208 OpChanged.push_back(false); 209 210 unsigned DefIdx = 0; 211 bool IsTiedToChangedOp = false; 212 // If it's a use that is tied with a previous def, it has no 213 // reg class constraint. 214 if (Changed && InlineAsm::isUseOperandTiedToDef(Flag, DefIdx)) 215 IsTiedToChangedOp = OpChanged[DefIdx]; 216 217 if (Kind != InlineAsm::Kind_RegUse && Kind != InlineAsm::Kind_RegDef 218 && Kind != InlineAsm::Kind_RegDefEarlyClobber) 219 continue; 220 221 unsigned RC; 222 bool HasRC = InlineAsm::hasRegClassConstraint(Flag, RC); 223 if ((!IsTiedToChangedOp && (!HasRC || RC != SP::IntRegsRegClassID)) 224 || NumRegs != 2) 225 continue; 226 227 assert((i+2 < NumOps) && "Invalid number of operands in inline asm"); 228 SDValue V0 = N->getOperand(i+1); 229 SDValue V1 = N->getOperand(i+2); 230 Register Reg0 = cast<RegisterSDNode>(V0)->getReg(); 231 Register Reg1 = cast<RegisterSDNode>(V1)->getReg(); 232 SDValue PairedReg; 233 MachineRegisterInfo &MRI = MF->getRegInfo(); 234 235 if (Kind == InlineAsm::Kind_RegDef || 236 Kind == InlineAsm::Kind_RegDefEarlyClobber) { 237 // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to 238 // the original GPRs. 239 240 Register GPVR = MRI.createVirtualRegister(&SP::IntPairRegClass); 241 PairedReg = CurDAG->getRegister(GPVR, MVT::v2i32); 242 SDValue Chain = SDValue(N,0); 243 244 SDNode *GU = N->getGluedUser(); 245 SDValue RegCopy = CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::v2i32, 246 Chain.getValue(1)); 247 248 // Extract values from a GPRPair reg and copy to the original GPR reg. 249 SDValue Sub0 = CurDAG->getTargetExtractSubreg(SP::sub_even, dl, MVT::i32, 250 RegCopy); 251 SDValue Sub1 = CurDAG->getTargetExtractSubreg(SP::sub_odd, dl, MVT::i32, 252 RegCopy); 253 SDValue T0 = CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, 254 RegCopy.getValue(1)); 255 SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1)); 256 257 // Update the original glue user. 258 std::vector<SDValue> Ops(GU->op_begin(), GU->op_end()-1); 259 Ops.push_back(T1.getValue(1)); 260 CurDAG->UpdateNodeOperands(GU, Ops); 261 } 262 else { 263 // For Kind == InlineAsm::Kind_RegUse, we first copy two GPRs into a 264 // 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 two 286 // 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::getFlagWord(Kind, 1 /* RegNum*/); 300 if (IsTiedToChangedOp) 301 Flag = InlineAsm::getFlagWordForMatchingOp(Flag, DefIdx); 302 else 303 Flag = InlineAsm::getFlagWordForRegClass(Flag, 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 328 void 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 for 381 /// inline asm expressions. 382 bool 383 SparcDAGToDAGISel::SelectInlineAsmMemoryOperand(const SDValue &Op, 384 unsigned ConstraintID, 385 std::vector<SDValue> &OutOps) { 386 SDValue Op0, Op1; 387 switch (ConstraintID) { 388 default: return true; 389 case InlineAsm::Constraint_o: 390 case InlineAsm::Constraint_m: // memory 391 if (!SelectADDRrr(Op, Op0, Op1)) 392 SelectADDRri(Op, Op0, Op1); 393 break; 394 } 395 396 OutOps.push_back(Op0); 397 OutOps.push_back(Op1); 398 return false; 399 } 400 401 /// createSparcISelDag - This pass converts a legalized DAG into a 402 /// SPARC-specific DAG, ready for instruction scheduling. 403 /// 404 FunctionPass *llvm::createSparcISelDag(SparcTargetMachine &TM) { 405 return new SparcDAGToDAGISel(TM); 406 } 407