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