1 | //===-- AVRISelDAGToDAG.cpp - A dag to dag inst selector for AVR ----------===// |
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 AVR target. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "AVR.h" |
14 | #include "AVRTargetMachine.h" |
15 | #include "MCTargetDesc/AVRMCTargetDesc.h" |
16 | |
17 | #include "llvm/CodeGen/MachineRegisterInfo.h" |
18 | #include "llvm/CodeGen/SelectionDAGISel.h" |
19 | #include "llvm/Support/Debug.h" |
20 | #include "llvm/Support/raw_ostream.h" |
21 | |
22 | #define DEBUG_TYPE "avr-isel" |
23 | #define PASS_NAME "AVR DAG->DAG Instruction Selection" |
24 | |
25 | using namespace llvm; |
26 | |
27 | namespace { |
28 | |
29 | /// Lowers LLVM IR (in DAG form) to AVR MC instructions (in DAG form). |
30 | class AVRDAGToDAGISel : public SelectionDAGISel { |
31 | public: |
32 | AVRDAGToDAGISel() = delete; |
33 | |
34 | AVRDAGToDAGISel(AVRTargetMachine &TM, CodeGenOptLevel OptLevel) |
35 | : SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {} |
36 | |
37 | bool runOnMachineFunction(MachineFunction &MF) override; |
38 | |
39 | bool SelectAddr(SDNode *Op, SDValue N, SDValue &Base, SDValue &Disp); |
40 | |
41 | bool selectIndexedLoad(SDNode *N); |
42 | unsigned selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT, int Bank); |
43 | |
44 | bool SelectInlineAsmMemoryOperand(const SDValue &Op, |
45 | InlineAsm::ConstraintCode ConstraintCode, |
46 | std::vector<SDValue> &OutOps) override; |
47 | |
48 | // Include the pieces autogenerated from the target description. |
49 | #include "AVRGenDAGISel.inc" |
50 | |
51 | private: |
52 | void Select(SDNode *N) override; |
53 | bool trySelect(SDNode *N); |
54 | |
55 | template <unsigned NodeType> bool select(SDNode *N); |
56 | bool selectMultiplication(SDNode *N); |
57 | |
58 | const AVRSubtarget *Subtarget; |
59 | }; |
60 | |
61 | class AVRDAGToDAGISelLegacy : public SelectionDAGISelLegacy { |
62 | public: |
63 | static char ID; |
64 | AVRDAGToDAGISelLegacy(AVRTargetMachine &TM, CodeGenOptLevel OptLevel) |
65 | : SelectionDAGISelLegacy( |
66 | ID, std::make_unique<AVRDAGToDAGISel>(args&: TM, args&: OptLevel)) {} |
67 | }; |
68 | |
69 | } // namespace |
70 | |
71 | char AVRDAGToDAGISelLegacy::ID = 0; |
72 | |
73 | INITIALIZE_PASS(AVRDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false) |
74 | |
75 | bool AVRDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { |
76 | Subtarget = &MF.getSubtarget<AVRSubtarget>(); |
77 | return SelectionDAGISel::runOnMachineFunction(mf&: MF); |
78 | } |
79 | |
80 | bool AVRDAGToDAGISel::SelectAddr(SDNode *Op, SDValue N, SDValue &Base, |
81 | SDValue &Disp) { |
82 | SDLoc dl(Op); |
83 | auto DL = CurDAG->getDataLayout(); |
84 | MVT PtrVT = getTargetLowering()->getPointerTy(DL); |
85 | |
86 | // if the address is a frame index get the TargetFrameIndex. |
87 | if (const FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Val&: N)) { |
88 | Base = CurDAG->getTargetFrameIndex(FI: FIN->getIndex(), VT: PtrVT); |
89 | Disp = CurDAG->getTargetConstant(Val: 0, DL: dl, VT: MVT::i8); |
90 | |
91 | return true; |
92 | } |
93 | |
94 | // Match simple Reg + uimm6 operands. |
95 | if (N.getOpcode() != ISD::ADD && N.getOpcode() != ISD::SUB && |
96 | !CurDAG->isBaseWithConstantOffset(Op: N)) { |
97 | return false; |
98 | } |
99 | |
100 | if (const ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(Val: N.getOperand(i: 1))) { |
101 | int RHSC = (int)RHS->getZExtValue(); |
102 | |
103 | // Convert negative offsets into positives ones. |
104 | if (N.getOpcode() == ISD::SUB) { |
105 | RHSC = -RHSC; |
106 | } |
107 | |
108 | // <#Frame index + const> |
109 | // Allow folding offsets bigger than 63 so the frame pointer can be used |
110 | // directly instead of copying it around by adjusting and restoring it for |
111 | // each access. |
112 | if (N.getOperand(i: 0).getOpcode() == ISD::FrameIndex) { |
113 | int FI = cast<FrameIndexSDNode>(Val: N.getOperand(i: 0))->getIndex(); |
114 | |
115 | Base = CurDAG->getTargetFrameIndex(FI, VT: PtrVT); |
116 | Disp = CurDAG->getTargetConstant(Val: RHSC, DL: dl, VT: MVT::i16); |
117 | |
118 | return true; |
119 | } |
120 | |
121 | // The value type of the memory instruction determines what is the maximum |
122 | // offset allowed. |
123 | MVT VT = cast<MemSDNode>(Val: Op)->getMemoryVT().getSimpleVT(); |
124 | |
125 | // We only accept offsets that fit in 6 bits (unsigned), with the exception |
126 | // of 16-bit loads - those can only go up to 62, because we desugar them |
127 | // into a pair of 8-bit loads like `ldd rx, RHSC` + `ldd ry, RHSC + 1`. |
128 | bool OkI8 = VT == MVT::i8 && RHSC <= 63; |
129 | bool OkI16 = VT == MVT::i16 && RHSC <= 62; |
130 | |
131 | if (OkI8 || OkI16) { |
132 | Base = N.getOperand(i: 0); |
133 | Disp = CurDAG->getTargetConstant(Val: RHSC, DL: dl, VT: MVT::i8); |
134 | |
135 | return true; |
136 | } |
137 | } |
138 | |
139 | return false; |
140 | } |
141 | |
142 | bool AVRDAGToDAGISel::selectIndexedLoad(SDNode *N) { |
143 | const LoadSDNode *LD = cast<LoadSDNode>(Val: N); |
144 | ISD::MemIndexedMode AM = LD->getAddressingMode(); |
145 | MVT VT = LD->getMemoryVT().getSimpleVT(); |
146 | auto PtrVT = getTargetLowering()->getPointerTy(DL: CurDAG->getDataLayout()); |
147 | |
148 | // We only care if this load uses a POSTINC or PREDEC mode. |
149 | if ((LD->getExtensionType() != ISD::NON_EXTLOAD) || |
150 | (AM != ISD::POST_INC && AM != ISD::PRE_DEC)) { |
151 | |
152 | return false; |
153 | } |
154 | |
155 | unsigned Opcode = 0; |
156 | bool isPre = (AM == ISD::PRE_DEC); |
157 | int Offs = cast<ConstantSDNode>(Val: LD->getOffset())->getSExtValue(); |
158 | |
159 | switch (VT.SimpleTy) { |
160 | case MVT::i8: { |
161 | if ((!isPre && Offs != 1) || (isPre && Offs != -1)) { |
162 | return false; |
163 | } |
164 | |
165 | Opcode = (isPre) ? AVR::LDRdPtrPd : AVR::LDRdPtrPi; |
166 | break; |
167 | } |
168 | case MVT::i16: { |
169 | if ((!isPre && Offs != 2) || (isPre && Offs != -2)) { |
170 | return false; |
171 | } |
172 | |
173 | Opcode = (isPre) ? AVR::LDWRdPtrPd : AVR::LDWRdPtrPi; |
174 | break; |
175 | } |
176 | default: |
177 | return false; |
178 | } |
179 | |
180 | SDNode *ResNode = |
181 | CurDAG->getMachineNode(Opcode, dl: SDLoc(N), VT1: VT, VT2: PtrVT, VT3: MVT::Other, |
182 | Op1: LD->getBasePtr(), Op2: LD->getChain()); |
183 | ReplaceUses(F: N, T: ResNode); |
184 | CurDAG->RemoveDeadNode(N); |
185 | |
186 | return true; |
187 | } |
188 | |
189 | unsigned AVRDAGToDAGISel::selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT, |
190 | int Bank) { |
191 | // Progmem indexed loads only work in POSTINC mode. |
192 | if (LD->getExtensionType() != ISD::NON_EXTLOAD || |
193 | LD->getAddressingMode() != ISD::POST_INC) |
194 | return 0; |
195 | |
196 | // Feature ELPM is needed for loading from extended program memory. |
197 | assert((Bank == 0 || Subtarget->hasELPM()) && |
198 | "cannot load from extended program memory on this mcu" ); |
199 | |
200 | unsigned Opcode = 0; |
201 | int Offs = cast<ConstantSDNode>(Val: LD->getOffset())->getSExtValue(); |
202 | |
203 | if (VT.SimpleTy == MVT::i8 && Offs == 1 && Bank == 0) |
204 | Opcode = AVR::LPMRdZPi; |
205 | |
206 | // TODO: Implements the expansion of the following pseudo instructions. |
207 | // LPMWRdZPi: type == MVT::i16, offset == 2, Bank == 0. |
208 | // ELPMBRdZPi: type == MVT::i8, offset == 1, Bank > 0. |
209 | // ELPMWRdZPi: type == MVT::i16, offset == 2, Bank > 0. |
210 | |
211 | return Opcode; |
212 | } |
213 | |
214 | bool AVRDAGToDAGISel::SelectInlineAsmMemoryOperand( |
215 | const SDValue &Op, InlineAsm::ConstraintCode ConstraintCode, |
216 | std::vector<SDValue> &OutOps) { |
217 | assert((ConstraintCode == InlineAsm::ConstraintCode::m || |
218 | ConstraintCode == InlineAsm::ConstraintCode::Q) && |
219 | "Unexpected asm memory constraint" ); |
220 | |
221 | MachineRegisterInfo &RI = MF->getRegInfo(); |
222 | const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>(); |
223 | const TargetLowering &TL = *STI.getTargetLowering(); |
224 | SDLoc dl(Op); |
225 | auto DL = CurDAG->getDataLayout(); |
226 | |
227 | const RegisterSDNode *RegNode = dyn_cast<RegisterSDNode>(Val: Op); |
228 | |
229 | // If address operand is of PTRDISPREGS class, all is OK, then. |
230 | if (RegNode && |
231 | RI.getRegClass(Reg: RegNode->getReg()) == &AVR::PTRDISPREGSRegClass) { |
232 | OutOps.push_back(x: Op); |
233 | return false; |
234 | } |
235 | |
236 | if (Op->getOpcode() == ISD::FrameIndex) { |
237 | SDValue Base, Disp; |
238 | |
239 | if (SelectAddr(Op: Op.getNode(), N: Op, Base, Disp)) { |
240 | OutOps.push_back(x: Base); |
241 | OutOps.push_back(x: Disp); |
242 | |
243 | return false; |
244 | } |
245 | |
246 | return true; |
247 | } |
248 | |
249 | // If Op is add 'register, immediate' and |
250 | // register is either virtual register or register of PTRDISPREGSRegClass |
251 | if (Op->getOpcode() == ISD::ADD || Op->getOpcode() == ISD::SUB) { |
252 | SDValue CopyFromRegOp = Op->getOperand(Num: 0); |
253 | SDValue ImmOp = Op->getOperand(Num: 1); |
254 | ConstantSDNode *ImmNode = dyn_cast<ConstantSDNode>(Val&: ImmOp); |
255 | |
256 | unsigned Reg; |
257 | bool CanHandleRegImmOpt = ImmNode && ImmNode->getAPIntValue().ult(RHS: 64); |
258 | |
259 | if (CopyFromRegOp->getOpcode() == ISD::CopyFromReg) { |
260 | RegisterSDNode *RegNode = |
261 | cast<RegisterSDNode>(Val: CopyFromRegOp->getOperand(Num: 1)); |
262 | Reg = RegNode->getReg(); |
263 | CanHandleRegImmOpt &= (Register::isVirtualRegister(Reg) || |
264 | AVR::PTRDISPREGSRegClass.contains(Reg)); |
265 | } else { |
266 | CanHandleRegImmOpt = false; |
267 | } |
268 | |
269 | // If we detect proper case - correct virtual register class |
270 | // if needed and go to another inlineasm operand. |
271 | if (CanHandleRegImmOpt) { |
272 | SDValue Base, Disp; |
273 | |
274 | if (RI.getRegClass(Reg) != &AVR::PTRDISPREGSRegClass) { |
275 | SDLoc dl(CopyFromRegOp); |
276 | |
277 | Register VReg = RI.createVirtualRegister(RegClass: &AVR::PTRDISPREGSRegClass); |
278 | |
279 | SDValue CopyToReg = |
280 | CurDAG->getCopyToReg(Chain: CopyFromRegOp, dl, Reg: VReg, N: CopyFromRegOp); |
281 | |
282 | SDValue NewCopyFromRegOp = |
283 | CurDAG->getCopyFromReg(Chain: CopyToReg, dl, Reg: VReg, VT: TL.getPointerTy(DL)); |
284 | |
285 | Base = NewCopyFromRegOp; |
286 | } else { |
287 | Base = CopyFromRegOp; |
288 | } |
289 | |
290 | if (ImmNode->getValueType(ResNo: 0) != MVT::i8) { |
291 | Disp = CurDAG->getTargetConstant(Val: ImmNode->getZExtValue(), DL: dl, VT: MVT::i8); |
292 | } else { |
293 | Disp = ImmOp; |
294 | } |
295 | |
296 | OutOps.push_back(x: Base); |
297 | OutOps.push_back(x: Disp); |
298 | |
299 | return false; |
300 | } |
301 | } |
302 | |
303 | // More generic case. |
304 | // Create chain that puts Op into pointer register |
305 | // and return that register. |
306 | Register VReg = RI.createVirtualRegister(RegClass: &AVR::PTRDISPREGSRegClass); |
307 | |
308 | SDValue CopyToReg = CurDAG->getCopyToReg(Chain: Op, dl, Reg: VReg, N: Op); |
309 | SDValue CopyFromReg = |
310 | CurDAG->getCopyFromReg(Chain: CopyToReg, dl, Reg: VReg, VT: TL.getPointerTy(DL)); |
311 | |
312 | OutOps.push_back(x: CopyFromReg); |
313 | |
314 | return false; |
315 | } |
316 | |
317 | template <> bool AVRDAGToDAGISel::select<ISD::FrameIndex>(SDNode *N) { |
318 | auto DL = CurDAG->getDataLayout(); |
319 | |
320 | // Convert the frameindex into a temp instruction that will hold the |
321 | // effective address of the final stack slot. |
322 | int FI = cast<FrameIndexSDNode>(Val: N)->getIndex(); |
323 | SDValue TFI = |
324 | CurDAG->getTargetFrameIndex(FI, VT: getTargetLowering()->getPointerTy(DL)); |
325 | |
326 | CurDAG->SelectNodeTo(N, MachineOpc: AVR::FRMIDX, VT: getTargetLowering()->getPointerTy(DL), |
327 | Op1: TFI, Op2: CurDAG->getTargetConstant(Val: 0, DL: SDLoc(N), VT: MVT::i16)); |
328 | return true; |
329 | } |
330 | |
331 | template <> bool AVRDAGToDAGISel::select<ISD::STORE>(SDNode *N) { |
332 | // Use the STD{W}SPQRr pseudo instruction when passing arguments through |
333 | // the stack on function calls for further expansion during the PEI phase. |
334 | const StoreSDNode *ST = cast<StoreSDNode>(Val: N); |
335 | SDValue BasePtr = ST->getBasePtr(); |
336 | |
337 | // Early exit when the base pointer is a frame index node or a constant. |
338 | if (isa<FrameIndexSDNode>(Val: BasePtr) || isa<ConstantSDNode>(Val: BasePtr) || |
339 | BasePtr.isUndef()) { |
340 | return false; |
341 | } |
342 | |
343 | const RegisterSDNode *RN = dyn_cast<RegisterSDNode>(Val: BasePtr.getOperand(i: 0)); |
344 | // Only stores where SP is the base pointer are valid. |
345 | if (!RN || (RN->getReg() != AVR::SP)) { |
346 | return false; |
347 | } |
348 | |
349 | int CST = (int)BasePtr.getConstantOperandVal(i: 1); |
350 | SDValue Chain = ST->getChain(); |
351 | EVT VT = ST->getValue().getValueType(); |
352 | SDLoc DL(N); |
353 | SDValue Offset = CurDAG->getTargetConstant(Val: CST, DL, VT: MVT::i16); |
354 | SDValue Ops[] = {BasePtr.getOperand(i: 0), Offset, ST->getValue(), Chain}; |
355 | unsigned Opc = (VT == MVT::i16) ? AVR::STDWSPQRr : AVR::STDSPQRr; |
356 | |
357 | SDNode *ResNode = CurDAG->getMachineNode(Opcode: Opc, dl: DL, VT: MVT::Other, Ops); |
358 | |
359 | // Transfer memory operands. |
360 | CurDAG->setNodeMemRefs(N: cast<MachineSDNode>(Val: ResNode), NewMemRefs: {ST->getMemOperand()}); |
361 | |
362 | ReplaceUses(F: SDValue(N, 0), T: SDValue(ResNode, 0)); |
363 | CurDAG->RemoveDeadNode(N); |
364 | |
365 | return true; |
366 | } |
367 | |
368 | template <> bool AVRDAGToDAGISel::select<ISD::LOAD>(SDNode *N) { |
369 | const LoadSDNode *LD = cast<LoadSDNode>(Val: N); |
370 | if (!AVR::isProgramMemoryAccess(N: LD)) { |
371 | // Check if the opcode can be converted into an indexed load. |
372 | return selectIndexedLoad(N); |
373 | } |
374 | |
375 | if (!Subtarget->hasLPM()) |
376 | report_fatal_error(reason: "cannot load from program memory on this mcu" ); |
377 | |
378 | int ProgMemBank = AVR::getProgramMemoryBank(N: LD); |
379 | if (ProgMemBank < 0 || ProgMemBank > 5) |
380 | report_fatal_error(reason: "unexpected program memory bank" ); |
381 | if (ProgMemBank > 0 && !Subtarget->hasELPM()) |
382 | report_fatal_error(reason: "unexpected program memory bank" ); |
383 | |
384 | // This is a flash memory load, move the pointer into R31R30 and emit |
385 | // the lpm instruction. |
386 | MVT VT = LD->getMemoryVT().getSimpleVT(); |
387 | SDValue Chain = LD->getChain(); |
388 | SDValue Ptr = LD->getBasePtr(); |
389 | SDNode *ResNode; |
390 | SDLoc DL(N); |
391 | |
392 | Chain = CurDAG->getCopyToReg(Chain, dl: DL, Reg: AVR::R31R30, N: Ptr, Glue: SDValue()); |
393 | Ptr = CurDAG->getCopyFromReg(Chain, dl: DL, Reg: AVR::R31R30, VT: MVT::i16, |
394 | Glue: Chain.getValue(R: 1)); |
395 | |
396 | // Check if the opcode can be converted into an indexed load. |
397 | if (unsigned LPMOpc = selectIndexedProgMemLoad(LD, VT, Bank: ProgMemBank)) { |
398 | // It is legal to fold the load into an indexed load. |
399 | if (ProgMemBank == 0) { |
400 | ResNode = |
401 | CurDAG->getMachineNode(Opcode: LPMOpc, dl: DL, VT1: VT, VT2: MVT::i16, VT3: MVT::Other, Ops: Ptr); |
402 | } else { |
403 | // Do not combine the LDI instruction into the ELPM pseudo instruction, |
404 | // since it may be reused by other ELPM pseudo instructions. |
405 | SDValue NC = CurDAG->getTargetConstant(Val: ProgMemBank, DL, VT: MVT::i8); |
406 | auto *NP = CurDAG->getMachineNode(Opcode: AVR::LDIRdK, dl: DL, VT: MVT::i8, Op1: NC); |
407 | ResNode = CurDAG->getMachineNode(Opcode: LPMOpc, dl: DL, VT1: VT, VT2: MVT::i16, VT3: MVT::Other, |
408 | Op1: Ptr, Op2: SDValue(NP, 0)); |
409 | } |
410 | } else { |
411 | // Selecting an indexed load is not legal, fallback to a normal load. |
412 | switch (VT.SimpleTy) { |
413 | case MVT::i8: |
414 | if (ProgMemBank == 0) { |
415 | unsigned Opc = Subtarget->hasLPMX() ? AVR::LPMRdZ : AVR::LPMBRdZ; |
416 | ResNode = |
417 | CurDAG->getMachineNode(Opcode: Opc, dl: DL, VT1: MVT::i8, VT2: MVT::Other, Ops: Ptr); |
418 | } else { |
419 | // Do not combine the LDI instruction into the ELPM pseudo instruction, |
420 | // since it may be reused by other ELPM pseudo instructions. |
421 | SDValue NC = CurDAG->getTargetConstant(Val: ProgMemBank, DL, VT: MVT::i8); |
422 | auto *NP = CurDAG->getMachineNode(Opcode: AVR::LDIRdK, dl: DL, VT: MVT::i8, Op1: NC); |
423 | ResNode = CurDAG->getMachineNode(Opcode: AVR::ELPMBRdZ, dl: DL, VT1: MVT::i8, VT2: MVT::Other, |
424 | Op1: Ptr, Op2: SDValue(NP, 0)); |
425 | } |
426 | break; |
427 | case MVT::i16: |
428 | if (ProgMemBank == 0) { |
429 | ResNode = |
430 | CurDAG->getMachineNode(Opcode: AVR::LPMWRdZ, dl: DL, VT1: MVT::i16, VT2: MVT::Other, Ops: Ptr); |
431 | } else { |
432 | // Do not combine the LDI instruction into the ELPM pseudo instruction, |
433 | // since LDI requires the destination register in range R16~R31. |
434 | SDValue NC = CurDAG->getTargetConstant(Val: ProgMemBank, DL, VT: MVT::i8); |
435 | auto *NP = CurDAG->getMachineNode(Opcode: AVR::LDIRdK, dl: DL, VT: MVT::i8, Op1: NC); |
436 | ResNode = CurDAG->getMachineNode(Opcode: AVR::ELPMWRdZ, dl: DL, VT1: MVT::i16, |
437 | VT2: MVT::Other, Op1: Ptr, Op2: SDValue(NP, 0)); |
438 | } |
439 | break; |
440 | default: |
441 | llvm_unreachable("Unsupported VT!" ); |
442 | } |
443 | } |
444 | |
445 | // Transfer memory operands. |
446 | CurDAG->setNodeMemRefs(N: cast<MachineSDNode>(Val: ResNode), NewMemRefs: {LD->getMemOperand()}); |
447 | |
448 | ReplaceUses(F: SDValue(N, 0), T: SDValue(ResNode, 0)); |
449 | ReplaceUses(F: SDValue(N, 1), T: SDValue(ResNode, 1)); |
450 | CurDAG->RemoveDeadNode(N); |
451 | |
452 | return true; |
453 | } |
454 | |
455 | template <> bool AVRDAGToDAGISel::select<AVRISD::CALL>(SDNode *N) { |
456 | SDValue InGlue; |
457 | SDValue Chain = N->getOperand(Num: 0); |
458 | SDValue Callee = N->getOperand(Num: 1); |
459 | unsigned LastOpNum = N->getNumOperands() - 1; |
460 | |
461 | // Direct calls are autogenerated. |
462 | unsigned Op = Callee.getOpcode(); |
463 | if (Op == ISD::TargetGlobalAddress || Op == ISD::TargetExternalSymbol) { |
464 | return false; |
465 | } |
466 | |
467 | // Skip the incoming flag if present |
468 | if (N->getOperand(Num: LastOpNum).getValueType() == MVT::Glue) { |
469 | --LastOpNum; |
470 | } |
471 | |
472 | SDLoc DL(N); |
473 | Chain = CurDAG->getCopyToReg(Chain, dl: DL, Reg: AVR::R31R30, N: Callee, Glue: InGlue); |
474 | SmallVector<SDValue, 8> Ops; |
475 | Ops.push_back(Elt: CurDAG->getRegister(Reg: AVR::R31R30, VT: MVT::i16)); |
476 | |
477 | // Map all operands into the new node. |
478 | for (unsigned i = 2, e = LastOpNum + 1; i != e; ++i) { |
479 | Ops.push_back(Elt: N->getOperand(Num: i)); |
480 | } |
481 | |
482 | Ops.push_back(Elt: Chain); |
483 | Ops.push_back(Elt: Chain.getValue(R: 1)); |
484 | |
485 | SDNode *ResNode = CurDAG->getMachineNode( |
486 | Opcode: Subtarget->hasEIJMPCALL() ? AVR::EICALL : AVR::ICALL, dl: DL, VT1: MVT::Other, |
487 | VT2: MVT::Glue, Ops); |
488 | |
489 | ReplaceUses(F: SDValue(N, 0), T: SDValue(ResNode, 0)); |
490 | ReplaceUses(F: SDValue(N, 1), T: SDValue(ResNode, 1)); |
491 | CurDAG->RemoveDeadNode(N); |
492 | |
493 | return true; |
494 | } |
495 | |
496 | template <> bool AVRDAGToDAGISel::select<ISD::BRIND>(SDNode *N) { |
497 | SDValue Chain = N->getOperand(Num: 0); |
498 | SDValue JmpAddr = N->getOperand(Num: 1); |
499 | |
500 | SDLoc DL(N); |
501 | // Move the destination address of the indirect branch into R31R30. |
502 | Chain = CurDAG->getCopyToReg(Chain, dl: DL, Reg: AVR::R31R30, N: JmpAddr); |
503 | SDNode *ResNode = CurDAG->getMachineNode(Opcode: AVR::IJMP, dl: DL, VT: MVT::Other, Op1: Chain); |
504 | |
505 | ReplaceUses(F: SDValue(N, 0), T: SDValue(ResNode, 0)); |
506 | CurDAG->RemoveDeadNode(N); |
507 | |
508 | return true; |
509 | } |
510 | |
511 | bool AVRDAGToDAGISel::selectMultiplication(llvm::SDNode *N) { |
512 | SDLoc DL(N); |
513 | MVT Type = N->getSimpleValueType(ResNo: 0); |
514 | |
515 | assert(Type == MVT::i8 && "unexpected value type" ); |
516 | |
517 | bool isSigned = N->getOpcode() == ISD::SMUL_LOHI; |
518 | unsigned MachineOp = isSigned ? AVR::MULSRdRr : AVR::MULRdRr; |
519 | |
520 | SDValue Lhs = N->getOperand(Num: 0); |
521 | SDValue Rhs = N->getOperand(Num: 1); |
522 | SDNode *Mul = CurDAG->getMachineNode(Opcode: MachineOp, dl: DL, VT: MVT::Glue, Op1: Lhs, Op2: Rhs); |
523 | SDValue InChain = CurDAG->getEntryNode(); |
524 | SDValue InGlue = SDValue(Mul, 0); |
525 | |
526 | // Copy the low half of the result, if it is needed. |
527 | if (N->hasAnyUseOfValue(Value: 0)) { |
528 | SDValue CopyFromLo = |
529 | CurDAG->getCopyFromReg(Chain: InChain, dl: DL, Reg: AVR::R0, VT: Type, Glue: InGlue); |
530 | |
531 | ReplaceUses(F: SDValue(N, 0), T: CopyFromLo); |
532 | |
533 | InChain = CopyFromLo.getValue(R: 1); |
534 | InGlue = CopyFromLo.getValue(R: 2); |
535 | } |
536 | |
537 | // Copy the high half of the result, if it is needed. |
538 | if (N->hasAnyUseOfValue(Value: 1)) { |
539 | SDValue CopyFromHi = |
540 | CurDAG->getCopyFromReg(Chain: InChain, dl: DL, Reg: AVR::R1, VT: Type, Glue: InGlue); |
541 | |
542 | ReplaceUses(F: SDValue(N, 1), T: CopyFromHi); |
543 | |
544 | InChain = CopyFromHi.getValue(R: 1); |
545 | InGlue = CopyFromHi.getValue(R: 2); |
546 | } |
547 | |
548 | CurDAG->RemoveDeadNode(N); |
549 | |
550 | // We need to clear R1. This is currently done (dirtily) |
551 | // using a custom inserter. |
552 | |
553 | return true; |
554 | } |
555 | |
556 | void AVRDAGToDAGISel::Select(SDNode *N) { |
557 | // If we have a custom node, we already have selected! |
558 | if (N->isMachineOpcode()) { |
559 | LLVM_DEBUG(errs() << "== " ; N->dump(CurDAG); errs() << "\n" ); |
560 | N->setNodeId(-1); |
561 | return; |
562 | } |
563 | |
564 | // See if subclasses can handle this node. |
565 | if (trySelect(N)) |
566 | return; |
567 | |
568 | // Select the default instruction |
569 | SelectCode(N); |
570 | } |
571 | |
572 | bool AVRDAGToDAGISel::trySelect(SDNode *N) { |
573 | unsigned Opcode = N->getOpcode(); |
574 | SDLoc DL(N); |
575 | |
576 | switch (Opcode) { |
577 | // Nodes we fully handle. |
578 | case ISD::FrameIndex: |
579 | return select<ISD::FrameIndex>(N); |
580 | case ISD::BRIND: |
581 | return select<ISD::BRIND>(N); |
582 | case ISD::UMUL_LOHI: |
583 | case ISD::SMUL_LOHI: |
584 | return selectMultiplication(N); |
585 | |
586 | // Nodes we handle partially. Other cases are autogenerated |
587 | case ISD::STORE: |
588 | return select<ISD::STORE>(N); |
589 | case ISD::LOAD: |
590 | return select<ISD::LOAD>(N); |
591 | case AVRISD::CALL: |
592 | return select<AVRISD::CALL>(N); |
593 | default: |
594 | return false; |
595 | } |
596 | } |
597 | |
598 | FunctionPass *llvm::createAVRISelDag(AVRTargetMachine &TM, |
599 | CodeGenOptLevel OptLevel) { |
600 | return new AVRDAGToDAGISelLegacy(TM, OptLevel); |
601 | } |
602 | |