Path: blob/main/contrib/llvm-project/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp
35266 views
//===-- AVRISelDAGToDAG.cpp - A dag to dag inst selector for AVR ----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file defines an instruction selector for the AVR target.9//10//===----------------------------------------------------------------------===//1112#include "AVR.h"13#include "AVRTargetMachine.h"14#include "MCTargetDesc/AVRMCTargetDesc.h"1516#include "llvm/CodeGen/MachineRegisterInfo.h"17#include "llvm/CodeGen/SelectionDAGISel.h"18#include "llvm/Support/Debug.h"19#include "llvm/Support/raw_ostream.h"2021#define DEBUG_TYPE "avr-isel"22#define PASS_NAME "AVR DAG->DAG Instruction Selection"2324using namespace llvm;2526namespace {2728/// Lowers LLVM IR (in DAG form) to AVR MC instructions (in DAG form).29class AVRDAGToDAGISel : public SelectionDAGISel {30public:31AVRDAGToDAGISel() = delete;3233AVRDAGToDAGISel(AVRTargetMachine &TM, CodeGenOptLevel OptLevel)34: SelectionDAGISel(TM, OptLevel), Subtarget(nullptr) {}3536bool runOnMachineFunction(MachineFunction &MF) override;3738bool SelectAddr(SDNode *Op, SDValue N, SDValue &Base, SDValue &Disp);3940bool selectIndexedLoad(SDNode *N);41unsigned selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT, int Bank);4243bool SelectInlineAsmMemoryOperand(const SDValue &Op,44InlineAsm::ConstraintCode ConstraintCode,45std::vector<SDValue> &OutOps) override;4647// Include the pieces autogenerated from the target description.48#include "AVRGenDAGISel.inc"4950private:51void Select(SDNode *N) override;52bool trySelect(SDNode *N);5354template <unsigned NodeType> bool select(SDNode *N);55bool selectMultiplication(SDNode *N);5657const AVRSubtarget *Subtarget;58};5960class AVRDAGToDAGISelLegacy : public SelectionDAGISelLegacy {61public:62static char ID;63AVRDAGToDAGISelLegacy(AVRTargetMachine &TM, CodeGenOptLevel OptLevel)64: SelectionDAGISelLegacy(65ID, std::make_unique<AVRDAGToDAGISel>(TM, OptLevel)) {}66};6768} // namespace6970char AVRDAGToDAGISelLegacy::ID = 0;7172INITIALIZE_PASS(AVRDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)7374bool AVRDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {75Subtarget = &MF.getSubtarget<AVRSubtarget>();76return SelectionDAGISel::runOnMachineFunction(MF);77}7879bool AVRDAGToDAGISel::SelectAddr(SDNode *Op, SDValue N, SDValue &Base,80SDValue &Disp) {81SDLoc dl(Op);82auto DL = CurDAG->getDataLayout();83MVT PtrVT = getTargetLowering()->getPointerTy(DL);8485// if the address is a frame index get the TargetFrameIndex.86if (const FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(N)) {87Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), PtrVT);88Disp = CurDAG->getTargetConstant(0, dl, MVT::i8);8990return true;91}9293// Match simple Reg + uimm6 operands.94if (N.getOpcode() != ISD::ADD && N.getOpcode() != ISD::SUB &&95!CurDAG->isBaseWithConstantOffset(N)) {96return false;97}9899if (const ConstantSDNode *RHS = dyn_cast<ConstantSDNode>(N.getOperand(1))) {100int RHSC = (int)RHS->getZExtValue();101102// Convert negative offsets into positives ones.103if (N.getOpcode() == ISD::SUB) {104RHSC = -RHSC;105}106107// <#Frame index + const>108// Allow folding offsets bigger than 63 so the frame pointer can be used109// directly instead of copying it around by adjusting and restoring it for110// each access.111if (N.getOperand(0).getOpcode() == ISD::FrameIndex) {112int FI = cast<FrameIndexSDNode>(N.getOperand(0))->getIndex();113114Base = CurDAG->getTargetFrameIndex(FI, PtrVT);115Disp = CurDAG->getTargetConstant(RHSC, dl, MVT::i16);116117return true;118}119120// The value type of the memory instruction determines what is the maximum121// offset allowed.122MVT VT = cast<MemSDNode>(Op)->getMemoryVT().getSimpleVT();123124// We only accept offsets that fit in 6 bits (unsigned), with the exception125// of 16-bit loads - those can only go up to 62, because we desugar them126// into a pair of 8-bit loads like `ldd rx, RHSC` + `ldd ry, RHSC + 1`.127bool OkI8 = VT == MVT::i8 && RHSC <= 63;128bool OkI16 = VT == MVT::i16 && RHSC <= 62;129130if (OkI8 || OkI16) {131Base = N.getOperand(0);132Disp = CurDAG->getTargetConstant(RHSC, dl, MVT::i8);133134return true;135}136}137138return false;139}140141bool AVRDAGToDAGISel::selectIndexedLoad(SDNode *N) {142const LoadSDNode *LD = cast<LoadSDNode>(N);143ISD::MemIndexedMode AM = LD->getAddressingMode();144MVT VT = LD->getMemoryVT().getSimpleVT();145auto PtrVT = getTargetLowering()->getPointerTy(CurDAG->getDataLayout());146147// We only care if this load uses a POSTINC or PREDEC mode.148if ((LD->getExtensionType() != ISD::NON_EXTLOAD) ||149(AM != ISD::POST_INC && AM != ISD::PRE_DEC)) {150151return false;152}153154unsigned Opcode = 0;155bool isPre = (AM == ISD::PRE_DEC);156int Offs = cast<ConstantSDNode>(LD->getOffset())->getSExtValue();157158switch (VT.SimpleTy) {159case MVT::i8: {160if ((!isPre && Offs != 1) || (isPre && Offs != -1)) {161return false;162}163164Opcode = (isPre) ? AVR::LDRdPtrPd : AVR::LDRdPtrPi;165break;166}167case MVT::i16: {168if ((!isPre && Offs != 2) || (isPre && Offs != -2)) {169return false;170}171172Opcode = (isPre) ? AVR::LDWRdPtrPd : AVR::LDWRdPtrPi;173break;174}175default:176return false;177}178179SDNode *ResNode =180CurDAG->getMachineNode(Opcode, SDLoc(N), VT, PtrVT, MVT::Other,181LD->getBasePtr(), LD->getChain());182ReplaceUses(N, ResNode);183CurDAG->RemoveDeadNode(N);184185return true;186}187188unsigned AVRDAGToDAGISel::selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT,189int Bank) {190// Progmem indexed loads only work in POSTINC mode.191if (LD->getExtensionType() != ISD::NON_EXTLOAD ||192LD->getAddressingMode() != ISD::POST_INC)193return 0;194195// Feature ELPM is needed for loading from extended program memory.196assert((Bank == 0 || Subtarget->hasELPM()) &&197"cannot load from extended program memory on this mcu");198199unsigned Opcode = 0;200int Offs = cast<ConstantSDNode>(LD->getOffset())->getSExtValue();201202if (VT.SimpleTy == MVT::i8 && Offs == 1 && Bank == 0)203Opcode = AVR::LPMRdZPi;204205// TODO: Implements the expansion of the following pseudo instructions.206// LPMWRdZPi: type == MVT::i16, offset == 2, Bank == 0.207// ELPMBRdZPi: type == MVT::i8, offset == 1, Bank > 0.208// ELPMWRdZPi: type == MVT::i16, offset == 2, Bank > 0.209210return Opcode;211}212213bool AVRDAGToDAGISel::SelectInlineAsmMemoryOperand(214const SDValue &Op, InlineAsm::ConstraintCode ConstraintCode,215std::vector<SDValue> &OutOps) {216assert((ConstraintCode == InlineAsm::ConstraintCode::m ||217ConstraintCode == InlineAsm::ConstraintCode::Q) &&218"Unexpected asm memory constraint");219220MachineRegisterInfo &RI = MF->getRegInfo();221const AVRSubtarget &STI = MF->getSubtarget<AVRSubtarget>();222const TargetLowering &TL = *STI.getTargetLowering();223SDLoc dl(Op);224auto DL = CurDAG->getDataLayout();225226const RegisterSDNode *RegNode = dyn_cast<RegisterSDNode>(Op);227228// If address operand is of PTRDISPREGS class, all is OK, then.229if (RegNode &&230RI.getRegClass(RegNode->getReg()) == &AVR::PTRDISPREGSRegClass) {231OutOps.push_back(Op);232return false;233}234235if (Op->getOpcode() == ISD::FrameIndex) {236SDValue Base, Disp;237238if (SelectAddr(Op.getNode(), Op, Base, Disp)) {239OutOps.push_back(Base);240OutOps.push_back(Disp);241242return false;243}244245return true;246}247248// If Op is add 'register, immediate' and249// register is either virtual register or register of PTRDISPREGSRegClass250if (Op->getOpcode() == ISD::ADD || Op->getOpcode() == ISD::SUB) {251SDValue CopyFromRegOp = Op->getOperand(0);252SDValue ImmOp = Op->getOperand(1);253ConstantSDNode *ImmNode = dyn_cast<ConstantSDNode>(ImmOp);254255unsigned Reg;256bool CanHandleRegImmOpt = ImmNode && ImmNode->getAPIntValue().ult(64);257258if (CopyFromRegOp->getOpcode() == ISD::CopyFromReg) {259RegisterSDNode *RegNode =260cast<RegisterSDNode>(CopyFromRegOp->getOperand(1));261Reg = RegNode->getReg();262CanHandleRegImmOpt &= (Register::isVirtualRegister(Reg) ||263AVR::PTRDISPREGSRegClass.contains(Reg));264} else {265CanHandleRegImmOpt = false;266}267268// If we detect proper case - correct virtual register class269// if needed and go to another inlineasm operand.270if (CanHandleRegImmOpt) {271SDValue Base, Disp;272273if (RI.getRegClass(Reg) != &AVR::PTRDISPREGSRegClass) {274SDLoc dl(CopyFromRegOp);275276Register VReg = RI.createVirtualRegister(&AVR::PTRDISPREGSRegClass);277278SDValue CopyToReg =279CurDAG->getCopyToReg(CopyFromRegOp, dl, VReg, CopyFromRegOp);280281SDValue NewCopyFromRegOp =282CurDAG->getCopyFromReg(CopyToReg, dl, VReg, TL.getPointerTy(DL));283284Base = NewCopyFromRegOp;285} else {286Base = CopyFromRegOp;287}288289if (ImmNode->getValueType(0) != MVT::i8) {290Disp = CurDAG->getTargetConstant(ImmNode->getZExtValue(), dl, MVT::i8);291} else {292Disp = ImmOp;293}294295OutOps.push_back(Base);296OutOps.push_back(Disp);297298return false;299}300}301302// More generic case.303// Create chain that puts Op into pointer register304// and return that register.305Register VReg = RI.createVirtualRegister(&AVR::PTRDISPREGSRegClass);306307SDValue CopyToReg = CurDAG->getCopyToReg(Op, dl, VReg, Op);308SDValue CopyFromReg =309CurDAG->getCopyFromReg(CopyToReg, dl, VReg, TL.getPointerTy(DL));310311OutOps.push_back(CopyFromReg);312313return false;314}315316template <> bool AVRDAGToDAGISel::select<ISD::FrameIndex>(SDNode *N) {317auto DL = CurDAG->getDataLayout();318319// Convert the frameindex into a temp instruction that will hold the320// effective address of the final stack slot.321int FI = cast<FrameIndexSDNode>(N)->getIndex();322SDValue TFI =323CurDAG->getTargetFrameIndex(FI, getTargetLowering()->getPointerTy(DL));324325CurDAG->SelectNodeTo(N, AVR::FRMIDX, getTargetLowering()->getPointerTy(DL),326TFI, CurDAG->getTargetConstant(0, SDLoc(N), MVT::i16));327return true;328}329330template <> bool AVRDAGToDAGISel::select<ISD::STORE>(SDNode *N) {331// Use the STD{W}SPQRr pseudo instruction when passing arguments through332// the stack on function calls for further expansion during the PEI phase.333const StoreSDNode *ST = cast<StoreSDNode>(N);334SDValue BasePtr = ST->getBasePtr();335336// Early exit when the base pointer is a frame index node or a constant.337if (isa<FrameIndexSDNode>(BasePtr) || isa<ConstantSDNode>(BasePtr) ||338BasePtr.isUndef()) {339return false;340}341342const RegisterSDNode *RN = dyn_cast<RegisterSDNode>(BasePtr.getOperand(0));343// Only stores where SP is the base pointer are valid.344if (!RN || (RN->getReg() != AVR::SP)) {345return false;346}347348int CST = (int)BasePtr.getConstantOperandVal(1);349SDValue Chain = ST->getChain();350EVT VT = ST->getValue().getValueType();351SDLoc DL(N);352SDValue Offset = CurDAG->getTargetConstant(CST, DL, MVT::i16);353SDValue Ops[] = {BasePtr.getOperand(0), Offset, ST->getValue(), Chain};354unsigned Opc = (VT == MVT::i16) ? AVR::STDWSPQRr : AVR::STDSPQRr;355356SDNode *ResNode = CurDAG->getMachineNode(Opc, DL, MVT::Other, Ops);357358// Transfer memory operands.359CurDAG->setNodeMemRefs(cast<MachineSDNode>(ResNode), {ST->getMemOperand()});360361ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));362CurDAG->RemoveDeadNode(N);363364return true;365}366367template <> bool AVRDAGToDAGISel::select<ISD::LOAD>(SDNode *N) {368const LoadSDNode *LD = cast<LoadSDNode>(N);369if (!AVR::isProgramMemoryAccess(LD)) {370// Check if the opcode can be converted into an indexed load.371return selectIndexedLoad(N);372}373374if (!Subtarget->hasLPM())375report_fatal_error("cannot load from program memory on this mcu");376377int ProgMemBank = AVR::getProgramMemoryBank(LD);378if (ProgMemBank < 0 || ProgMemBank > 5)379report_fatal_error("unexpected program memory bank");380if (ProgMemBank > 0 && !Subtarget->hasELPM())381report_fatal_error("unexpected program memory bank");382383// This is a flash memory load, move the pointer into R31R30 and emit384// the lpm instruction.385MVT VT = LD->getMemoryVT().getSimpleVT();386SDValue Chain = LD->getChain();387SDValue Ptr = LD->getBasePtr();388SDNode *ResNode;389SDLoc DL(N);390391Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, Ptr, SDValue());392Ptr = CurDAG->getCopyFromReg(Chain, DL, AVR::R31R30, MVT::i16,393Chain.getValue(1));394395// Check if the opcode can be converted into an indexed load.396if (unsigned LPMOpc = selectIndexedProgMemLoad(LD, VT, ProgMemBank)) {397// It is legal to fold the load into an indexed load.398if (ProgMemBank == 0) {399ResNode =400CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other, Ptr);401} else {402// Do not combine the LDI instruction into the ELPM pseudo instruction,403// since it may be reused by other ELPM pseudo instructions.404SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);405auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);406ResNode = CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other,407Ptr, SDValue(NP, 0));408}409} else {410// Selecting an indexed load is not legal, fallback to a normal load.411switch (VT.SimpleTy) {412case MVT::i8:413if (ProgMemBank == 0) {414unsigned Opc = Subtarget->hasLPMX() ? AVR::LPMRdZ : AVR::LPMBRdZ;415ResNode =416CurDAG->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.420SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);421auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);422ResNode = CurDAG->getMachineNode(AVR::ELPMBRdZ, DL, MVT::i8, MVT::Other,423Ptr, SDValue(NP, 0));424}425break;426case MVT::i16:427if (ProgMemBank == 0) {428ResNode =429CurDAG->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.433SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);434auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);435ResNode = CurDAG->getMachineNode(AVR::ELPMWRdZ, DL, MVT::i16,436MVT::Other, Ptr, SDValue(NP, 0));437}438break;439default:440llvm_unreachable("Unsupported VT!");441}442}443444// Transfer memory operands.445CurDAG->setNodeMemRefs(cast<MachineSDNode>(ResNode), {LD->getMemOperand()});446447ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));448ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1));449CurDAG->RemoveDeadNode(N);450451return true;452}453454template <> bool AVRDAGToDAGISel::select<AVRISD::CALL>(SDNode *N) {455SDValue InGlue;456SDValue Chain = N->getOperand(0);457SDValue Callee = N->getOperand(1);458unsigned LastOpNum = N->getNumOperands() - 1;459460// Direct calls are autogenerated.461unsigned Op = Callee.getOpcode();462if (Op == ISD::TargetGlobalAddress || Op == ISD::TargetExternalSymbol) {463return false;464}465466// Skip the incoming flag if present467if (N->getOperand(LastOpNum).getValueType() == MVT::Glue) {468--LastOpNum;469}470471SDLoc DL(N);472Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, Callee, InGlue);473SmallVector<SDValue, 8> Ops;474Ops.push_back(CurDAG->getRegister(AVR::R31R30, MVT::i16));475476// Map all operands into the new node.477for (unsigned i = 2, e = LastOpNum + 1; i != e; ++i) {478Ops.push_back(N->getOperand(i));479}480481Ops.push_back(Chain);482Ops.push_back(Chain.getValue(1));483484SDNode *ResNode = CurDAG->getMachineNode(485Subtarget->hasEIJMPCALL() ? AVR::EICALL : AVR::ICALL, DL, MVT::Other,486MVT::Glue, Ops);487488ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));489ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1));490CurDAG->RemoveDeadNode(N);491492return true;493}494495template <> bool AVRDAGToDAGISel::select<ISD::BRIND>(SDNode *N) {496SDValue Chain = N->getOperand(0);497SDValue JmpAddr = N->getOperand(1);498499SDLoc DL(N);500// Move the destination address of the indirect branch into R31R30.501Chain = CurDAG->getCopyToReg(Chain, DL, AVR::R31R30, JmpAddr);502SDNode *ResNode = CurDAG->getMachineNode(AVR::IJMP, DL, MVT::Other, Chain);503504ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));505CurDAG->RemoveDeadNode(N);506507return true;508}509510bool AVRDAGToDAGISel::selectMultiplication(llvm::SDNode *N) {511SDLoc DL(N);512MVT Type = N->getSimpleValueType(0);513514assert(Type == MVT::i8 && "unexpected value type");515516bool isSigned = N->getOpcode() == ISD::SMUL_LOHI;517unsigned MachineOp = isSigned ? AVR::MULSRdRr : AVR::MULRdRr;518519SDValue Lhs = N->getOperand(0);520SDValue Rhs = N->getOperand(1);521SDNode *Mul = CurDAG->getMachineNode(MachineOp, DL, MVT::Glue, Lhs, Rhs);522SDValue InChain = CurDAG->getEntryNode();523SDValue InGlue = SDValue(Mul, 0);524525// Copy the low half of the result, if it is needed.526if (N->hasAnyUseOfValue(0)) {527SDValue CopyFromLo =528CurDAG->getCopyFromReg(InChain, DL, AVR::R0, Type, InGlue);529530ReplaceUses(SDValue(N, 0), CopyFromLo);531532InChain = CopyFromLo.getValue(1);533InGlue = CopyFromLo.getValue(2);534}535536// Copy the high half of the result, if it is needed.537if (N->hasAnyUseOfValue(1)) {538SDValue CopyFromHi =539CurDAG->getCopyFromReg(InChain, DL, AVR::R1, Type, InGlue);540541ReplaceUses(SDValue(N, 1), CopyFromHi);542543InChain = CopyFromHi.getValue(1);544InGlue = CopyFromHi.getValue(2);545}546547CurDAG->RemoveDeadNode(N);548549// We need to clear R1. This is currently done (dirtily)550// using a custom inserter.551552return true;553}554555void AVRDAGToDAGISel::Select(SDNode *N) {556// If we have a custom node, we already have selected!557if (N->isMachineOpcode()) {558LLVM_DEBUG(errs() << "== "; N->dump(CurDAG); errs() << "\n");559N->setNodeId(-1);560return;561}562563// See if subclasses can handle this node.564if (trySelect(N))565return;566567// Select the default instruction568SelectCode(N);569}570571bool AVRDAGToDAGISel::trySelect(SDNode *N) {572unsigned Opcode = N->getOpcode();573SDLoc DL(N);574575switch (Opcode) {576// Nodes we fully handle.577case ISD::FrameIndex:578return select<ISD::FrameIndex>(N);579case ISD::BRIND:580return select<ISD::BRIND>(N);581case ISD::UMUL_LOHI:582case ISD::SMUL_LOHI:583return selectMultiplication(N);584585// Nodes we handle partially. Other cases are autogenerated586case ISD::STORE:587return select<ISD::STORE>(N);588case ISD::LOAD:589return select<ISD::LOAD>(N);590case AVRISD::CALL:591return select<AVRISD::CALL>(N);592default:593return false;594}595}596597FunctionPass *llvm::createAVRISelDag(AVRTargetMachine &TM,598CodeGenOptLevel OptLevel) {599return new AVRDAGToDAGISelLegacy(TM, OptLevel);600}601602603