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