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, CodeGenOptLevel 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, |
52 | InlineAsm::ConstraintCode ConstraintID, |
53 | std::vector<SDValue> &OutOps) override; |
54 | |
55 | #include "CSKYGenDAGISel.inc" |
56 | }; |
57 | } // namespace |
58 | |
59 | char CSKYDAGToDAGISel::ID = 0; |
60 | |
61 | INITIALIZE_PASS(CSKYDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false) |
62 | |
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; |
69 | } |
70 | |
71 | SDLoc Dl(N); |
72 | unsigned Opcode = N->getOpcode(); |
73 | bool IsSelected = false; |
74 | |
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&: *MF); |
86 | ReplaceNode(F: N, T: CurDAG->getRegister(Reg: GP, VT: N->getValueType(ResNo: 0)).getNode()); |
87 | |
88 | IsSelected = true; |
89 | break; |
90 | } |
91 | case ISD::FrameIndex: { |
92 | SDValue Imm = CurDAG->getTargetConstant(0, Dl, MVT::i32); |
93 | int FI = cast<FrameIndexSDNode>(Val: N)->getIndex(); |
94 | SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::VT: i32); |
95 | ReplaceNode(N, CurDAG->getMachineNode(Subtarget->hasE2() ? CSKY::ADDI32 |
96 | : CSKY::ADDI16XZ, |
97 | Dl, MVT::i32, TFI, Imm)); |
98 | |
99 | IsSelected = true; |
100 | break; |
101 | } |
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; |
109 | } |
110 | |
111 | if (IsSelected) |
112 | return; |
113 | |
114 | // Select the default instruction. |
115 | SelectCode(N); |
116 | } |
117 | |
118 | bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) { |
119 | std::vector<SDValue> AsmNodeOperands; |
120 | InlineAsm::Flag Flag; |
121 | bool Changed = false; |
122 | unsigned NumOps = N->getNumOperands(); |
123 | |
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. |
128 | |
129 | SDLoc dl(N); |
130 | SDValue Glue = |
131 | N->getGluedNode() ? N->getOperand(Num: NumOps - 1) : SDValue(nullptr, 0); |
132 | |
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(Num: i); |
138 | AsmNodeOperands.push_back(x: op); |
139 | |
140 | if (i < InlineAsm::Op_FirstOperand) |
141 | continue; |
142 | |
143 | if (const auto *C = dyn_cast<ConstantSDNode>(Val: N->getOperand(Num: i))) |
144 | Flag = InlineAsm::Flag(C->getZExtValue()); |
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 (Flag.isImmKind()) { |
153 | SDValue op = N->getOperand(Num: ++i); |
154 | AsmNodeOperands.push_back(x: op); |
155 | continue; |
156 | } |
157 | |
158 | const unsigned NumRegs = Flag.getNumOperandRegisters(); |
159 | if (NumRegs) |
160 | OpChanged.push_back(Elt: 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 && Flag.isUseOperandTiedToDef(Idx&: 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 |
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(Num: ++i); |
176 | AsmNodeOperands.push_back(x: op); |
177 | continue; |
178 | } |
179 | |
180 | if (!Flag.isRegUseKind() && !Flag.isRegDefKind() && |
181 | !Flag.isRegDefEarlyClobberKind()) |
182 | continue; |
183 | |
184 | unsigned RC; |
185 | const bool HasRC = Flag.hasRegClassConstraint(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(Num: i + 1); |
192 | SDValue V1 = N->getOperand(Num: i + 2); |
193 | unsigned Reg0 = cast<RegisterSDNode>(Val&: V0)->getReg(); |
194 | unsigned Reg1 = cast<RegisterSDNode>(Val&: V1)->getReg(); |
195 | SDValue PairedReg; |
196 | MachineRegisterInfo &MRI = MF->getRegInfo(); |
197 | |
198 | if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) { |
199 | // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to |
200 | // the original GPRs. |
201 | |
202 | Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass); |
203 | PairedReg = CurDAG->getRegister(Reg: GPVR, MVT::VT: i64); |
204 | SDValue Chain = SDValue(N, 0); |
205 | |
206 | SDNode *GU = N->getGluedUser(); |
207 | SDValue RegCopy = |
208 | CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(R: 1)); |
209 | |
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(Chain: Sub0, dl, Reg: Reg0, N: Sub0, Glue: RegCopy.getValue(R: 1)); |
217 | SDValue T1 = CurDAG->getCopyToReg(Chain: Sub1, dl, Reg: Reg1, N: Sub1, Glue: T0.getValue(R: 1)); |
218 | |
219 | // Update the original glue user. |
220 | std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1); |
221 | Ops.push_back(x: T1.getValue(R: 1)); |
222 | CurDAG->UpdateNodeOperands(N: 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]; |
227 | |
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(R: 1)); |
231 | SDValue T1 = |
232 | CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(R: 1)); |
233 | SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0); |
234 | |
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(Reg: GPVR, MVT::VT: i64); |
239 | Chain = CurDAG->getCopyToReg(Chain: T1, dl, Reg: GPVR, N: Pair, Glue: T1.getValue(R: 1)); |
240 | |
241 | AsmNodeOperands[InlineAsm::Op_InputChain] = Chain; |
242 | Glue = Chain.getValue(R: 1); |
243 | } |
244 | |
245 | Changed = true; |
246 | |
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(x: PairedReg); |
260 | // Skip the next two GPRs. |
261 | i += 2; |
262 | } |
263 | } |
264 | |
265 | if (Glue.getNode()) |
266 | AsmNodeOperands.push_back(x: 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(F: N, T: New.getNode()); |
275 | return true; |
276 | } |
277 | |
278 | bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) { |
279 | SDLoc Dl(N); |
280 | auto VT = N->getValueType(ResNo: 0); |
281 | auto V = N->getOperand(Num: 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(F: SDValue(N, 0), T: V1); |
290 | ReplaceUses(F: SDValue(N, 1), T: 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(ResNo: 0); |
299 | auto Type1 = N->getValueType(ResNo: 1); |
300 | auto Op0 = N->getOperand(Num: 0); |
301 | auto Op1 = N->getOperand(Num: 1); |
302 | auto Op2 = N->getOperand(Num: 2); |
303 | |
304 | SDLoc Dl(N); |
305 | |
306 | if (isNullConstant(V: 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(V: 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(F: N, T: 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(ResNo: 0); |
342 | auto Type1 = N->getValueType(ResNo: 1); |
343 | auto Op0 = N->getOperand(Num: 0); |
344 | auto Op1 = N->getOperand(Num: 1); |
345 | auto Op2 = N->getOperand(Num: 2); |
346 | |
347 | SDLoc Dl(N); |
348 | |
349 | if (isNullConstant(V: 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(V: 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, DAG: CurDAG, Dl, OldCarry: 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, DAG: CurDAG, Dl, OldCarry: SDValue(NewNode, 1)); |
368 | |
369 | ReplaceUses(F: SDValue(N, 0), T: SDValue(NewNode, 0)); |
370 | ReplaceUses(F: SDValue(N, 1), T: 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(Opcode: TargetOpcode::REG_SEQUENCE, dl, VT, Ops); |
384 | } |
385 | |
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(x: Op); |
394 | return false; |
395 | default: |
396 | break; |
397 | } |
398 | |
399 | return true; |
400 | } |
401 | |
402 | FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM, |
403 | CodeGenOptLevel OptLevel) { |
404 | return new CSKYDAGToDAGISel(TM, OptLevel); |
405 | } |
406 | |