diff --git a/llvm/lib/Target/AVR/AVRDevices.td b/llvm/lib/Target/AVR/AVRDevices.td --- a/llvm/lib/Target/AVR/AVRDevices.td +++ b/llvm/lib/Target/AVR/AVRDevices.td @@ -125,6 +125,12 @@ "The device has Tiny core specific " "instruction encodings">; +// When writing a 16-bit port or storing a 16-bit word, do the low byte first. +def FeatureLowByteFirst + : SubtargetFeature<"lowbytefirst", "m_hasLowByteFirst", "true", + "Do the low byte first when writing a 16-bit port or " + "storing a 16-bit word">; + // The device has CPU registers mapped in data address space def FeatureMMR : SubtargetFeature<"memmappedregs", "m_hasMemMappedGPR", "true", "The device has CPU registers " @@ -200,14 +206,15 @@ [FamilyAVR0, FeatureLPM, FeaturePROGMEM, FeatureIJMPCALL, FeatureADDSUBIW, FeatureSRAM, FeatureJMPCALL, FeatureMultiplication, FeatureMOVW, FeatureLPMX, - FeatureBREAK]>; + FeatureBREAK, FeatureLowByteFirst]>; def FamilyXMEGA : Family<"xmega", [FamilyAVR0, FeatureLPM, FeaturePROGMEM, FeatureIJMPCALL, FeatureADDSUBIW, FeatureSRAM, FeatureJMPCALL, FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM, FeatureBREAK, FeatureEIJMPCALL, - FeatureSPMX, FeatureDES, FeatureELPM, FeatureELPMX]>; + FeatureSPMX, FeatureDES, FeatureELPM, FeatureELPMX, + FeatureLowByteFirst]>; def FamilyXMEGAU : Family<"xmegau", [FamilyXMEGA, FeatureRMW]>; diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -967,18 +967,15 @@ template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); MachineInstr &MI = *MBBI; Register SrcLoReg, SrcHiReg; Register SrcReg = MI.getOperand(1).getReg(); bool SrcIsKill = MI.getOperand(1).isKill(); - unsigned OpLo = AVR::STSKRr; - unsigned OpHi = AVR::STSKRr; TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); - // Write the high byte first in case this address belongs to a special - // I/O address with a special temporary register. - auto MIBHI = buildMI(MBB, MBBI, OpHi); - auto MIBLO = buildMI(MBB, MBBI, OpLo); + auto MIB0 = buildMI(MBB, MBBI, AVR::STSKRr); + auto MIB1 = buildMI(MBB, MBBI, AVR::STSKRr); switch (MI.getOperand(0).getType()) { case MachineOperand::MO_GlobalAddress: { @@ -986,26 +983,50 @@ int64_t Offs = MI.getOperand(0).getOffset(); unsigned TF = MI.getOperand(0).getTargetFlags(); - MIBLO.addGlobalAddress(GV, Offs, TF); - MIBHI.addGlobalAddress(GV, Offs + 1, TF); + if (STI.hasLowByteFirst()) { + // Write the low byte first for XMEGA devices. + MIB0.addGlobalAddress(GV, Offs, TF); + MIB1.addGlobalAddress(GV, Offs + 1, TF); + } else { + // Write the high byte first for traditional devices. + MIB0.addGlobalAddress(GV, Offs + 1, TF); + MIB1.addGlobalAddress(GV, Offs, TF); + } + break; } case MachineOperand::MO_Immediate: { unsigned Imm = MI.getOperand(0).getImm(); - MIBLO.addImm(Imm); - MIBHI.addImm(Imm + 1); + if (STI.hasLowByteFirst()) { + // Write the low byte first for XMEGA devices. + MIB0.addImm(Imm); + MIB1.addImm(Imm + 1); + } else { + // Write the high byte first for traditional devices. + MIB0.addImm(Imm + 1); + MIB1.addImm(Imm); + } + break; } default: llvm_unreachable("Unknown operand type!"); } - MIBLO.addReg(SrcLoReg, getKillRegState(SrcIsKill)); - MIBHI.addReg(SrcHiReg, getKillRegState(SrcIsKill)); - - MIBLO.setMemRefs(MI.memoperands()); - MIBHI.setMemRefs(MI.memoperands()); + if (STI.hasLowByteFirst()) { + // Write the low byte first for XMEGA devices. + MIB0.addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + MIB1.addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + } else { + // Write the high byte first for traditional devices. + MIB0.addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + MIB1.addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + } MI.eraseFromParent(); return true; @@ -1257,27 +1278,28 @@ template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); MachineInstr &MI = *MBBI; Register SrcLoReg, SrcHiReg; unsigned Imm = MI.getOperand(0).getImm(); Register SrcReg = MI.getOperand(1).getReg(); bool SrcIsKill = MI.getOperand(1).isKill(); - unsigned OpLo = AVR::OUTARr; - unsigned OpHi = AVR::OUTARr; TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); // Since we add 1 to the Imm value for the high byte below, and 63 is the // highest Imm value allowed for the instruction, 62 is the limit here. assert(Imm <= 62 && "Address is out of range"); - // 16 bit I/O writes need the high byte first - auto MIBHI = buildMI(MBB, MBBI, OpHi) - .addImm(Imm + 1) - .addReg(SrcHiReg, getKillRegState(SrcIsKill)); - - auto MIBLO = buildMI(MBB, MBBI, OpLo) - .addImm(Imm) - .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + // 16 bit I/O writes need the high byte first on normal AVR devices, + // and in reverse order for the XMEGA/XMEGA3/XMEGAU families. + auto MIBHI = buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(STI.hasLowByteFirst() ? Imm : Imm + 1) + .addReg(STI.hasLowByteFirst() ? SrcLoReg : SrcHiReg, + getKillRegState(SrcIsKill)); + auto MIBLO = buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(STI.hasLowByteFirst() ? Imm + 1 : Imm) + .addReg(STI.hasLowByteFirst() ? SrcHiReg : SrcLoReg, + getKillRegState(SrcIsKill)); MIBLO.setMemRefs(MI.memoperands()); MIBHI.setMemRefs(MI.memoperands()); diff --git a/llvm/lib/Target/AVR/AVRSubtarget.h b/llvm/lib/Target/AVR/AVRSubtarget.h --- a/llvm/lib/Target/AVR/AVRSubtarget.h +++ b/llvm/lib/Target/AVR/AVRSubtarget.h @@ -82,6 +82,7 @@ bool hasBREAK() const { return m_hasBREAK; } bool hasTinyEncoding() const { return m_hasTinyEncoding; } bool hasMemMappedGPR() const { return m_hasMemMappedGPR; } + bool hasLowByteFirst() const { return m_hasLowByteFirst; } uint8_t getIORegisterOffset() const { return hasMemMappedGPR() ? 0x20 : 0x0; } @@ -136,6 +137,7 @@ bool m_supportsMultiplication = false; bool m_hasBREAK = false; bool m_hasTinyEncoding = false; + bool m_hasLowByteFirst = false; bool m_hasMemMappedGPR = false; // Dummy member, used by FeatureSet's. We cannot have a SubtargetFeature with diff --git a/llvm/test/CodeGen/AVR/pseudo/OUTWARr.mir b/llvm/test/CodeGen/AVR/pseudo/OUTWARr.mir --- a/llvm/test/CodeGen/AVR/pseudo/OUTWARr.mir +++ b/llvm/test/CodeGen/AVR/pseudo/OUTWARr.mir @@ -1,4 +1,13 @@ -# RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mtriple=avr -mcpu=attiny11 %s -o - \ +# RUN: | FileCheck %s +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mtriple=avr -mcpu=atmega328 %s -o - \ +# RUN: | FileCheck %s +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mtriple=avr -mcpu=attiny817 %s -o - \ +# RUN: | FileCheck --check-prefix=XMEGA %s +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mtriple=avr -mcpu=atxmega64a1 %s -o - \ +# RUN: | FileCheck --check-prefix=XMEGA %s +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mtriple=avr -mcpu=atxmega256a3u %s -o - \ +# RUN: | FileCheck --check-prefix=XMEGA %s --- | target triple = "avr--" @@ -15,9 +24,12 @@ liveins: $r15r14 ; CHECK-LABEL: test + ; CHECK: OUTARr 32, $r15 + ; CHECK-NEXT: OUTARr 31, $r14 - ; CHECK: OUTARr 32, $r15 - ; CHECK-NEXT: OUTARr 31, $r14 + ; XMEGA-LABEL: test + ; XMEGA: OUTARr 31, $r14 + ; XMEGA-NEXT: OUTARr 32, $r15 OUTWARr 31, $r15r14 ... diff --git a/llvm/test/CodeGen/AVR/pseudo/STSWKRr.mir b/llvm/test/CodeGen/AVR/pseudo/STSWKRr.mir --- a/llvm/test/CodeGen/AVR/pseudo/STSWKRr.mir +++ b/llvm/test/CodeGen/AVR/pseudo/STSWKRr.mir @@ -1,4 +1,6 @@ # RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mcpu=atxmega64a1 %s -o - \ +# RUN: | FileCheck --check-prefix=XMEGA %s # This test checks the expansion of the 16-bit STSWRdK pseudo instruction. @@ -17,9 +19,12 @@ liveins: $r31r30 ; CHECK-LABEL: test_stswkrr + ; CHECK: STSKRr 2560, $r31 + ; CHECK-NEXT: STSKRr 2559, $r30 - ; CHECK: STSKRr 2560, $r31 - ; CHECK-NEXT: STSKRr 2559, $r30 + ; XMEGA-LABEL: test_stswkrr + ; XMEGA: STSKRr 2559, $r30 + ; XMEGA-NEXT: STSKRr 2560, $r31 STSWKRr 2559, $r31r30 ...