Skip to content

Commit 94173dc

Browse files
committed
[AVR] Generate ELPM for loading byte/word from extended program memory
Reviewed By: aykevl Differential Revision: https://reviews.llvm.org/D116493
1 parent c1dd607 commit 94173dc

File tree

7 files changed

+577
-49
lines changed

7 files changed

+577
-49
lines changed

llvm/lib/Target/AVR/AVR.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ inline bool isProgramMemoryAccess(MemSDNode const *N) {
7777
return false;
7878
}
7979

80+
// Get the index of the program memory bank.
81+
// -1: not program memory
82+
// 0: ordinary program memory
83+
// 1~5: extended program memory
84+
inline int getProgramMemoryBank(MemSDNode const *N) {
85+
auto *V = N->getMemOperand()->getValue();
86+
if (V == nullptr || !isProgramMemoryAddress(V))
87+
return -1;
88+
AddressSpace AS = getAddressSpace(V);
89+
assert(ProgramMemory <= AS && AS <= ProgramMemory5);
90+
return static_cast<int>(AS - ProgramMemory);
91+
}
92+
8093
} // end of namespace AVR
8194

8295
} // end namespace llvm

llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class AVRExpandPseudo : public MachineFunctionPass {
102102
bool expandLSLW12Rd(Block &MBB, BlockIt MBBI);
103103
bool expandLSRW12Rd(Block &MBB, BlockIt MBBI);
104104

105+
// Common implementation of LPMWRdZ and ELPMWRdZ.
106+
bool expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt);
107+
105108
/// Scavenges a free GPR8 register for use.
106109
Register scavengeGPR8(MachineInstr &MI);
107110
};
@@ -809,18 +812,25 @@ bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) {
809812
return true;
810813
}
811814

812-
template <>
813-
bool AVRExpandPseudo::expand<AVR::LPMWRdZ>(Block &MBB, BlockIt MBBI) {
815+
bool AVRExpandPseudo::expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt) {
814816
MachineInstr &MI = *MBBI;
815817
Register DstLoReg, DstHiReg;
816818
Register DstReg = MI.getOperand(0).getReg();
817819
Register TmpReg = 0; // 0 for no temporary register
818820
Register SrcReg = MI.getOperand(1).getReg();
819821
bool SrcIsKill = MI.getOperand(1).isKill();
820-
unsigned OpLo = AVR::LPMRdZPi;
821-
unsigned OpHi = AVR::LPMRdZ;
822+
unsigned OpLo = IsExt ? AVR::ELPMRdZPi : AVR::LPMRdZPi;
823+
unsigned OpHi = IsExt ? AVR::ELPMRdZ : AVR::LPMRdZ;
822824
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
823825

826+
// Set the I/O register RAMPZ for ELPM.
827+
if (IsExt) {
828+
const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
829+
Register Bank = MI.getOperand(2).getReg();
830+
// out RAMPZ, rtmp
831+
buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(Bank);
832+
}
833+
824834
// Use a temporary register if src and dst registers are the same.
825835
if (DstReg == SrcReg)
826836
TmpReg = scavengeGPR8(MI);
@@ -857,9 +867,52 @@ bool AVRExpandPseudo::expand<AVR::LPMWRdZ>(Block &MBB, BlockIt MBBI) {
857867
return true;
858868
}
859869

870+
template <>
871+
bool AVRExpandPseudo::expand<AVR::LPMWRdZ>(Block &MBB, BlockIt MBBI) {
872+
return expandLPMWELPMW(MBB, MBBI, false);
873+
}
874+
875+
template <>
876+
bool AVRExpandPseudo::expand<AVR::ELPMWRdZ>(Block &MBB, BlockIt MBBI) {
877+
return expandLPMWELPMW(MBB, MBBI, true);
878+
}
879+
880+
template <>
881+
bool AVRExpandPseudo::expand<AVR::ELPMBRdZ>(Block &MBB, BlockIt MBBI) {
882+
MachineInstr &MI = *MBBI;
883+
Register DstReg = MI.getOperand(0).getReg();
884+
Register SrcReg = MI.getOperand(1).getReg();
885+
Register BankReg = MI.getOperand(2).getReg();
886+
bool SrcIsKill = MI.getOperand(1).isKill();
887+
const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
888+
889+
// Set the I/O register RAMPZ for ELPM (out RAMPZ, rtmp).
890+
buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg);
891+
892+
// Load byte.
893+
auto MILB = buildMI(MBB, MBBI, AVR::ELPMRdZ)
894+
.addReg(DstReg, RegState::Define)
895+
.addReg(SrcReg, getKillRegState(SrcIsKill));
896+
897+
MILB.setMemRefs(MI.memoperands());
898+
899+
MI.eraseFromParent();
900+
return true;
901+
}
902+
860903
template <>
861904
bool AVRExpandPseudo::expand<AVR::LPMWRdZPi>(Block &MBB, BlockIt MBBI) {
862-
llvm_unreachable("wide LPMPi is unimplemented");
905+
llvm_unreachable("16-bit LPMPi is unimplemented");
906+
}
907+
908+
template <>
909+
bool AVRExpandPseudo::expand<AVR::ELPMBRdZPi>(Block &MBB, BlockIt MBBI) {
910+
llvm_unreachable("byte ELPMPi is unimplemented");
911+
}
912+
913+
template <>
914+
bool AVRExpandPseudo::expand<AVR::ELPMWRdZPi>(Block &MBB, BlockIt MBBI) {
915+
llvm_unreachable("16-bit ELPMPi is unimplemented");
863916
}
864917

865918
template <typename Func>
@@ -2269,6 +2322,10 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) {
22692322
EXPAND(AVR::LDDWRdPtrQ);
22702323
EXPAND(AVR::LPMWRdZ);
22712324
EXPAND(AVR::LPMWRdZPi);
2325+
EXPAND(AVR::ELPMBRdZ);
2326+
EXPAND(AVR::ELPMWRdZ);
2327+
EXPAND(AVR::ELPMBRdZPi);
2328+
EXPAND(AVR::ELPMWRdZPi);
22722329
EXPAND(AVR::AtomicLoad8);
22732330
EXPAND(AVR::AtomicLoad16);
22742331
EXPAND(AVR::AtomicStore8);

llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class AVRDAGToDAGISel : public SelectionDAGISel {
3838
bool SelectAddr(SDNode *Op, SDValue N, SDValue &Base, SDValue &Disp);
3939

4040
bool selectIndexedLoad(SDNode *N);
41-
unsigned selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT);
41+
unsigned selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT, int Bank);
4242

4343
bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintCode,
4444
std::vector<SDValue> &OutOps) override;
@@ -165,35 +165,31 @@ bool AVRDAGToDAGISel::selectIndexedLoad(SDNode *N) {
165165
return true;
166166
}
167167

168-
unsigned AVRDAGToDAGISel::selectIndexedProgMemLoad(const LoadSDNode *LD,
169-
MVT VT) {
170-
ISD::MemIndexedMode AM = LD->getAddressingMode();
171-
168+
unsigned AVRDAGToDAGISel::selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT,
169+
int Bank) {
172170
// Progmem indexed loads only work in POSTINC mode.
173-
if (LD->getExtensionType() != ISD::NON_EXTLOAD || AM != ISD::POST_INC) {
171+
if (LD->getExtensionType() != ISD::NON_EXTLOAD ||
172+
LD->getAddressingMode() != ISD::POST_INC)
174173
return 0;
175-
}
174+
175+
// Feature ELPM is needed for loading from extended program memory.
176+
assert((Bank == 0 || Subtarget->hasELPM()) &&
177+
"cannot load from extended program memory on this mcu");
176178

177179
unsigned Opcode = 0;
178180
int Offs = cast<ConstantSDNode>(LD->getOffset())->getSExtValue();
179181

180182
switch (VT.SimpleTy) {
181-
case MVT::i8: {
182-
if (Offs != 1) {
183-
return 0;
184-
}
185-
Opcode = AVR::LPMRdZPi;
183+
case MVT::i8:
184+
if (Offs == 1)
185+
Opcode = Bank > 0 ? AVR::ELPMBRdZPi : AVR::LPMRdZPi;
186186
break;
187-
}
188-
case MVT::i16: {
189-
if (Offs != 2) {
190-
return 0;
191-
}
192-
Opcode = AVR::LPMWRdZPi;
187+
case MVT::i16:
188+
if (Offs == 2)
189+
Opcode = Bank > 0 ? AVR::ELPMWRdZPi : AVR::LPMWRdZPi;
193190
break;
194-
}
195191
default:
196-
return 0;
192+
break;
197193
}
198194

199195
return Opcode;
@@ -360,7 +356,12 @@ template <> bool AVRDAGToDAGISel::select<ISD::LOAD>(SDNode *N) {
360356
return selectIndexedLoad(N);
361357
}
362358

363-
assert(Subtarget->hasLPM() && "cannot load from program memory on this mcu");
359+
if (!Subtarget->hasLPM())
360+
report_fatal_error("cannot load from program memory on this mcu");
361+
362+
int ProgMemBank = AVR::getProgramMemoryBank(LD);
363+
if (ProgMemBank < 0 || ProgMemBank > 5)
364+
report_fatal_error("unexpected program memory bank");
364365

365366
// This is a flash memory load, move the pointer into R31R30 and emit
366367
// the lpm instruction.
@@ -374,25 +375,48 @@ template <> bool AVRDAGToDAGISel::select<ISD::LOAD>(SDNode *N) {
374375
Ptr = CurDAG->getCopyFromReg(Chain, DL, AVR::R31R30, MVT::i16,
375376
Chain.getValue(1));
376377

377-
SDValue RegZ = CurDAG->getRegister(AVR::R31R30, MVT::i16);
378-
379378
// Check if the opcode can be converted into an indexed load.
380-
if (unsigned LPMOpc = selectIndexedProgMemLoad(LD, VT)) {
379+
if (unsigned LPMOpc = selectIndexedProgMemLoad(LD, VT, ProgMemBank)) {
381380
// It is legal to fold the load into an indexed load.
382-
ResNode =
383-
CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other, Ptr, RegZ);
384-
ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1));
381+
if (ProgMemBank == 0) {
382+
ResNode =
383+
CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other, Ptr);
384+
} else {
385+
// Do not combine the LDI instruction into the ELPM pseudo instruction,
386+
// since it may be reused by other ELPM pseudo instructions.
387+
SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);
388+
auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);
389+
ResNode = CurDAG->getMachineNode(LPMOpc, DL, VT, MVT::i16, MVT::Other,
390+
Ptr, SDValue(NP, 0));
391+
}
385392
} else {
386393
// Selecting an indexed load is not legal, fallback to a normal load.
387394
switch (VT.SimpleTy) {
388395
case MVT::i8:
389-
ResNode = CurDAG->getMachineNode(AVR::LPMRdZ, DL, MVT::i8, MVT::Other,
390-
Ptr, RegZ);
396+
if (ProgMemBank == 0) {
397+
ResNode =
398+
CurDAG->getMachineNode(AVR::LPMRdZ, DL, MVT::i8, MVT::Other, Ptr);
399+
} else {
400+
// Do not combine the LDI instruction into the ELPM pseudo instruction,
401+
// since it may be reused by other ELPM pseudo instructions.
402+
SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);
403+
auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);
404+
ResNode = CurDAG->getMachineNode(AVR::ELPMBRdZ, DL, MVT::i8, MVT::Other,
405+
Ptr, SDValue(NP, 0));
406+
}
391407
break;
392408
case MVT::i16:
393-
ResNode = CurDAG->getMachineNode(AVR::LPMWRdZ, DL, MVT::i16, MVT::Other,
394-
Ptr, RegZ);
395-
ReplaceUses(SDValue(N, 1), SDValue(ResNode, 1));
409+
if (ProgMemBank == 0) {
410+
ResNode =
411+
CurDAG->getMachineNode(AVR::LPMWRdZ, DL, MVT::i16, MVT::Other, Ptr);
412+
} else {
413+
// Do not combine the LDI instruction into the ELPM pseudo instruction,
414+
// since LDI requires the destination register in range R16~R31.
415+
SDValue NC = CurDAG->getTargetConstant(ProgMemBank, DL, MVT::i8);
416+
auto *NP = CurDAG->getMachineNode(AVR::LDIRdK, DL, MVT::i8, NC);
417+
ResNode = CurDAG->getMachineNode(AVR::ELPMWRdZ, DL, MVT::i16,
418+
MVT::Other, Ptr, SDValue(NP, 0));
419+
}
396420
break;
397421
default:
398422
llvm_unreachable("Unsupported VT!");

llvm/lib/Target/AVR/AVRInstrInfo.td

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,21 +1702,34 @@ let mayLoad = 1, hasSideEffects = 0 in {
17021702
: F16<0b1001010111011000, (outs), (ins), "elpm", []>,
17031703
Requires<[HasELPM]>;
17041704

1705-
def ELPMRdZ : FLPMX<1, 0,
1706-
(outs GPR8
1707-
: $dst),
1708-
(ins ZREG
1709-
: $z),
1705+
def ELPMRdZ : FLPMX<1, 0, (outs GPR8:$dst), (ins ZREG:$z),
17101706
"elpm\t$dst, $z", []>,
17111707
Requires<[HasELPMX]>;
17121708

1713-
let Defs = [R31R30] in def ELPMRdZPi : FLPMX<1, 1,
1714-
(outs GPR8
1715-
: $dst),
1716-
(ins ZREG
1717-
: $z),
1718-
"elpm\t$dst, $z+", []>,
1719-
Requires<[HasELPMX]>;
1709+
let Defs = [R31R30] in {
1710+
def ELPMRdZPi : FLPMX<1, 1, (outs GPR8:$dst), (ins ZREG:$z),
1711+
"elpm\t$dst, $z+", []>,
1712+
Requires<[HasELPMX]>;
1713+
}
1714+
1715+
// These pseudos are combination of the OUT and ELPM instructions.
1716+
let Defs = [R31R30], hasSideEffects = 1 in {
1717+
def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
1718+
"elpmb\t$dst, $z, $p", []>,
1719+
Requires<[HasELPMX]>;
1720+
1721+
def ELPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z, LD8:$p),
1722+
"elpmw\t$dst, $z, $p", []>,
1723+
Requires<[HasELPMX]>;
1724+
1725+
def ELPMBRdZPi : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
1726+
"elpmb\t$dst, $z+, $p", []>,
1727+
Requires<[HasELPMX]>;
1728+
1729+
def ELPMWRdZPi : Pseudo<(outs DREGS:$dst), (ins ZREG:$z, LD8:$p),
1730+
"elpmw\t$dst, $z+, $p", []>,
1731+
Requires<[HasELPMX]>;
1732+
}
17201733
}
17211734

17221735
// Store program memory operations.

llvm/lib/Target/AVR/AVRSubtarget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class AVRSubtarget : public AVRGenSubtargetInfo {
9191
return ELFArch;
9292
}
9393

94+
/// Get I/O register address.
95+
int getIORegRAMPZ(void) const { return 0x3b; }
96+
9497
private:
9598
/// The ELF e_flags architecture.
9699
unsigned ELFArch;

0 commit comments

Comments
 (0)