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