1 | //===-- SparcISelDAGToDAG.cpp - A dag to dag inst selector for Sparc ------===// |
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 SPARC target. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "SparcSelectionDAGInfo.h" |
14 | #include "SparcTargetMachine.h" |
15 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
16 | #include "llvm/CodeGen/SelectionDAGISel.h" |
17 | #include "llvm/Support/ErrorHandling.h" |
18 | using namespace llvm; |
19 | |
20 | #define DEBUG_TYPE "sparc-isel" |
21 | #define PASS_NAME "SPARC DAG->DAG Pattern Instruction Selection" |
22 | |
23 | //===----------------------------------------------------------------------===// |
24 | // Instruction Selector Implementation |
25 | //===----------------------------------------------------------------------===// |
26 | |
27 | //===--------------------------------------------------------------------===// |
28 | /// SparcDAGToDAGISel - SPARC specific code to select SPARC machine |
29 | /// instructions for SelectionDAG operations. |
30 | /// |
31 | namespace { |
32 | class SparcDAGToDAGISel : public SelectionDAGISel { |
33 | /// Subtarget - Keep a pointer to the Sparc Subtarget around so that we can |
34 | /// make the right decision when generating code for different targets. |
35 | const SparcSubtarget *Subtarget = nullptr; |
36 | |
37 | public: |
38 | SparcDAGToDAGISel() = delete; |
39 | |
40 | explicit SparcDAGToDAGISel(SparcTargetMachine &tm) : SelectionDAGISel(tm) {} |
41 | |
42 | bool runOnMachineFunction(MachineFunction &MF) override { |
43 | Subtarget = &MF.getSubtarget<SparcSubtarget>(); |
44 | return SelectionDAGISel::runOnMachineFunction(mf&: MF); |
45 | } |
46 | |
47 | void Select(SDNode *N) override; |
48 | |
49 | // Complex Pattern Selectors. |
50 | bool SelectADDRrr(SDValue N, SDValue &R1, SDValue &R2); |
51 | bool SelectADDRri(SDValue N, SDValue &Base, SDValue &Offset); |
52 | |
53 | /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for |
54 | /// inline asm expressions. |
55 | bool SelectInlineAsmMemoryOperand(const SDValue &Op, |
56 | InlineAsm::ConstraintCode ConstraintID, |
57 | std::vector<SDValue> &OutOps) override; |
58 | |
59 | // Include the pieces autogenerated from the target description. |
60 | #include "SparcGenDAGISel.inc" |
61 | |
62 | private: |
63 | SDNode* getGlobalBaseReg(); |
64 | bool tryInlineAsm(SDNode *N); |
65 | }; |
66 | |
67 | class SparcDAGToDAGISelLegacy : public SelectionDAGISelLegacy { |
68 | public: |
69 | static char ID; |
70 | explicit SparcDAGToDAGISelLegacy(SparcTargetMachine &tm) |
71 | : SelectionDAGISelLegacy(ID, std::make_unique<SparcDAGToDAGISel>(args&: tm)) {} |
72 | }; |
73 | } // end anonymous namespace |
74 | |
75 | char SparcDAGToDAGISelLegacy::ID = 0; |
76 | |
77 | INITIALIZE_PASS(SparcDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false) |
78 | |
79 | SDNode* SparcDAGToDAGISel::getGlobalBaseReg() { |
80 | Register GlobalBaseReg = Subtarget->getInstrInfo()->getGlobalBaseReg(MF); |
81 | return CurDAG->getRegister(Reg: GlobalBaseReg, |
82 | VT: TLI->getPointerTy(DL: CurDAG->getDataLayout())) |
83 | .getNode(); |
84 | } |
85 | |
86 | bool SparcDAGToDAGISel::SelectADDRri(SDValue Addr, |
87 | SDValue &Base, SDValue &Offset) { |
88 | if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Val&: Addr)) { |
89 | Base = CurDAG->getTargetFrameIndex( |
90 | FI: FIN->getIndex(), VT: TLI->getPointerTy(DL: CurDAG->getDataLayout())); |
91 | Offset = CurDAG->getTargetConstant(Val: 0, DL: SDLoc(Addr), VT: MVT::i32); |
92 | return true; |
93 | } |
94 | if (Addr.getOpcode() == ISD::TargetExternalSymbol || |
95 | Addr.getOpcode() == ISD::TargetGlobalAddress || |
96 | Addr.getOpcode() == ISD::TargetGlobalTLSAddress) |
97 | return false; // direct calls. |
98 | |
99 | if (Addr.getOpcode() == ISD::ADD) { |
100 | if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Val: Addr.getOperand(i: 1))) { |
101 | if (isInt<13>(x: CN->getSExtValue())) { |
102 | if (FrameIndexSDNode *FIN = |
103 | dyn_cast<FrameIndexSDNode>(Val: Addr.getOperand(i: 0))) { |
104 | // Constant offset from frame ref. |
105 | Base = CurDAG->getTargetFrameIndex( |
106 | FI: FIN->getIndex(), VT: TLI->getPointerTy(DL: CurDAG->getDataLayout())); |
107 | } else { |
108 | Base = Addr.getOperand(i: 0); |
109 | } |
110 | Offset = CurDAG->getSignedTargetConstant(Val: CN->getSExtValue(), |
111 | DL: SDLoc(Addr), VT: MVT::i32); |
112 | return true; |
113 | } |
114 | } |
115 | if (Addr.getOperand(i: 0).getOpcode() == SPISD::Lo) { |
116 | Base = Addr.getOperand(i: 1); |
117 | Offset = Addr.getOperand(i: 0).getOperand(i: 0); |
118 | return true; |
119 | } |
120 | if (Addr.getOperand(i: 1).getOpcode() == SPISD::Lo) { |
121 | Base = Addr.getOperand(i: 0); |
122 | Offset = Addr.getOperand(i: 1).getOperand(i: 0); |
123 | return true; |
124 | } |
125 | } |
126 | Base = Addr; |
127 | Offset = CurDAG->getTargetConstant(Val: 0, DL: SDLoc(Addr), VT: MVT::i32); |
128 | return true; |
129 | } |
130 | |
131 | bool SparcDAGToDAGISel::SelectADDRrr(SDValue Addr, SDValue &R1, SDValue &R2) { |
132 | if (Addr.getOpcode() == ISD::FrameIndex) return false; |
133 | if (Addr.getOpcode() == ISD::TargetExternalSymbol || |
134 | Addr.getOpcode() == ISD::TargetGlobalAddress || |
135 | Addr.getOpcode() == ISD::TargetGlobalTLSAddress) |
136 | return false; // direct calls. |
137 | |
138 | if (Addr.getOpcode() == ISD::ADD) { |
139 | if (ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Val: Addr.getOperand(i: 1))) |
140 | if (isInt<13>(x: CN->getSExtValue())) |
141 | return false; // Let the reg+imm pattern catch this! |
142 | if (Addr.getOperand(i: 0).getOpcode() == SPISD::Lo || |
143 | Addr.getOperand(i: 1).getOpcode() == SPISD::Lo) |
144 | return false; // Let the reg+imm pattern catch this! |
145 | R1 = Addr.getOperand(i: 0); |
146 | R2 = Addr.getOperand(i: 1); |
147 | return true; |
148 | } |
149 | |
150 | R1 = Addr; |
151 | R2 = CurDAG->getRegister(Reg: SP::G0, VT: TLI->getPointerTy(DL: CurDAG->getDataLayout())); |
152 | return true; |
153 | } |
154 | |
155 | |
156 | // Re-assemble i64 arguments split up in SelectionDAGBuilder's |
157 | // visitInlineAsm / GetRegistersForValue functions. |
158 | // |
159 | // Note: This function was copied from, and is essentially identical |
160 | // to ARMISelDAGToDAG::SelectInlineAsm. It is very unfortunate that |
161 | // such hacking-up is necessary; a rethink of how inline asm operands |
162 | // are handled may be in order to make doing this more sane. |
163 | // |
164 | // TODO: fix inline asm support so I can simply tell it that 'i64' |
165 | // inputs to asm need to be allocated to the IntPair register type, |
166 | // and have that work. Then, delete this function. |
167 | bool SparcDAGToDAGISel::tryInlineAsm(SDNode *N){ |
168 | std::vector<SDValue> AsmNodeOperands; |
169 | InlineAsm::Flag Flag; |
170 | bool Changed = false; |
171 | unsigned NumOps = N->getNumOperands(); |
172 | |
173 | // Normally, i64 data is bounded to two arbitrary GPRs for "%r" |
174 | // constraint. However, some instructions (e.g. ldd/std) require |
175 | // (even/even+1) GPRs. |
176 | |
177 | // So, here, we check for this case, and mutate the inlineasm to use |
178 | // a single IntPair register instead, which guarantees such even/odd |
179 | // placement. |
180 | |
181 | SDLoc dl(N); |
182 | SDValue Glue = N->getGluedNode() ? N->getOperand(Num: NumOps - 1) : SDValue(); |
183 | |
184 | SmallVector<bool, 8> OpChanged; |
185 | // Glue node will be appended late. |
186 | for(unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e; ++i) { |
187 | SDValue op = N->getOperand(Num: i); |
188 | AsmNodeOperands.push_back(x: op); |
189 | |
190 | if (i < InlineAsm::Op_FirstOperand) |
191 | continue; |
192 | |
193 | if (const auto *C = dyn_cast<ConstantSDNode>(Val: N->getOperand(Num: i))) |
194 | Flag = InlineAsm::Flag(C->getZExtValue()); |
195 | else |
196 | continue; |
197 | |
198 | // Immediate operands to inline asm in the SelectionDAG are modeled with |
199 | // two operands. The first is a constant of value InlineAsm::Kind::Imm, and |
200 | // the second is a constant with the value of the immediate. If we get here |
201 | // and we have a Kind::Imm, skip the next operand, and continue. |
202 | if (Flag.isImmKind()) { |
203 | SDValue op = N->getOperand(Num: ++i); |
204 | AsmNodeOperands.push_back(x: op); |
205 | continue; |
206 | } |
207 | |
208 | const unsigned NumRegs = Flag.getNumOperandRegisters(); |
209 | if (NumRegs) |
210 | OpChanged.push_back(Elt: false); |
211 | |
212 | unsigned DefIdx = 0; |
213 | bool IsTiedToChangedOp = false; |
214 | // If it's a use that is tied with a previous def, it has no |
215 | // reg class constraint. |
216 | if (Changed && Flag.isUseOperandTiedToDef(Idx&: DefIdx)) |
217 | IsTiedToChangedOp = OpChanged[DefIdx]; |
218 | |
219 | if (!Flag.isRegUseKind() && !Flag.isRegDefKind() && |
220 | !Flag.isRegDefEarlyClobberKind()) |
221 | continue; |
222 | |
223 | unsigned RC; |
224 | const bool HasRC = Flag.hasRegClassConstraint(RC); |
225 | if ((!IsTiedToChangedOp && (!HasRC || RC != SP::IntRegsRegClassID)) |
226 | || NumRegs != 2) |
227 | continue; |
228 | |
229 | assert((i+2 < NumOps) && "Invalid number of operands in inline asm" ); |
230 | SDValue V0 = N->getOperand(Num: i+1); |
231 | SDValue V1 = N->getOperand(Num: i+2); |
232 | Register Reg0 = cast<RegisterSDNode>(Val&: V0)->getReg(); |
233 | Register Reg1 = cast<RegisterSDNode>(Val&: V1)->getReg(); |
234 | SDValue PairedReg; |
235 | MachineRegisterInfo &MRI = MF->getRegInfo(); |
236 | |
237 | if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) { |
238 | // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to |
239 | // the original GPRs. |
240 | |
241 | Register GPVR = MRI.createVirtualRegister(RegClass: &SP::IntPairRegClass); |
242 | PairedReg = CurDAG->getRegister(Reg: GPVR, VT: MVT::v2i32); |
243 | SDValue Chain = SDValue(N,0); |
244 | |
245 | SDNode *GU = N->getGluedUser(); |
246 | SDValue RegCopy = CurDAG->getCopyFromReg(Chain, dl, Reg: GPVR, VT: MVT::v2i32, |
247 | Glue: Chain.getValue(R: 1)); |
248 | |
249 | // Extract values from a GPRPair reg and copy to the original GPR reg. |
250 | SDValue Sub0 = CurDAG->getTargetExtractSubreg(SRIdx: SP::sub_even, DL: dl, VT: MVT::i32, |
251 | Operand: RegCopy); |
252 | SDValue Sub1 = CurDAG->getTargetExtractSubreg(SRIdx: SP::sub_odd, DL: dl, VT: MVT::i32, |
253 | Operand: RegCopy); |
254 | SDValue T0 = CurDAG->getCopyToReg(Chain: Sub0, dl, Reg: Reg0, N: Sub0, |
255 | Glue: RegCopy.getValue(R: 1)); |
256 | SDValue T1 = CurDAG->getCopyToReg(Chain: Sub1, dl, Reg: Reg1, N: Sub1, Glue: T0.getValue(R: 1)); |
257 | |
258 | // Update the original glue user. |
259 | std::vector<SDValue> Ops(GU->op_begin(), GU->op_end()-1); |
260 | Ops.push_back(x: T1.getValue(R: 1)); |
261 | CurDAG->UpdateNodeOperands(N: GU, Ops); |
262 | } else { |
263 | // For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a |
264 | // GPRPair and then pass the GPRPair to the inline asm. |
265 | SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain]; |
266 | |
267 | // As REG_SEQ doesn't take RegisterSDNode, we copy them first. |
268 | SDValue T0 = CurDAG->getCopyFromReg(Chain, dl, Reg: Reg0, VT: MVT::i32, |
269 | Glue: Chain.getValue(R: 1)); |
270 | SDValue T1 = CurDAG->getCopyFromReg(Chain, dl, Reg: Reg1, VT: MVT::i32, |
271 | Glue: T0.getValue(R: 1)); |
272 | SDValue Pair = SDValue( |
273 | CurDAG->getMachineNode( |
274 | Opcode: TargetOpcode::REG_SEQUENCE, dl, VT: MVT::v2i32, |
275 | Ops: { |
276 | CurDAG->getTargetConstant(Val: SP::IntPairRegClassID, DL: dl, |
277 | VT: MVT::i32), |
278 | T0, |
279 | CurDAG->getTargetConstant(Val: SP::sub_even, DL: dl, VT: MVT::i32), |
280 | T1, |
281 | CurDAG->getTargetConstant(Val: SP::sub_odd, DL: dl, VT: MVT::i32), |
282 | }), |
283 | 0); |
284 | |
285 | // Copy REG_SEQ into a GPRPair-typed VR and replace the original two |
286 | // i32 VRs of inline asm with it. |
287 | Register GPVR = MRI.createVirtualRegister(RegClass: &SP::IntPairRegClass); |
288 | PairedReg = CurDAG->getRegister(Reg: GPVR, VT: MVT::v2i32); |
289 | Chain = CurDAG->getCopyToReg(Chain: T1, dl, Reg: GPVR, N: Pair, Glue: T1.getValue(R: 1)); |
290 | |
291 | AsmNodeOperands[InlineAsm::Op_InputChain] = Chain; |
292 | Glue = Chain.getValue(R: 1); |
293 | } |
294 | |
295 | Changed = true; |
296 | |
297 | if(PairedReg.getNode()) { |
298 | OpChanged[OpChanged.size() -1 ] = true; |
299 | Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/); |
300 | if (IsTiedToChangedOp) |
301 | Flag.setMatchingOp(DefIdx); |
302 | else |
303 | Flag.setRegClass(SP::IntPairRegClassID); |
304 | // Replace the current flag. |
305 | AsmNodeOperands[AsmNodeOperands.size() -1] = CurDAG->getTargetConstant( |
306 | Val: Flag, DL: dl, VT: MVT::i32); |
307 | // Add the new register node and skip the original two GPRs. |
308 | AsmNodeOperands.push_back(x: PairedReg); |
309 | // Skip the next two GPRs. |
310 | i += 2; |
311 | } |
312 | } |
313 | |
314 | if (Glue.getNode()) |
315 | AsmNodeOperands.push_back(x: Glue); |
316 | if (!Changed) |
317 | return false; |
318 | |
319 | SelectInlineAsmMemoryOperands(Ops&: AsmNodeOperands, DL: SDLoc(N)); |
320 | |
321 | SDValue New = CurDAG->getNode(Opcode: N->getOpcode(), DL: SDLoc(N), |
322 | VTList: CurDAG->getVTList(VT1: MVT::Other, VT2: MVT::Glue), Ops: AsmNodeOperands); |
323 | New->setNodeId(-1); |
324 | ReplaceNode(F: N, T: New.getNode()); |
325 | return true; |
326 | } |
327 | |
328 | void SparcDAGToDAGISel::Select(SDNode *N) { |
329 | SDLoc dl(N); |
330 | if (N->isMachineOpcode()) { |
331 | N->setNodeId(-1); |
332 | return; // Already selected. |
333 | } |
334 | |
335 | switch (N->getOpcode()) { |
336 | default: break; |
337 | case ISD::INLINEASM: |
338 | case ISD::INLINEASM_BR: { |
339 | if (tryInlineAsm(N)) |
340 | return; |
341 | break; |
342 | } |
343 | case SPISD::GLOBAL_BASE_REG: |
344 | ReplaceNode(F: N, T: getGlobalBaseReg()); |
345 | return; |
346 | |
347 | case ISD::SDIV: |
348 | case ISD::UDIV: { |
349 | // sdivx / udivx handle 64-bit divides. |
350 | if (N->getValueType(ResNo: 0) == MVT::i64) |
351 | break; |
352 | // FIXME: should use a custom expander to expose the SRA to the dag. |
353 | SDValue DivLHS = N->getOperand(Num: 0); |
354 | SDValue DivRHS = N->getOperand(Num: 1); |
355 | |
356 | // Set the Y register to the high-part. |
357 | SDValue TopPart; |
358 | if (N->getOpcode() == ISD::SDIV) { |
359 | TopPart = SDValue(CurDAG->getMachineNode(Opcode: SP::SRAri, dl, VT: MVT::i32, Op1: DivLHS, |
360 | Op2: CurDAG->getTargetConstant(Val: 31, DL: dl, VT: MVT::i32)), |
361 | 0); |
362 | } else { |
363 | TopPart = CurDAG->getRegister(Reg: SP::G0, VT: MVT::i32); |
364 | } |
365 | TopPart = CurDAG->getCopyToReg(Chain: CurDAG->getEntryNode(), dl, Reg: SP::Y, N: TopPart, |
366 | Glue: SDValue()) |
367 | .getValue(R: 1); |
368 | |
369 | // FIXME: Handle div by immediate. |
370 | unsigned Opcode = N->getOpcode() == ISD::SDIV ? SP::SDIVrr : SP::UDIVrr; |
371 | CurDAG->SelectNodeTo(N, MachineOpc: Opcode, VT: MVT::i32, Op1: DivLHS, Op2: DivRHS, Op3: TopPart); |
372 | return; |
373 | } |
374 | } |
375 | |
376 | SelectCode(N); |
377 | } |
378 | |
379 | |
380 | /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for |
381 | /// inline asm expressions. |
382 | bool SparcDAGToDAGISel::SelectInlineAsmMemoryOperand( |
383 | const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, |
384 | std::vector<SDValue> &OutOps) { |
385 | SDValue Op0, Op1; |
386 | switch (ConstraintID) { |
387 | default: return true; |
388 | case InlineAsm::ConstraintCode::o: |
389 | case InlineAsm::ConstraintCode::m: // memory |
390 | if (!SelectADDRrr(Addr: Op, R1&: Op0, R2&: Op1)) |
391 | SelectADDRri(Addr: Op, Base&: Op0, Offset&: Op1); |
392 | break; |
393 | } |
394 | |
395 | OutOps.push_back(x: Op0); |
396 | OutOps.push_back(x: Op1); |
397 | return false; |
398 | } |
399 | |
400 | /// createSparcISelDag - This pass converts a legalized DAG into a |
401 | /// SPARC-specific DAG, ready for instruction scheduling. |
402 | /// |
403 | FunctionPass *llvm::createSparcISelDag(SparcTargetMachine &TM) { |
404 | return new SparcDAGToDAGISelLegacy(TM); |
405 | } |
406 | |