1 //===-- CSKYISelDAGToDAG.cpp - A dag to dag inst selector for CSKY---------===// 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 CSKY target. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "CSKY.h" 14 #include "CSKYSubtarget.h" 15 #include "CSKYTargetMachine.h" 16 #include "MCTargetDesc/CSKYMCTargetDesc.h" 17 #include "llvm/CodeGen/MachineFrameInfo.h" 18 #include "llvm/CodeGen/SelectionDAG.h" 19 #include "llvm/CodeGen/SelectionDAGISel.h" 20 21 using namespace llvm; 22 23 #define DEBUG_TYPE "csky-isel" 24 25 namespace { 26 class CSKYDAGToDAGISel : public SelectionDAGISel { 27 const CSKYSubtarget *Subtarget; 28 29 public: 30 explicit CSKYDAGToDAGISel(CSKYTargetMachine &TM) : SelectionDAGISel(TM) {} 31 32 StringRef getPassName() const override { 33 return "CSKY DAG->DAG Pattern Instruction Selection"; 34 } 35 36 bool runOnMachineFunction(MachineFunction &MF) override { 37 // Reset the subtarget each time through. 38 Subtarget = &MF.getSubtarget<CSKYSubtarget>(); 39 SelectionDAGISel::runOnMachineFunction(MF); 40 return true; 41 } 42 43 void Select(SDNode *N) override; 44 bool selectAddCarry(SDNode *N); 45 bool selectSubCarry(SDNode *N); 46 bool selectBITCAST_TO_LOHI(SDNode *N); 47 bool selectInlineAsm(SDNode *N); 48 49 SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1); 50 51 bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 52 std::vector<SDValue> &OutOps) override; 53 54 #include "CSKYGenDAGISel.inc" 55 }; 56 } // namespace 57 58 void CSKYDAGToDAGISel::Select(SDNode *N) { 59 // If we have a custom node, we have already selected 60 if (N->isMachineOpcode()) { 61 LLVM_DEBUG(dbgs() << "== "; N->dump(CurDAG); dbgs() << "\n"); 62 N->setNodeId(-1); 63 return; 64 } 65 66 SDLoc Dl(N); 67 unsigned Opcode = N->getOpcode(); 68 bool IsSelected = false; 69 70 switch (Opcode) { 71 default: 72 break; 73 case ISD::ADDCARRY: 74 IsSelected = selectAddCarry(N); 75 break; 76 case ISD::SUBCARRY: 77 IsSelected = selectSubCarry(N); 78 break; 79 case ISD::GLOBAL_OFFSET_TABLE: { 80 Register GP = Subtarget->getInstrInfo()->getGlobalBaseReg(*MF); 81 ReplaceNode(N, CurDAG->getRegister(GP, N->getValueType(0)).getNode()); 82 83 IsSelected = true; 84 break; 85 } 86 case ISD::FrameIndex: { 87 SDValue Imm = CurDAG->getTargetConstant(0, Dl, MVT::i32); 88 int FI = cast<FrameIndexSDNode>(N)->getIndex(); 89 SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32); 90 ReplaceNode(N, CurDAG->getMachineNode(Subtarget->hasE2() ? CSKY::ADDI32 91 : CSKY::ADDI16XZ, 92 Dl, MVT::i32, TFI, Imm)); 93 94 IsSelected = true; 95 break; 96 } 97 case CSKYISD::BITCAST_TO_LOHI: 98 IsSelected = selectBITCAST_TO_LOHI(N); 99 break; 100 case ISD::INLINEASM: 101 case ISD::INLINEASM_BR: 102 IsSelected = selectInlineAsm(N); 103 break; 104 } 105 106 if (IsSelected) 107 return; 108 109 // Select the default instruction. 110 SelectCode(N); 111 } 112 113 bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) { 114 std::vector<SDValue> AsmNodeOperands; 115 unsigned Flag, Kind; 116 bool Changed = false; 117 unsigned NumOps = N->getNumOperands(); 118 119 // Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint. 120 // However, some instructions (e.g. mula.s32) require GPR pair. 121 // Since there is no constraint to explicitly specify a 122 // reg pair, we use GPRPair reg class for "%r" for 64-bit data. 123 124 SDLoc dl(N); 125 SDValue Glue = 126 N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0); 127 128 SmallVector<bool, 8> OpChanged; 129 // Glue node will be appended late. 130 for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e; 131 ++i) { 132 SDValue op = N->getOperand(i); 133 AsmNodeOperands.push_back(op); 134 135 if (i < InlineAsm::Op_FirstOperand) 136 continue; 137 138 if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(N->getOperand(i))) { 139 Flag = C->getZExtValue(); 140 Kind = InlineAsm::getKind(Flag); 141 } else 142 continue; 143 144 // Immediate operands to inline asm in the SelectionDAG are modeled with 145 // two operands. The first is a constant of value InlineAsm::Kind_Imm, and 146 // the second is a constant with the value of the immediate. If we get here 147 // and we have a Kind_Imm, skip the next operand, and continue. 148 if (Kind == InlineAsm::Kind_Imm) { 149 SDValue op = N->getOperand(++i); 150 AsmNodeOperands.push_back(op); 151 continue; 152 } 153 154 unsigned NumRegs = InlineAsm::getNumOperandRegisters(Flag); 155 if (NumRegs) 156 OpChanged.push_back(false); 157 158 unsigned DefIdx = 0; 159 bool IsTiedToChangedOp = false; 160 // If it's a use that is tied with a previous def, it has no 161 // reg class constraint. 162 if (Changed && InlineAsm::isUseOperandTiedToDef(Flag, DefIdx)) 163 IsTiedToChangedOp = OpChanged[DefIdx]; 164 165 // Memory operands to inline asm in the SelectionDAG are modeled with two 166 // operands: a constant of value InlineAsm::Kind_Mem followed by the input 167 // operand. If we get here and we have a Kind_Mem, skip the next operand (so 168 // it doesn't get misinterpreted), and continue. We do this here because 169 // it's important to update the OpChanged array correctly before moving on. 170 if (Kind == InlineAsm::Kind_Mem) { 171 SDValue op = N->getOperand(++i); 172 AsmNodeOperands.push_back(op); 173 continue; 174 } 175 176 if (Kind != InlineAsm::Kind_RegUse && Kind != InlineAsm::Kind_RegDef && 177 Kind != InlineAsm::Kind_RegDefEarlyClobber) 178 continue; 179 180 unsigned RC; 181 bool HasRC = InlineAsm::hasRegClassConstraint(Flag, RC); 182 if ((!IsTiedToChangedOp && (!HasRC || RC != CSKY::GPRRegClassID)) || 183 NumRegs != 2) 184 continue; 185 186 assert((i + 2 < NumOps) && "Invalid number of operands in inline asm"); 187 SDValue V0 = N->getOperand(i + 1); 188 SDValue V1 = N->getOperand(i + 2); 189 unsigned Reg0 = cast<RegisterSDNode>(V0)->getReg(); 190 unsigned Reg1 = cast<RegisterSDNode>(V1)->getReg(); 191 SDValue PairedReg; 192 MachineRegisterInfo &MRI = MF->getRegInfo(); 193 194 if (Kind == InlineAsm::Kind_RegDef || 195 Kind == InlineAsm::Kind_RegDefEarlyClobber) { 196 // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to 197 // the original GPRs. 198 199 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass); 200 PairedReg = CurDAG->getRegister(GPVR, MVT::i64); 201 SDValue Chain = SDValue(N, 0); 202 203 SDNode *GU = N->getGluedUser(); 204 SDValue RegCopy = 205 CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1)); 206 207 // Extract values from a GPRPair reg and copy to the original GPR reg. 208 SDValue Sub0 = 209 CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy); 210 SDValue Sub1 = 211 CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy); 212 SDValue T0 = 213 CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1)); 214 SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1)); 215 216 // Update the original glue user. 217 std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1); 218 Ops.push_back(T1.getValue(1)); 219 CurDAG->UpdateNodeOperands(GU, Ops); 220 } else { 221 // For Kind == InlineAsm::Kind_RegUse, we first copy two GPRs into a 222 // GPRPair and then pass the GPRPair to the inline asm. 223 SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain]; 224 225 // As REG_SEQ doesn't take RegisterSDNode, we copy them first. 226 SDValue T0 = 227 CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1)); 228 SDValue T1 = 229 CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1)); 230 SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0); 231 232 // Copy REG_SEQ into a GPRPair-typed VR and replace the original two 233 // i32 VRs of inline asm with it. 234 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass); 235 PairedReg = CurDAG->getRegister(GPVR, MVT::i64); 236 Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1)); 237 238 AsmNodeOperands[InlineAsm::Op_InputChain] = Chain; 239 Glue = Chain.getValue(1); 240 } 241 242 Changed = true; 243 244 if (PairedReg.getNode()) { 245 OpChanged[OpChanged.size() - 1] = true; 246 Flag = InlineAsm::getFlagWord(Kind, 1 /* RegNum*/); 247 if (IsTiedToChangedOp) 248 Flag = InlineAsm::getFlagWordForMatchingOp(Flag, DefIdx); 249 else 250 Flag = InlineAsm::getFlagWordForRegClass(Flag, CSKY::GPRPairRegClassID); 251 // Replace the current flag. 252 AsmNodeOperands[AsmNodeOperands.size() - 1] = 253 CurDAG->getTargetConstant(Flag, dl, MVT::i32); 254 // Add the new register node and skip the original two GPRs. 255 AsmNodeOperands.push_back(PairedReg); 256 // Skip the next two GPRs. 257 i += 2; 258 } 259 } 260 261 if (Glue.getNode()) 262 AsmNodeOperands.push_back(Glue); 263 if (!Changed) 264 return false; 265 266 SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N), 267 CurDAG->getVTList(MVT::Other, MVT::Glue), 268 AsmNodeOperands); 269 New->setNodeId(-1); 270 ReplaceNode(N, New.getNode()); 271 return true; 272 } 273 274 bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) { 275 SDLoc Dl(N); 276 auto VT = N->getValueType(0); 277 auto V = N->getOperand(0); 278 279 if (!Subtarget->hasFPUv2DoubleFloat()) 280 return false; 281 282 SDValue V1 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRL_D, Dl, VT, V), 0); 283 SDValue V2 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRH_D, Dl, VT, V), 0); 284 285 ReplaceUses(SDValue(N, 0), V1); 286 ReplaceUses(SDValue(N, 1), V2); 287 CurDAG->RemoveDeadNode(N); 288 289 return true; 290 } 291 292 bool CSKYDAGToDAGISel::selectAddCarry(SDNode *N) { 293 MachineSDNode *NewNode = nullptr; 294 auto Type0 = N->getValueType(0); 295 auto Type1 = N->getValueType(1); 296 auto Op0 = N->getOperand(0); 297 auto Op1 = N->getOperand(1); 298 auto Op2 = N->getOperand(2); 299 300 SDLoc Dl(N); 301 302 if (isNullConstant(Op2)) { 303 auto *CA = CurDAG->getMachineNode( 304 Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1); 305 NewNode = CurDAG->getMachineNode( 306 Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1}, 307 {Op0, Op1, SDValue(CA, 0)}); 308 } else if (isOneConstant(Op2)) { 309 auto *CA = CurDAG->getMachineNode( 310 Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1); 311 NewNode = CurDAG->getMachineNode( 312 Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1}, 313 {Op0, Op1, SDValue(CA, 0)}); 314 } else { 315 NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::ADDC32 316 : CSKY::ADDC16, 317 Dl, {Type0, Type1}, {Op0, Op1, Op2}); 318 } 319 ReplaceNode(N, NewNode); 320 return true; 321 } 322 323 static SDValue InvertCarryFlag(const CSKYSubtarget *Subtarget, 324 SelectionDAG *DAG, SDLoc Dl, SDValue OldCarry) { 325 auto NewCarryReg = 326 DAG->getMachineNode(Subtarget->has2E3() ? CSKY::MVCV32 : CSKY::MVCV16, Dl, 327 MVT::i32, OldCarry); 328 auto NewCarry = 329 DAG->getMachineNode(Subtarget->hasE2() ? CSKY::BTSTI32 : CSKY::BTSTI16, 330 Dl, OldCarry.getValueType(), SDValue(NewCarryReg, 0), 331 DAG->getTargetConstant(0, Dl, MVT::i32)); 332 return SDValue(NewCarry, 0); 333 } 334 335 bool CSKYDAGToDAGISel::selectSubCarry(SDNode *N) { 336 MachineSDNode *NewNode = nullptr; 337 auto Type0 = N->getValueType(0); 338 auto Type1 = N->getValueType(1); 339 auto Op0 = N->getOperand(0); 340 auto Op1 = N->getOperand(1); 341 auto Op2 = N->getOperand(2); 342 343 SDLoc Dl(N); 344 345 if (isNullConstant(Op2)) { 346 auto *CA = CurDAG->getMachineNode( 347 Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1); 348 NewNode = CurDAG->getMachineNode( 349 Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1}, 350 {Op0, Op1, SDValue(CA, 0)}); 351 } else if (isOneConstant(Op2)) { 352 auto *CA = CurDAG->getMachineNode( 353 Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1); 354 NewNode = CurDAG->getMachineNode( 355 Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1}, 356 {Op0, Op1, SDValue(CA, 0)}); 357 } else { 358 auto CarryIn = InvertCarryFlag(Subtarget, CurDAG, Dl, Op2); 359 NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::SUBC32 360 : CSKY::SUBC16, 361 Dl, {Type0, Type1}, {Op0, Op1, CarryIn}); 362 } 363 auto CarryOut = InvertCarryFlag(Subtarget, CurDAG, Dl, SDValue(NewNode, 1)); 364 365 ReplaceUses(SDValue(N, 0), SDValue(NewNode, 0)); 366 ReplaceUses(SDValue(N, 1), CarryOut); 367 CurDAG->RemoveDeadNode(N); 368 369 return true; 370 } 371 372 SDNode *CSKYDAGToDAGISel::createGPRPairNode(EVT VT, SDValue V0, SDValue V1) { 373 SDLoc dl(V0.getNode()); 374 SDValue RegClass = 375 CurDAG->getTargetConstant(CSKY::GPRPairRegClassID, dl, MVT::i32); 376 SDValue SubReg0 = CurDAG->getTargetConstant(CSKY::sub32_0, dl, MVT::i32); 377 SDValue SubReg1 = CurDAG->getTargetConstant(CSKY::sub32_32, dl, MVT::i32); 378 const SDValue Ops[] = {RegClass, V0, SubReg0, V1, SubReg1}; 379 return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops); 380 } 381 382 bool CSKYDAGToDAGISel::SelectInlineAsmMemoryOperand( 383 const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 384 switch (ConstraintID) { 385 case InlineAsm::Constraint_m: 386 // We just support simple memory operands that have a single address 387 // operand and need no special handling. 388 OutOps.push_back(Op); 389 return false; 390 default: 391 break; 392 } 393 394 return true; 395 } 396 397 FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM) { 398 return new CSKYDAGToDAGISel(TM); 399 } 400