[llvm-shlib] Fix the version naming style of libLLVM for Windows (#85710)
[llvm-project.git] / llvm / lib / Target / CSKY / CSKYISelDAGToDAG.cpp
blobc0c23a45d1551442982382a2e88636c88336380d
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.
11 //===----------------------------------------------------------------------===//
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"
21 using namespace llvm;
23 #define DEBUG_TYPE "csky-isel"
24 #define PASS_NAME "CSKY DAG->DAG Pattern Instruction Selection"
26 namespace {
27 class CSKYDAGToDAGISel : public SelectionDAGISel {
28 const CSKYSubtarget *Subtarget;
30 public:
31 static char ID;
33 explicit CSKYDAGToDAGISel(CSKYTargetMachine &TM, CodeGenOptLevel OptLevel)
34 : SelectionDAGISel(ID, TM, OptLevel) {}
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;
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);
49 SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1);
51 bool SelectInlineAsmMemoryOperand(const SDValue &Op,
52 InlineAsm::ConstraintCode ConstraintID,
53 std::vector<SDValue> &OutOps) override;
55 #include "CSKYGenDAGISel.inc"
57 } // namespace
59 char CSKYDAGToDAGISel::ID = 0;
61 INITIALIZE_PASS(CSKYDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
63 void CSKYDAGToDAGISel::Select(SDNode *N) {
64 // If we have a custom node, we have already selected
65 if (N->isMachineOpcode()) {
66 LLVM_DEBUG(dbgs() << "== "; N->dump(CurDAG); dbgs() << "\n");
67 N->setNodeId(-1);
68 return;
71 SDLoc Dl(N);
72 unsigned Opcode = N->getOpcode();
73 bool IsSelected = false;
75 switch (Opcode) {
76 default:
77 break;
78 case ISD::UADDO_CARRY:
79 IsSelected = selectAddCarry(N);
80 break;
81 case ISD::USUBO_CARRY:
82 IsSelected = selectSubCarry(N);
83 break;
84 case ISD::GLOBAL_OFFSET_TABLE: {
85 Register GP = Subtarget->getInstrInfo()->getGlobalBaseReg(*MF);
86 ReplaceNode(N, CurDAG->getRegister(GP, N->getValueType(0)).getNode());
88 IsSelected = true;
89 break;
91 case ISD::FrameIndex: {
92 SDValue Imm = CurDAG->getTargetConstant(0, Dl, MVT::i32);
93 int FI = cast<FrameIndexSDNode>(N)->getIndex();
94 SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32);
95 ReplaceNode(N, CurDAG->getMachineNode(Subtarget->hasE2() ? CSKY::ADDI32
96 : CSKY::ADDI16XZ,
97 Dl, MVT::i32, TFI, Imm));
99 IsSelected = true;
100 break;
102 case CSKYISD::BITCAST_TO_LOHI:
103 IsSelected = selectBITCAST_TO_LOHI(N);
104 break;
105 case ISD::INLINEASM:
106 case ISD::INLINEASM_BR:
107 IsSelected = selectInlineAsm(N);
108 break;
111 if (IsSelected)
112 return;
114 // Select the default instruction.
115 SelectCode(N);
118 bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) {
119 std::vector<SDValue> AsmNodeOperands;
120 InlineAsm::Flag Flag;
121 bool Changed = false;
122 unsigned NumOps = N->getNumOperands();
124 // Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint.
125 // However, some instructions (e.g. mula.s32) require GPR pair.
126 // Since there is no constraint to explicitly specify a
127 // reg pair, we use GPRPair reg class for "%r" for 64-bit data.
129 SDLoc dl(N);
130 SDValue Glue =
131 N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0);
133 SmallVector<bool, 8> OpChanged;
134 // Glue node will be appended late.
135 for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e;
136 ++i) {
137 SDValue op = N->getOperand(i);
138 AsmNodeOperands.push_back(op);
140 if (i < InlineAsm::Op_FirstOperand)
141 continue;
143 if (const auto *C = dyn_cast<ConstantSDNode>(N->getOperand(i)))
144 Flag = InlineAsm::Flag(C->getZExtValue());
145 else
146 continue;
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 (Flag.isImmKind()) {
153 SDValue op = N->getOperand(++i);
154 AsmNodeOperands.push_back(op);
155 continue;
158 const unsigned NumRegs = Flag.getNumOperandRegisters();
159 if (NumRegs)
160 OpChanged.push_back(false);
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 && Flag.isUseOperandTiedToDef(DefIdx))
167 IsTiedToChangedOp = OpChanged[DefIdx];
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
172 // (so 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 (Flag.isMemKind()) {
175 SDValue op = N->getOperand(++i);
176 AsmNodeOperands.push_back(op);
177 continue;
180 if (!Flag.isRegUseKind() && !Flag.isRegDefKind() &&
181 !Flag.isRegDefEarlyClobberKind())
182 continue;
184 unsigned RC;
185 const bool HasRC = Flag.hasRegClassConstraint(RC);
186 if ((!IsTiedToChangedOp && (!HasRC || RC != CSKY::GPRRegClassID)) ||
187 NumRegs != 2)
188 continue;
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();
198 if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {
199 // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to
200 // the original GPRs.
202 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
203 PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
204 SDValue Chain = SDValue(N, 0);
206 SDNode *GU = N->getGluedUser();
207 SDValue RegCopy =
208 CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1));
210 // Extract values from a GPRPair reg and copy to the original GPR reg.
211 SDValue Sub0 =
212 CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy);
213 SDValue Sub1 =
214 CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy);
215 SDValue T0 =
216 CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));
217 SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));
219 // Update the original glue user.
220 std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
221 Ops.push_back(T1.getValue(1));
222 CurDAG->UpdateNodeOperands(GU, Ops);
223 } else {
224 // For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a
225 // GPRPair and then pass the GPRPair to the inline asm.
226 SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];
228 // As REG_SEQ doesn't take RegisterSDNode, we copy them first.
229 SDValue T0 =
230 CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1));
231 SDValue T1 =
232 CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1));
233 SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0);
235 // Copy REG_SEQ into a GPRPair-typed VR and replace the original two
236 // i32 VRs of inline asm with it.
237 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
238 PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
239 Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));
241 AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
242 Glue = Chain.getValue(1);
245 Changed = true;
247 if (PairedReg.getNode()) {
248 OpChanged[OpChanged.size() - 1] = true;
249 // TODO: maybe a setter for getNumOperandRegisters?
250 Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/);
251 if (IsTiedToChangedOp)
252 Flag.setMatchingOp(DefIdx);
253 else
254 Flag.setRegClass(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;
265 if (Glue.getNode())
266 AsmNodeOperands.push_back(Glue);
267 if (!Changed)
268 return false;
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;
278 bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) {
279 SDLoc Dl(N);
280 auto VT = N->getValueType(0);
281 auto V = N->getOperand(0);
283 if (!Subtarget->hasFPUv2DoubleFloat())
284 return false;
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);
289 ReplaceUses(SDValue(N, 0), V1);
290 ReplaceUses(SDValue(N, 1), V2);
291 CurDAG->RemoveDeadNode(N);
293 return true;
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);
304 SDLoc Dl(N);
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});
323 ReplaceNode(N, NewNode);
324 return true;
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);
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);
347 SDLoc Dl(N);
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});
367 auto CarryOut = InvertCarryFlag(Subtarget, CurDAG, Dl, SDValue(NewNode, 1));
369 ReplaceUses(SDValue(N, 0), SDValue(NewNode, 0));
370 ReplaceUses(SDValue(N, 1), CarryOut);
371 CurDAG->RemoveDeadNode(N);
373 return true;
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);
386 bool CSKYDAGToDAGISel::SelectInlineAsmMemoryOperand(
387 const SDValue &Op, const InlineAsm::ConstraintCode ConstraintID,
388 std::vector<SDValue> &OutOps) {
389 switch (ConstraintID) {
390 case InlineAsm::ConstraintCode::m:
391 // We just support simple memory operands that have a single address
392 // operand and need no special handling.
393 OutOps.push_back(Op);
394 return false;
395 default:
396 break;
399 return true;
402 FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM,
403 CodeGenOptLevel OptLevel) {
404 return new CSKYDAGToDAGISel(TM, OptLevel);