413 lines · cpp
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-exception6//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 21using namespace llvm;22 23#define DEBUG_TYPE "csky-isel"24#define PASS_NAME "CSKY DAG->DAG Pattern Instruction Selection"25 26namespace {27class CSKYDAGToDAGISel : public SelectionDAGISel {28 const CSKYSubtarget *Subtarget;29 30public: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 56class CSKYDAGToDAGISelLegacy : public SelectionDAGISelLegacy {57public:58 static char ID;59 explicit CSKYDAGToDAGISelLegacy(CSKYTargetMachine &TM,60 CodeGenOptLevel OptLevel)61 : SelectionDAGISelLegacy(62 ID, std::make_unique<CSKYDAGToDAGISel>(TM, OptLevel)) {}63};64} // namespace65 66char CSKYDAGToDAGISelLegacy::ID = 0;67 68INITIALIZE_PASS(CSKYDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)69 70void CSKYDAGToDAGISel::Select(SDNode *N) {71 // If we have a custom node, we have already selected72 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::ADDI32103 : 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 125bool 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 a134 // 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 else153 continue;154 155 // Immediate operands to inline asm in the SelectionDAG are modeled with156 // two operands. The first is a constant of value InlineAsm::Kind::Imm, and157 // the second is a constant with the value of the immediate. If we get here158 // 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 no172 // 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 two177 // operands: a constant of value InlineAsm::Kind::Mem followed by the input178 // operand. If we get here and we have a Kind::Mem, skip the next operand179 // (so it doesn't get misinterpreted), and continue. We do this here because180 // 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 to207 // 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 a232 // 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 two243 // 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 else261 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 285bool 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 303bool 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::ADDC32327 : CSKY::ADDC16,328 Dl, {Type0, Type1}, {Op0, Op1, Op2});329 }330 ReplaceNode(N, NewNode);331 return true;332}333 334static 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 346bool 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::SUBC32371 : 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 383SDNode *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 393bool 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 address399 // 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 409FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM,410 CodeGenOptLevel OptLevel) {411 return new CSKYDAGToDAGISelLegacy(TM, OptLevel);412}413