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