600 lines · cpp
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-exception6//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 25using namespace llvm;26 27namespace {28 29/// Lowers LLVM IR (in DAG form) to AVR MC instructions (in DAG form).30class AVRDAGToDAGISel : public SelectionDAGISel {31public: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 51private: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 61class AVRDAGToDAGISelLegacy : public SelectionDAGISelLegacy {62public:63 static char ID;64 AVRDAGToDAGISelLegacy(AVRTargetMachine &TM, CodeGenOptLevel OptLevel)65 : SelectionDAGISelLegacy(66 ID, std::make_unique<AVRDAGToDAGISel>(TM, OptLevel)) {}67};68 69} // namespace70 71char AVRDAGToDAGISelLegacy::ID = 0;72 73INITIALIZE_PASS(AVRDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)74 75bool AVRDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {76 Subtarget = &MF.getSubtarget<AVRSubtarget>();77 return SelectionDAGISel::runOnMachineFunction(MF);78}79 80bool 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>(N)) {88 Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), PtrVT);89 Disp = CurDAG->getTargetConstant(0, dl, 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(N)) {97 return false;98 }99 100 if (const ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(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 used110 // directly instead of copying it around by adjusting and restoring it for111 // each access.112 if (N.getOperand(0).getOpcode() == ISD::FrameIndex) {113 int FI = cast<FrameIndexSDNode>(N.getOperand(0))->getIndex();114 115 Base = CurDAG->getTargetFrameIndex(FI, PtrVT);116 Disp = CurDAG->getTargetConstant(RHSC, dl, MVT::i16);117 118 return true;119 }120 121 // The value type of the memory instruction determines what is the maximum122 // offset allowed.123 MVT VT = cast<MemSDNode>(Op)->getMemoryVT().getSimpleVT();124 125 // We only accept offsets that fit in 6 bits (unsigned), with the exception126 // of 16-bit loads - those can only go up to 62, because we desugar them127 // 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(0);133 Disp = CurDAG->getTargetConstant(RHSC, dl, MVT::i8);134 135 return true;136 }137 }138 139 return false;140}141 142bool AVRDAGToDAGISel::selectIndexedLoad(SDNode *N) {143 const LoadSDNode *LD = cast<LoadSDNode>(N);144 ISD::MemIndexedMode AM = LD->getAddressingMode();145 MVT VT = LD->getMemoryVT().getSimpleVT();146 auto PtrVT = getTargetLowering()->getPointerTy(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>(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, SDLoc(N), VT, PtrVT, MVT::Other,182 LD->getBasePtr(), LD->getChain());183 ReplaceUses(N, ResNode);184 CurDAG->RemoveDeadNode(N);185 186 return true;187}188 189unsigned 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>(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 214bool 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>(Op);228 229 // If address operand is of PTRDISPREGS class, all is OK, then.230 if (RegNode &&231 RI.getRegClass(RegNode->getReg()) == &AVR::PTRDISPREGSRegClass) {232 OutOps.push_back(Op);233 return false;234 }235 236 if (Op->getOpcode() == ISD::FrameIndex) {237 SDValue Base, Disp;238 239 if (SelectAddr(Op.getNode(), Op, Base, Disp)) {240 OutOps.push_back(Base);241 OutOps.push_back(Disp);242 243 return false;244 }245 246 return true;247 }248 249 // If Op is add 'register, immediate' and250 // register is either virtual register or register of PTRDISPREGSRegClass251 if (Op->getOpcode() == ISD::ADD || Op->getOpcode() == ISD::SUB) {252 SDValue CopyFromRegOp = Op->getOperand(0);253 SDValue ImmOp = Op->getOperand(1);254 ConstantSDNode *ImmNode = dyn_cast<ConstantSDNode>(ImmOp);255 256 unsigned Reg;257 bool CanHandleRegImmOpt = ImmNode && ImmNode->getAPIntValue().ult(64);258 259 if (CopyFromRegOp->getOpcode() == ISD::CopyFromReg) {260 RegisterSDNode *RegNode =261 cast<RegisterSDNode>(CopyFromRegOp->getOperand(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 class270 // 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(&AVR::PTRDISPREGSRegClass);278 279 SDValue CopyToReg =280 CurDAG->getCopyToReg(CopyFromRegOp, dl, VReg, CopyFromRegOp);281 282 SDValue NewCopyFromRegOp =283 CurDAG->getCopyFromReg(CopyToReg, dl, VReg, TL.getPointerTy(DL));284 285 Base = NewCopyFromRegOp;286 } else {287 Base = CopyFromRegOp;288 }289 290 if (ImmNode->getValueType(0) != MVT::i8) {291 Disp = CurDAG->getTargetConstant(ImmNode->getZExtValue(), dl, MVT::i8);292 } else {293 Disp = ImmOp;294 }295 296 OutOps.push_back(Base);297 OutOps.push_back(Disp);298 299 return false;300 }301 }302 303 // More generic case.304 // Create chain that puts Op into pointer register305 // and return that register.306 Register VReg = RI.createVirtualRegister(&AVR::PTRDISPREGSRegClass);307 308 SDValue CopyToReg = CurDAG->getCopyToReg(Op, dl, VReg, Op);309 SDValue CopyFromReg =310 CurDAG->getCopyFromReg(CopyToReg, dl, VReg, TL.getPointerTy(DL));311 312 OutOps.push_back(CopyFromReg);313 314 return false;315}316 317template <> bool AVRDAGToDAGISel::select<ISD::FrameIndex>(SDNode *N) {318 auto DL = CurDAG->getDataLayout();319 320 // Convert the frameindex into a temp instruction that will hold the321 // effective address of the final stack slot.322 int FI = cast<FrameIndexSDNode>(N)->getIndex();323 SDValue TFI =324 CurDAG->getTargetFrameIndex(FI, getTargetLowering()->getPointerTy(DL));325 326 CurDAG->SelectNodeTo(N, AVR::FRMIDX, getTargetLowering()->getPointerTy(DL),327 TFI, CurDAG->getTargetConstant(0, SDLoc(N), MVT::i16));328 return true;329}330 331template <> bool AVRDAGToDAGISel::select<ISD::STORE>(SDNode *N) {332 // Use the STD{W}SPQRr pseudo instruction when passing arguments through333 // the stack on function calls for further expansion during the PEI phase.334 const StoreSDNode *ST = cast<StoreSDNode>(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>(BasePtr) || isa<ConstantSDNode>(BasePtr) ||339 BasePtr.isUndef()) {340 return false;341 }342 343 const RegisterSDNode *RN = dyn_cast<RegisterSDNode>(BasePtr.getOperand(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(1);350 SDValue Chain = ST->getChain();351 EVT VT = ST->getValue().getValueType();352 SDLoc DL(N);353 SDValue Offset = CurDAG->getTargetConstant(CST, DL, MVT::i16);354 SDValue Ops[] = {BasePtr.getOperand(0), Offset, ST->getValue(), Chain};355 unsigned Opc = (VT == MVT::i16) ? AVR::STDWSPQRr : AVR::STDSPQRr;356 357 SDNode *ResNode = CurDAG->getMachineNode(Opc, DL, MVT::Other, Ops);358 359 // Transfer memory operands.360 CurDAG->setNodeMemRefs(cast<MachineSDNode>(ResNode), {ST->getMemOperand()});361 362 ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));363 CurDAG->RemoveDeadNode(N);364 365 return true;366}367 368template <> bool AVRDAGToDAGISel::select<ISD::LOAD>(SDNode *N) {369 const LoadSDNode *LD = cast<LoadSDNode>(N);370 if (!AVR::isProgramMemoryAccess(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("cannot load from program memory on this mcu");377 378 int ProgMemBank = AVR::getProgramMemoryBank(LD);379 if (ProgMemBank < 0 || ProgMemBank > 5)380 report_fatal_error("unexpected program memory bank");381 if (ProgMemBank > 0 && !Subtarget->hasELPM())382 report_fatal_error("unexpected program memory bank");383 384 // This is a flash memory load, move the pointer into R31R30 and emit385 // 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, AVR::R31R30, Ptr, SDValue());393 Ptr = CurDAG->getCopyFromReg(Chain, DL, AVR::R31R30, MVT::i16,394 Chain.getValue(1));395 396 // Check if the opcode can be converted into an indexed load.397 if (unsigned LPMOpc = selectIndexedProgMemLoad(LD, VT, ProgMemBank)) {398 // It is legal to fold the load into an indexed load.399 if (ProgMemBank == 0) {400 ResNode =401 CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other, 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(ProgMemBank, DL, MVT::i8);406 auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);407 ResNode = CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other,408 Ptr, 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 = CurDAG->getMachineNode(Opc, DL, MVT::i8, MVT::Other, Ptr);417 } else {418 // Do not combine the LDI instruction into the ELPM pseudo instruction,419 // since it may be reused by other ELPM pseudo instructions.420 SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);421 auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);422 ResNode = CurDAG->getMachineNode(AVR::ELPMBRdZ, DL, MVT::i8, MVT::Other,423 Ptr, SDValue(NP, 0));424 }425 break;426 case MVT::i16:427 if (ProgMemBank == 0) {428 ResNode =429 CurDAG->getMachineNode(AVR::LPMWRdZ, DL, MVT::i16, MVT::Other, Ptr);430 } else {431 // Do not combine the LDI instruction into the ELPM pseudo instruction,432 // since LDI requires the destination register in range R16~R31.433 SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);434 auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);435 ResNode = CurDAG->getMachineNode(AVR::ELPMWRdZ, DL, MVT::i16,436 MVT::Other, Ptr, SDValue(NP, 0));437 }438 break;439 default:440 llvm_unreachable("Unsupported VT!");441 }442 }443 444 // Transfer memory operands.445 CurDAG->setNodeMemRefs(cast<MachineSDNode>(ResNode), {LD->getMemOperand()});446 447 ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));448 ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1));449 CurDAG->RemoveDeadNode(N);450 451 return true;452}453 454template <> bool AVRDAGToDAGISel::select<AVRISD::CALL>(SDNode *N) {455 SDValue InGlue;456 SDValue Chain = N->getOperand(0);457 SDValue Callee = N->getOperand(1);458 unsigned LastOpNum = N->getNumOperands() - 1;459 460 // Direct calls are autogenerated.461 unsigned Op = Callee.getOpcode();462 if (Op == ISD::TargetGlobalAddress || Op == ISD::TargetExternalSymbol) {463 return false;464 }465 466 // Skip the incoming flag if present467 if (N->getOperand(LastOpNum).getValueType() == MVT::Glue) {468 --LastOpNum;469 }470 471 SDLoc DL(N);472 Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, Callee, InGlue);473 SmallVector<SDValue, 8> Ops;474 Ops.push_back(CurDAG->getRegister(AVR::R31R30, MVT::i16));475 476 // Map all operands into the new node.477 for (unsigned i = 2, e = LastOpNum + 1; i != e; ++i) {478 Ops.push_back(N->getOperand(i));479 }480 481 Ops.push_back(Chain);482 Ops.push_back(Chain.getValue(1));483 484 SDNode *ResNode = CurDAG->getMachineNode(485 Subtarget->hasEIJMPCALL() ? AVR::EICALL : AVR::ICALL, DL, MVT::Other,486 MVT::Glue, Ops);487 488 ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));489 ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1));490 CurDAG->RemoveDeadNode(N);491 492 return true;493}494 495template <> bool AVRDAGToDAGISel::select<ISD::BRIND>(SDNode *N) {496 SDValue Chain = N->getOperand(0);497 SDValue JmpAddr = N->getOperand(1);498 499 SDLoc DL(N);500 // Move the destination address of the indirect branch into R31R30.501 Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, JmpAddr);502 SDNode *ResNode = CurDAG->getMachineNode(AVR::IJMP, DL, MVT::Other, Chain);503 504 ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));505 CurDAG->RemoveDeadNode(N);506 507 return true;508}509 510bool AVRDAGToDAGISel::selectMultiplication(llvm::SDNode *N) {511 SDLoc DL(N);512 MVT Type = N->getSimpleValueType(0);513 514 assert(Type == MVT::i8 && "unexpected value type");515 516 bool isSigned = N->getOpcode() == ISD::SMUL_LOHI;517 unsigned MachineOp = isSigned ? AVR::MULSRdRr : AVR::MULRdRr;518 519 SDValue Lhs = N->getOperand(0);520 SDValue Rhs = N->getOperand(1);521 SDNode *Mul = CurDAG->getMachineNode(MachineOp, DL, MVT::Glue, Lhs, Rhs);522 SDValue InChain = CurDAG->getEntryNode();523 SDValue InGlue = SDValue(Mul, 0);524 525 // Copy the low half of the result, if it is needed.526 if (N->hasAnyUseOfValue(0)) {527 SDValue CopyFromLo =528 CurDAG->getCopyFromReg(InChain, DL, AVR::R0, Type, InGlue);529 530 ReplaceUses(SDValue(N, 0), CopyFromLo);531 532 InChain = CopyFromLo.getValue(1);533 InGlue = CopyFromLo.getValue(2);534 }535 536 // Copy the high half of the result, if it is needed.537 if (N->hasAnyUseOfValue(1)) {538 SDValue CopyFromHi =539 CurDAG->getCopyFromReg(InChain, DL, AVR::R1, Type, InGlue);540 541 ReplaceUses(SDValue(N, 1), CopyFromHi);542 543 InChain = CopyFromHi.getValue(1);544 InGlue = CopyFromHi.getValue(2);545 }546 547 CurDAG->RemoveDeadNode(N);548 549 // We need to clear R1. This is currently done (dirtily)550 // using a custom inserter.551 552 return true;553}554 555void AVRDAGToDAGISel::Select(SDNode *N) {556 // If we have a custom node, we already have selected!557 if (N->isMachineOpcode()) {558 LLVM_DEBUG(errs() << "== "; N->dump(CurDAG); errs() << "\n");559 N->setNodeId(-1);560 return;561 }562 563 // See if subclasses can handle this node.564 if (trySelect(N))565 return;566 567 // Select the default instruction568 SelectCode(N);569}570 571bool AVRDAGToDAGISel::trySelect(SDNode *N) {572 unsigned Opcode = N->getOpcode();573 574 switch (Opcode) {575 // Nodes we fully handle.576 case ISD::FrameIndex:577 return select<ISD::FrameIndex>(N);578 case ISD::BRIND:579 return select<ISD::BRIND>(N);580 case ISD::UMUL_LOHI:581 case ISD::SMUL_LOHI:582 return selectMultiplication(N);583 584 // Nodes we handle partially. Other cases are autogenerated585 case ISD::STORE:586 return select<ISD::STORE>(N);587 case ISD::LOAD:588 return select<ISD::LOAD>(N);589 case AVRISD::CALL:590 return select<AVRISD::CALL>(N);591 default:592 return false;593 }594}595 596FunctionPass *llvm::createAVRISelDag(AVRTargetMachine &TM,597 CodeGenOptLevel OptLevel) {598 return new AVRDAGToDAGISelLegacy(TM, OptLevel);599}600