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