bool expandASRW15Rd(Block &MBB, BlockIt MBBI);
// Common implementation of LPMWRdZ and ELPMWRdZ.
- bool expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt);
+ bool expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsELPM);
// Common implementation of LPMBRdZ and ELPMBRdZ.
- bool expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt);
+ bool expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsELPM);
};
char AVRExpandPseudo::ID = 0;
return true;
}
-bool AVRExpandPseudo::expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt) {
+bool AVRExpandPseudo::expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsELPM) {
MachineInstr &MI = *MBBI;
Register DstLoReg, DstHiReg;
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = MI.getOperand(1).getReg();
+ Register SrcLoReg, SrcHiReg;
bool SrcIsKill = MI.getOperand(1).isKill();
- unsigned OpLo = IsExt ? AVR::ELPMRdZPi : AVR::LPMRdZPi;
- unsigned OpHi = IsExt ? AVR::ELPMRdZ : AVR::LPMRdZ;
+ const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
+ bool IsLPMRn = IsELPM ? STI.hasELPMX() : STI.hasLPMX();
+
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
+ TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
// Set the I/O register RAMPZ for ELPM.
- if (IsExt) {
- const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
+ if (IsELPM) {
Register Bank = MI.getOperand(2).getReg();
// out RAMPZ, rtmp
buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(Bank);
// This is enforced by the @earlyclobber constraint.
assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same");
- // Load low byte.
- auto MIBLO = buildMI(MBB, MBBI, OpLo)
- .addReg(DstLoReg, RegState::Define)
- .addReg(SrcReg);
-
- // Load high byte.
- auto MIBHI = buildMI(MBB, MBBI, OpHi)
- .addReg(DstHiReg, RegState::Define)
- .addReg(SrcReg, getKillRegState(SrcIsKill));
+ if (IsLPMRn) {
+ unsigned OpLo = IsELPM ? AVR::ELPMRdZPi : AVR::LPMRdZPi;
+ unsigned OpHi = IsELPM ? AVR::ELPMRdZ : AVR::LPMRdZ;
+ // Load low byte.
+ auto MIBLO = buildMI(MBB, MBBI, OpLo)
+ .addReg(DstLoReg, RegState::Define)
+ .addReg(SrcReg);
+ // Load high byte.
+ auto MIBHI = buildMI(MBB, MBBI, OpHi)
+ .addReg(DstHiReg, RegState::Define)
+ .addReg(SrcReg, getKillRegState(SrcIsKill));
+ MIBLO.setMemRefs(MI.memoperands());
+ MIBHI.setMemRefs(MI.memoperands());
+ } else {
+ unsigned Opc = IsELPM ? AVR::ELPM : AVR::LPM;
+ // Load low byte, and copy to the low destination register.
+ auto MIBLO = buildMI(MBB, MBBI, Opc);
+ buildMI(MBB, MBBI, AVR::MOVRdRr)
+ .addReg(DstLoReg, RegState::Define)
+ .addReg(AVR::R0, RegState::Kill);
+ MIBLO.setMemRefs(MI.memoperands());
+ // Increase the Z register by 1.
+ if (STI.hasADDSUBIW()) {
+ // adiw r31:r30, 1
+ auto MIINC = buildMI(MBB, MBBI, AVR::ADIWRdK)
+ .addReg(SrcReg, RegState::Define)
+ .addReg(SrcReg, getKillRegState(SrcIsKill))
+ .addImm(1);
+ MIINC->getOperand(3).setIsDead();
+ } else {
+ // subi r30, 255
+ // sbci r31, 255
+ buildMI(MBB, MBBI, AVR::SUBIRdK)
+ .addReg(SrcLoReg, RegState::Define)
+ .addReg(SrcLoReg, getKillRegState(SrcIsKill))
+ .addImm(255);
+ auto MIZHI = buildMI(MBB, MBBI, AVR::SBCIRdK)
+ .addReg(SrcHiReg, RegState::Define)
+ .addReg(SrcHiReg, getKillRegState(SrcIsKill))
+ .addImm(255);
+ MIZHI->getOperand(3).setIsDead();
+ MIZHI->getOperand(4).setIsKill();
+ }
+ // Load high byte, and copy to the high destination register.
+ auto MIBHI = buildMI(MBB, MBBI, Opc);
+ buildMI(MBB, MBBI, AVR::MOVRdRr)
+ .addReg(DstHiReg, RegState::Define)
+ .addReg(AVR::R0, RegState::Kill);
+ MIBHI.setMemRefs(MI.memoperands());
+ }
- MIBLO.setMemRefs(MI.memoperands());
- MIBHI.setMemRefs(MI.memoperands());
+ // Restore the Z register if it is not killed.
+ if (!SrcIsKill) {
+ if (STI.hasADDSUBIW()) {
+ // sbiw r31:r30, 1
+ auto MIDEC = buildMI(MBB, MBBI, AVR::SBIWRdK)
+ .addReg(SrcReg, RegState::Define)
+ .addReg(SrcReg, getKillRegState(SrcIsKill))
+ .addImm(1);
+ MIDEC->getOperand(3).setIsDead();
+ } else {
+ // subi r30, 1
+ // sbci r31, 0
+ buildMI(MBB, MBBI, AVR::SUBIRdK)
+ .addReg(SrcLoReg, RegState::Define)
+ .addReg(SrcLoReg, getKillRegState(SrcIsKill))
+ .addImm(1);
+ auto MIZHI = buildMI(MBB, MBBI, AVR::SBCIRdK)
+ .addReg(SrcHiReg, RegState::Define)
+ .addReg(SrcHiReg, getKillRegState(SrcIsKill))
+ .addImm(0);
+ MIZHI->getOperand(3).setIsDead();
+ MIZHI->getOperand(4).setIsKill();
+ }
+ }
MI.eraseFromParent();
return true;
return expandLPMWELPMW(MBB, MBBI, true);
}
-bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt) {
+bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsELPM) {
MachineInstr &MI = *MBBI;
Register DstReg = MI.getOperand(0).getReg();
Register SrcReg = MI.getOperand(1).getReg();
bool SrcIsKill = MI.getOperand(1).isKill();
const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
- bool HasX = IsExt ? STI.hasELPMX() : STI.hasLPMX();
+ bool IsLPMRn = IsELPM ? STI.hasELPMX() : STI.hasLPMX();
// Set the I/O register RAMPZ for ELPM (out RAMPZ, rtmp).
- if (IsExt) {
+ if (IsELPM) {
Register BankReg = MI.getOperand(2).getReg();
buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg);
}
// Load byte.
- if (HasX) {
- unsigned Opc = IsExt ? AVR::ELPMRdZ : AVR::LPMRdZ;
+ if (IsLPMRn) {
+ unsigned Opc = IsELPM ? AVR::ELPMRdZ : AVR::LPMRdZ;
auto MILB = buildMI(MBB, MBBI, Opc)
.addReg(DstReg, RegState::Define)
.addReg(SrcReg, getKillRegState(SrcIsKill));
} else {
// For the basic ELPM/LPM instruction, its operand[0] is the implicit
// 'Z' register, and its operand[1] is the implicit 'R0' register.
- unsigned Opc = IsExt ? AVR::ELPM : AVR::LPM;
+ unsigned Opc = IsELPM ? AVR::ELPM : AVR::LPM;
auto MILB = buildMI(MBB, MBBI, Opc);
buildMI(MBB, MBBI, AVR::MOVRdRr)
.addReg(DstReg, RegState::Define)
: F16<0b1001010111001000, (outs), (ins), "lpm", []>,
Requires<[HasLPM]>;
- // This pseudo is combination of LPM and MOV instructions.
- let Defs = [R0] in
- def LPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z), "lpmb\t$dst, $z", []>,
- Requires<[HasLPM]>;
+ // These pseudo instructions are combination of the OUT and LPM instructions.
+ let Defs = [R0] in {
+ def LPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z), "lpmb\t$dst, $z", []>,
+ Requires<[HasLPM]>;
+
+ let Constraints = "@earlyclobber $dst" in
+ def LPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z), "lpmw\t$dst, $z", []>,
+ Requires<[HasLPM]>;
+ }
def LPMRdZ : FLPMX<0, 0,
(outs GPR8
"lpm\t$rd, $z+", []>,
Requires<[HasLPMX]>;
- let Constraints = "@earlyclobber $dst" in
- def LPMWRdZ : Pseudo<(outs DREGS
- : $dst),
- (ins ZREG
- : $z),
- "lpmw\t$dst, $z", []>,
- Requires<[HasLPMX]>;
-
def LPMWRdZPi : Pseudo<(outs DREGS
: $dst),
(ins ZREG
Requires<[HasELPMX]>;
}
- // This pseudo is combination of the OUT and ELPM instructions.
- let Defs = [R0] in
- def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
- "elpmb\t$dst, $z, $p", []>,
- Requires<[HasELPM]>;
+ // These pseudo instructions are combination of the OUT and ELPM instructions.
+ let Defs = [R0] in {
+ def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
+ "elpmb\t$dst, $z, $p", []>,
+ Requires<[HasELPM]>;
- // These pseudos are combination of the OUT and ELPM instructions.
- let Defs = [R31R30], hasSideEffects = 1 in {
let Constraints = "@earlyclobber $dst" in
def ELPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z, LD8:$p),
"elpmw\t$dst, $z, $p", []>,
- Requires<[HasELPMX]>;
+ Requires<[HasELPM]>;
+ }
+ // These pseudos are combination of the OUT and ELPM instructions.
+ let Defs = [R31R30], hasSideEffects = 1 in {
def ELPMBRdZPi : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
"elpmb\t$dst, $z+, $p", []>,
Requires<[HasELPMX]>;
; CHECK-NEXT: sub r24, r18
; CHECK-NEXT: sbc r25, r19
; CHECK-NEXT: ret
+;
+; NOX-LABEL: foo0:
+; NOX: ; %bb.0: ; %entry
+; NOX-NEXT: lsl r22
+; NOX-NEXT: rol r23
+; NOX-NEXT: subi r22, lo8(-(arr0))
+; NOX-NEXT: sbci r23, hi8(-(arr0))
+; NOX-NEXT: movw r30, r22
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r18, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r19, r0
+; NOX-NEXT: lsl r24
+; NOX-NEXT: rol r25
+; NOX-NEXT: subi r24, lo8(-(arr0))
+; NOX-NEXT: sbci r25, hi8(-(arr0))
+; NOX-NEXT: movw r30, r24
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r24, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r25, r0
+; NOX-NEXT: sub r24, r18
+; NOX-NEXT: sbc r25, r19
+; NOX-NEXT: ret
entry:
%arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(1)* @arr0, i16 0, i16 %a
%0 = load i16, i16 addrspace(1)* %arrayidx, align 1
; CHECK-NEXT: sub r24, r20
; CHECK-NEXT: sbc r25, r21
; CHECK-NEXT: ret
+;
+; NOX-LABEL: foo1:
+; NOX: ; %bb.0: ; %entry
+; NOX-NEXT: lsl r22
+; NOX-NEXT: rol r23
+; NOX-NEXT: subi r22, lo8(-(arr1))
+; NOX-NEXT: sbci r23, hi8(-(arr1))
+; NOX-NEXT: movw r30, r22
+; NOX-NEXT: ldi r18, 1
+; NOX-NEXT: out 59, r18
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r20, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r21, r0
+; NOX-NEXT: lsl r24
+; NOX-NEXT: rol r25
+; NOX-NEXT: subi r24, lo8(-(arr0))
+; NOX-NEXT: sbci r25, hi8(-(arr0))
+; NOX-NEXT: movw r30, r24
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r24, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r25, r0
+; NOX-NEXT: sub r24, r20
+; NOX-NEXT: sbc r25, r21
+; NOX-NEXT: ret
entry:
%arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(1)* @arr0, i16 0, i16 %a
%0 = load i16, i16 addrspace(1)* %arrayidx, align 1
; CHECK-NEXT: sub r24, r18
; CHECK-NEXT: sbc r25, r19
; CHECK-NEXT: ret
+;
+; NOX-LABEL: foo2:
+; NOX: ; %bb.0: ; %entry
+; NOX-NEXT: lsl r24
+; NOX-NEXT: rol r25
+; NOX-NEXT: subi r24, lo8(-(arr2))
+; NOX-NEXT: sbci r25, hi8(-(arr2))
+; NOX-NEXT: movw r30, r24
+; NOX-NEXT: ldi r18, 2
+; NOX-NEXT: out 59, r18
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r24, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r25, r0
+; NOX-NEXT: lsl r22
+; NOX-NEXT: rol r23
+; NOX-NEXT: subi r22, lo8(-(arr0))
+; NOX-NEXT: sbci r23, hi8(-(arr0))
+; NOX-NEXT: movw r30, r22
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r18, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: lpm
+; NOX-NEXT: mov r19, r0
+; NOX-NEXT: sub r24, r18
+; NOX-NEXT: sbc r25, r19
+; NOX-NEXT: ret
entry:
%arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(3)* @arr2, i16 0, i16 %a
%0 = load i16, i16 addrspace(3)* %arrayidx, align 1
; CHECK-NEXT: sub r24, r20
; CHECK-NEXT: sbc r25, r21
; CHECK-NEXT: ret
+;
+; NOX-LABEL: foo3:
+; NOX: ; %bb.0: ; %entry
+; NOX-NEXT: lsl r22
+; NOX-NEXT: rol r23
+; NOX-NEXT: subi r22, lo8(-(arr1))
+; NOX-NEXT: sbci r23, hi8(-(arr1))
+; NOX-NEXT: movw r30, r22
+; NOX-NEXT: ldi r18, 1
+; NOX-NEXT: out 59, r18
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r20, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r21, r0
+; NOX-NEXT: lsl r24
+; NOX-NEXT: rol r25
+; NOX-NEXT: subi r24, lo8(-(arr2))
+; NOX-NEXT: sbci r25, hi8(-(arr2))
+; NOX-NEXT: movw r30, r24
+; NOX-NEXT: ldi r18, 2
+; NOX-NEXT: out 59, r18
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r24, r0
+; NOX-NEXT: adiw r30, 1
+; NOX-NEXT: elpm
+; NOX-NEXT: mov r25, r0
+; NOX-NEXT: sub r24, r20
+; NOX-NEXT: sbc r25, r21
+; NOX-NEXT: ret
entry:
%arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(3)* @arr2, i16 0, i16 %a
%0 = load i16, i16 addrspace(3)* %arrayidx, align 1
-# RUN: llc -mcpu=atmega1284p -start-before=greedy %s -o - | FileCheck %s
+# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+elpm -mattr=+lpmx -mattr=+elpmx \
+# RUN: -mattr=+movw -start-before=greedy %s -o - | FileCheck %s
+# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+elpm -mattr=-lpmx -mattr=-elpmx \
+# RUN: -mattr=+addsubiw -mattr=+movw -start-before=greedy %s -o - \
+# RUN: | FileCheck --check-prefix=NOX %s
+# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+elpm -mattr=-lpmx -mattr=-elpmx \
+# RUN: -mattr=-addsubiw -mattr=+movw -start-before=greedy %s -o - \
+# RUN: | FileCheck --check-prefix=NOADIWNOX %s
# This test checks the expansion of the 16-bit ELPM pseudo instruction and that
# the register allocator won't use R31R30 as an output register (which would
entry:
ret void
}
+ define void @test_elpmwrdz_2() {
+ entry:
+ ret void
+ }
...
---
; CHECK-NEXT: elpm r25, Z
; CHECK-NEXT: movw r30, r24
+ ; NOX-LABEL: test_elpmwrdz
+ ; NOX: ; %bb.0:
+ ; NOX-NEXT: ldi r18, 1
+ ; NOX-NEXT: out
+ ; NOX-NEXT: elpm
+ ; NOX-NEXT: mov r24, r0
+ ; NOX-NEXT: adiw r30, 1
+ ; NOX-NEXT: elpm
+ ; NOX-NEXT: mov r25, r0
+ ; NOX-NEXT: movw r30, r24
+
+ ; NOADIWNOX-LABEL: test_elpmwrdz
+ ; NOADIWNOX: ; %bb.0:
+ ; NOADIWNOX-NEXT: ldi r18, 1
+ ; NOADIWNOX-NEXT: out
+ ; NOADIWNOX-NEXT: elpm
+ ; NOADIWNOX-NEXT: mov r24, r0
+ ; NOADIWNOX-NEXT: subi r30, 255
+ ; NOADIWNOX-NEXT: sbci r31, 255
+ ; NOADIWNOX-NEXT: elpm
+ ; NOADIWNOX-NEXT: mov r25, r0
+ ; NOADIWNOX-NEXT: movw r30, r24
+
%1:zreg = COPY killed $r31r30
%2:ld8 = LDIRdK 1
- %3:dregs = ELPMWRdZ %1, %2, implicit-def dead $r31r30
+ %3:dregs = ELPMWRdZ %1, %2, implicit-def dead $r0
$r31r30 = COPY %3
RET implicit $r31r30
...
+
+---
+name: test_elpmwrdz_2
+tracksRegLiveness: true
+body: |
+ bb.0.entry:
+ liveins: $r31r30
+
+ ; CHECK-LABEL: test_elpmwrdz_2
+ ; CHECK: ; %bb.0:
+ ; CHECK-NEXT: ldi r24, 1
+ ; CHECK-NEXT: out 59, r24
+ ; CHECK-NEXT: elpm r18, Z+
+ ; CHECK-NEXT: elpm r19, Z
+ ; CHECK-NEXT: sbiw r30, 1
+ ; CHECK-NEXT: ret
+
+ ; NOX-LABEL: test_elpmwrdz_2
+ ; NOX: ; %bb.0:
+ ; NOX-NEXT: ldi r24, 1
+ ; NOX-NEXT: out 59, r24
+ ; NOX-NEXT: elpm
+ ; NOX-NEXT: mov r18, r0
+ ; NOX-NEXT: adiw r30, 1
+ ; NOX-NEXT: elpm
+ ; NOX-NEXT: mov r19, r0
+ ; NOX-NEXT: sbiw r30, 1
+ ; NOX-NEXT: ret
+
+ ; NOADIWNOX-LABEL: test_elpmwrdz_2
+ ; NOADIWNOX: ; %bb.0:
+ ; NOADIWNOX-NEXT: ldi r24, 1
+ ; NOADIWNOX-NEXT: out 59, r24
+ ; NOADIWNOX-NEXT: elpm
+ ; NOADIWNOX-NEXT: mov r18, r0
+ ; NOADIWNOX-NEXT: subi r30, 255
+ ; NOADIWNOX-NEXT: sbci r31, 255
+ ; NOADIWNOX-NEXT: elpm
+ ; NOADIWNOX-NEXT: mov r19, r0
+ ; NOADIWNOX-NEXT: subi r30, 1
+ ; NOADIWNOX-NEXT: sbci r31, 0
+
+ %1:zreg = COPY $r31r30
+ %2:ld8 = LDIRdK 1
+ %3:dregs = ELPMWRdZ %1, %2, implicit-def dead $r0
+ RET implicit $r31r30
+...