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