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