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