diff --git a/lld/test/ELF/avr-flags.s b/lld/test/ELF/avr-flags.s index 69e08cd4dab9e..df449af1c2468 100644 --- a/lld/test/ELF/avr-flags.s +++ b/lld/test/ELF/avr-flags.s @@ -11,7 +11,9 @@ ; RUN: not ld.lld %t-v5 %t-xmega3 -o /dev/null 2>&1 | FileCheck --check-prefix ERR %s ; ERR: error: {{.*}}: cannot link object files with incompatible target ISA -; V5: Flags [ (0x5) +; V5: Flags [ (0x85) ; V5: EF_AVR_ARCH_AVR5 (0x5) -; XMEGA3: Flags [ (0x67) +; V5: EF_AVR_LINKRELAX_PREPARED (0x80) +; XMEGA3: Flags [ (0xE7) ; XMEGA3: EF_AVR_ARCH_XMEGA3 (0x67) +; XMEGA3: EF_AVR_LINKRELAX_PREPARED (0x80) diff --git a/lld/test/ELF/linkerscript/avr5.test b/lld/test/ELF/linkerscript/avr5.test index 6c65a74c6c11f..5e52c945bae86 100644 --- a/lld/test/ELF/linkerscript/avr5.test +++ b/lld/test/ELF/linkerscript/avr5.test @@ -16,7 +16,7 @@ # RUN: llvm-readelf --headers %t/avr5b.out | FileCheck %s --check-prefix=HEAD # HEAD: Atmel AVR 8-bit microcontroller -# HEAD: 0x5, EF_AVR_ARCH_AVR5 +# HEAD: 0x85, EF_AVR_ARCH_AVR5, relaxable # HEAD: Name Type Address Off Size # HEAD-NEXT: NULL 00000000 000000 000000 diff --git a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp index c625290751080..0e6ec0268f857 100644 --- a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp +++ b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp @@ -101,56 +101,51 @@ bool AVRAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, const char *ExtraCode, raw_ostream &O) { // Default asm printer can only deal with some extra codes, // so try it first. - bool Error = AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O); - - if (Error && ExtraCode && ExtraCode[0]) { - if (ExtraCode[1] != 0) - return true; // Unknown modifier. + if (!AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O)) + return false; - if (ExtraCode[0] >= 'A' && ExtraCode[0] <= 'Z') { - const MachineOperand &RegOp = MI->getOperand(OpNum); + const MachineOperand &MO = MI->getOperand(OpNum); - assert(RegOp.isReg() && "Operand must be a register when you're" - "using 'A'..'Z' operand extracodes."); - Register Reg = RegOp.getReg(); + if (ExtraCode && ExtraCode[0]) { + // Unknown extra code. + if (ExtraCode[1] != 0 || ExtraCode[0] < 'A' || ExtraCode[0] > 'Z') + return true; - unsigned ByteNumber = ExtraCode[0] - 'A'; + // Operand must be a register when using 'A' ~ 'Z' extra code. + if (!MO.isReg()) + return true; - unsigned OpFlags = MI->getOperand(OpNum - 1).getImm(); - unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags); - (void)NumOpRegs; + Register Reg = MO.getReg(); - const AVRSubtarget &STI = MF->getSubtarget(); - const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); + unsigned ByteNumber = ExtraCode[0] - 'A'; + unsigned OpFlags = MI->getOperand(OpNum - 1).getImm(); + unsigned NumOpRegs = InlineAsm::getNumOperandRegisters(OpFlags); - const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg); - unsigned BytesPerReg = TRI.getRegSizeInBits(*RC) / 8; - assert(BytesPerReg <= 2 && "Only 8 and 16 bit regs are supported."); + const AVRSubtarget &STI = MF->getSubtarget(); + const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); - unsigned RegIdx = ByteNumber / BytesPerReg; - if (RegIdx >= NumOpRegs) - return true; - Reg = MI->getOperand(OpNum + RegIdx).getReg(); + const TargetRegisterClass *RC = TRI.getMinimalPhysRegClass(Reg); + unsigned BytesPerReg = TRI.getRegSizeInBits(*RC) / 8; + assert(BytesPerReg <= 2 && "Only 8 and 16 bit regs are supported."); - if (BytesPerReg == 2) { - Reg = TRI.getSubReg(Reg, ByteNumber % BytesPerReg ? AVR::sub_hi - : AVR::sub_lo); - } + unsigned RegIdx = ByteNumber / BytesPerReg; + if (RegIdx >= NumOpRegs) + return true; + Reg = MI->getOperand(OpNum + RegIdx).getReg(); - O << AVRInstPrinter::getPrettyRegisterName(Reg, MRI); - return false; + if (BytesPerReg == 2) { + Reg = TRI.getSubReg(Reg, + ByteNumber % BytesPerReg ? AVR::sub_hi : AVR::sub_lo); } - } - // Print global symbols. - const auto &MO = MI->getOperand(OpNum); - if (Error && MO.getType() == MachineOperand::MO_GlobalAddress) { - PrintSymbolOperand(MO, O); + O << AVRInstPrinter::getPrettyRegisterName(Reg, MRI); return false; } - if (Error) - printOperand(MI, OpNum, O); + if (MO.getType() == MachineOperand::MO_GlobalAddress) + PrintSymbolOperand(MO, O); // Print global symbols. + else + printOperand(MI, OpNum, O); // Fallback to ordinary cases. return false; } @@ -259,9 +254,9 @@ bool AVRAsmPrinter::doFinalization(Module &M) { auto *Section = cast(TLOF.SectionForGlobal(&GO, TM)); if (Section->getName().startswith(".data")) NeedsCopyData = true; - else if (Section->getName().startswith(".rodata") && SubTM->hasPROGMEM()) - // AVRs that have a separate PROGMEM (that's most AVRs) store .rodata - // sections in RAM. + else if (Section->getName().startswith(".rodata") && SubTM->hasLPM()) + // AVRs that have a separate program memory (that's most AVRs) store + // .rodata sections in RAM. NeedsCopyData = true; else if (Section->getName().startswith(".bss")) NeedsClearBSS = true; diff --git a/llvm/lib/Target/AVR/AVRDevices.td b/llvm/lib/Target/AVR/AVRDevices.td index f2c8a2e7a71e7..f6b36dba7733b 100644 --- a/llvm/lib/Target/AVR/AVRDevices.td +++ b/llvm/lib/Target/AVR/AVRDevices.td @@ -65,11 +65,6 @@ def FeatureMOVW : SubtargetFeature<"movw", "m_hasMOVW", "true", "The device supports the 16-bit MOVW " "instruction">; -// The device has a separate flash namespace that must be accessed using special -// instructions like lpm. -def FeaturePROGMEM : SubtargetFeature<"progmem", "m_hasPROGMEM", "true", - "The device has a separate flash namespace">; - // The device supports the `LPM` instruction, with implied destination being r0. def FeatureLPM : SubtargetFeature<"lpm", "m_hasLPM", "true", "The device supports the `LPM` instruction">; @@ -125,6 +120,12 @@ def FeatureTinyEncoding "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 " @@ -161,7 +162,7 @@ def ELFArchXMEGA7 : ELFArch<"EF_AVR_ARCH_XMEGA7">; // device should have. def FamilyAVR0 : Family<"avr0", []>; -def FamilyAVR1 : Family<"avr1", [FamilyAVR0, FeatureLPM, FeaturePROGMEM, FeatureMMR]>; +def FamilyAVR1 : Family<"avr1", [FamilyAVR0, FeatureLPM, FeatureMMR]>; def FamilyAVR2 : Family<"avr2", @@ -197,17 +198,18 @@ def FamilyTiny FeatureSmallStack]>; def FamilyXMEGA3 : Family<"xmega3", - [FamilyAVR0, FeatureLPM, FeaturePROGMEM, FeatureIJMPCALL, + [FamilyAVR0, FeatureLPM, FeatureIJMPCALL, FeatureADDSUBIW, FeatureSRAM, FeatureJMPCALL, FeatureMultiplication, FeatureMOVW, FeatureLPMX, - FeatureBREAK]>; + FeatureBREAK, FeatureLowByteFirst]>; def FamilyXMEGA : Family<"xmega", - [FamilyAVR0, FeatureLPM, FeaturePROGMEM, FeatureIJMPCALL, + [FamilyAVR0, FeatureLPM, 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 index 2c97dea0bce03..f257ccea6c50a 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -70,6 +70,7 @@ class AVRExpandPseudo : public MachineFunctionPass { bool expandLogic(unsigned Op, Block &MBB, BlockIt MBBI); bool expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI); bool isLogicImmOpRedundant(unsigned Op, unsigned ImmVal) const; + bool isLogicRegOpUndef(unsigned Op, unsigned ImmVal) const; template bool expandAtomic(Block &MBB, BlockIt MBBI, Func f); @@ -97,7 +98,11 @@ class AVRExpandPseudo : public MachineFunctionPass { 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 IsELPM); + // Common implementation of ROLBRdR1 and ROLBRdR17. + bool expandROLBRd(Block &MBB, BlockIt MBBI); }; char AVRExpandPseudo::ID = 0; @@ -224,6 +229,18 @@ bool AVRExpandPseudo::isLogicImmOpRedundant(unsigned Op, return false; } +bool AVRExpandPseudo::isLogicRegOpUndef(unsigned Op, unsigned ImmVal) const { + // ANDI Rd, 0x00 clears all input bits. + if (Op == AVR::ANDIRdK && ImmVal == 0x00) + return true; + + // ORI Rd, 0xff sets all input bits. + if (Op == AVR::ORIRdK && ImmVal == 0xff) + return true; + + return false; +} + bool AVRExpandPseudo::expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; Register DstLoReg, DstHiReg; @@ -245,6 +262,9 @@ bool AVRExpandPseudo::expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI) { // SREG is always implicitly dead MIBLO->getOperand(3).setIsDead(); + + if (isLogicRegOpUndef(Op, Lo8)) + MIBLO->getOperand(1).setIsUndef(true); } if (!isLogicImmOpRedundant(Op, Hi8)) { @@ -256,6 +276,9 @@ bool AVRExpandPseudo::expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI) { if (ImpIsDead) MIBHI->getOperand(3).setIsDead(); + + if (isLogicRegOpUndef(Op, Hi8)) + MIBHI->getOperand(1).setIsUndef(true); } MI.eraseFromParent(); @@ -810,19 +833,21 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { 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(); + 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(); + if (IsELPM) { Register Bank = MI.getOperand(2).getReg(); // out RAMPZ, rtmp buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(Bank); @@ -831,18 +856,81 @@ bool AVRExpandPseudo::expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt) { // 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; @@ -858,29 +946,52 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandLPMWELPMW(MBB, MBBI, true); } -template <> -bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { +bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsELPM) { MachineInstr &MI = *MBBI; Register DstReg = MI.getOperand(0).getReg(); Register SrcReg = MI.getOperand(1).getReg(); - Register BankReg = MI.getOperand(2).getReg(); bool SrcIsKill = MI.getOperand(1).isKill(); const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); + bool IsLPMRn = IsELPM ? STI.hasELPMX() : STI.hasLPMX(); // Set the I/O register RAMPZ for ELPM (out RAMPZ, rtmp). - buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg); + if (IsELPM) { + Register BankReg = MI.getOperand(2).getReg(); + buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg); + } // Load byte. - auto MILB = buildMI(MBB, MBBI, AVR::ELPMRdZ) - .addReg(DstReg, RegState::Define) - .addReg(SrcReg, getKillRegState(SrcIsKill)); - - MILB.setMemRefs(MI.memoperands()); + if (IsLPMRn) { + unsigned Opc = IsELPM ? AVR::ELPMRdZ : AVR::LPMRdZ; + auto MILB = buildMI(MBB, MBBI, Opc) + .addReg(DstReg, RegState::Define) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + MILB.setMemRefs(MI.memoperands()); + } 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 = IsELPM ? AVR::ELPM : AVR::LPM; + auto MILB = buildMI(MBB, MBBI, Opc); + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstReg, RegState::Define) + .addReg(AVR::R0, RegState::Kill); + MILB.setMemRefs(MI.memoperands()); + } MI.eraseFromParent(); return true; } +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandLPMBELPMB(MBB, MBBI, true); +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandLPMBELPMB(MBB, MBBI, false); +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { llvm_unreachable("16-bit LPMPi is unimplemented"); @@ -967,18 +1078,15 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { 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 +1094,50 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { 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; @@ -1036,16 +1168,27 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { } else { Register SrcLoReg, SrcHiReg; TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); - buildMI(MBB, MBBI, AVR::STPtrRr) - .addReg(DstReg, getUndefRegState(DstIsUndef)) - .addReg(SrcLoReg, getKillRegState(SrcIsKill)) - .setMemRefs(MI.memoperands()); - - buildMI(MBB, MBBI, AVR::STDPtrQRr) - .addReg(DstReg, getUndefRegState(DstIsUndef)) - .addImm(1) - .addReg(SrcHiReg, getKillRegState(SrcIsKill)) - .setMemRefs(MI.memoperands()); + if (STI.hasLowByteFirst()) { + buildMI(MBB, MBBI, AVR::STPtrRr) + .addReg(DstReg, getUndefRegState(DstIsUndef)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + buildMI(MBB, MBBI, AVR::STDPtrQRr) + .addReg(DstReg, getUndefRegState(DstIsUndef)) + .addImm(1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + } else { + buildMI(MBB, MBBI, AVR::STDPtrQRr) + .addReg(DstReg, getUndefRegState(DstIsUndef)) + .addImm(1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + buildMI(MBB, MBBI, AVR::STPtrRr) + .addReg(DstReg, getUndefRegState(DstIsUndef)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + } } MI.eraseFromParent(); @@ -1162,23 +1305,32 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { .addImm(Imm + 2); } } else { - unsigned OpLo = AVR::STDPtrQRr; - unsigned OpHi = AVR::STDPtrQRr; Register SrcLoReg, SrcHiReg; TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); - auto MIBLO = buildMI(MBB, MBBI, OpLo) - .addReg(DstReg) - .addImm(Imm) - .addReg(SrcLoReg, getKillRegState(SrcIsKill)); - - auto MIBHI = buildMI(MBB, MBBI, OpHi) - .addReg(DstReg, getKillRegState(DstIsKill)) - .addImm(Imm + 1) - .addReg(SrcHiReg, getKillRegState(SrcIsKill)); - - MIBLO.setMemRefs(MI.memoperands()); - MIBHI.setMemRefs(MI.memoperands()); + if (STI.hasLowByteFirst()) { + buildMI(MBB, MBBI, AVR::STDPtrQRr) + .addReg(DstReg) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + buildMI(MBB, MBBI, AVR::STDPtrQRr) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + } else { + buildMI(MBB, MBBI, AVR::STDPtrQRr) + .addReg(DstReg) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + buildMI(MBB, MBBI, AVR::STDPtrQRr) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMemRefs(MI.memoperands()); + } } MI.eraseFromParent(); @@ -1257,27 +1409,28 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { 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()); @@ -1328,8 +1481,7 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return true; } -template <> -bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { +bool AVRExpandPseudo::expandROLBRd(Block &MBB, BlockIt MBBI) { // In AVR, the rotate instructions behave quite unintuitively. They rotate // bits through the carry bit in SREG, effectively rotating over 9 bits, // instead of 8. This is useful when we are dealing with numbers over @@ -1339,7 +1491,7 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; unsigned OpShift, OpCarry; Register DstReg = MI.getOperand(0).getReg(); - Register ZeroReg = MI.getOperand(2).getReg(); + Register ZeroReg = MI.getOperand(3).getReg(); bool DstIsDead = MI.getOperand(0).isDead(); bool DstIsKill = MI.getOperand(1).isKill(); OpShift = AVR::ADDRdRr; @@ -1367,6 +1519,16 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return true; } +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandROLBRd(MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandROLBRd(MBB, MBBI); +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { // In AVR, the rotate instructions behave quite unintuitively. They rotate @@ -2428,6 +2590,7 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) { EXPAND(AVR::LDWRdPtrPd); case AVR::LDDWRdYQ: //: FIXME: remove this once PR13375 gets fixed EXPAND(AVR::LDDWRdPtrQ); + EXPAND(AVR::LPMBRdZ); EXPAND(AVR::LPMWRdZ); EXPAND(AVR::LPMWRdZPi); EXPAND(AVR::ELPMBRdZ); @@ -2450,7 +2613,8 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) { EXPAND(AVR::OUTWARr); EXPAND(AVR::PUSHWRr); EXPAND(AVR::POPWRd); - EXPAND(AVR::ROLBRd); + EXPAND(AVR::ROLBRdR1); + EXPAND(AVR::ROLBRdR17); EXPAND(AVR::RORBRd); EXPAND(AVR::LSLWRd); EXPAND(AVR::LSRWRd); diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp index 904cdf8420eb0..aff2d5ed7b121 100644 --- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp +++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp @@ -260,6 +260,16 @@ bool AVRFrameLowering::spillCalleeSavedRegisters( Register Reg = I.getReg(); bool IsNotLiveIn = !MBB.isLiveIn(Reg); + // Check if Reg is a sub register of a 16-bit livein register, and then + // add it to the livein list. + if (IsNotLiveIn) + for (const auto &LiveIn : MBB.liveins()) + if (STI.getRegisterInfo()->isSubRegister(LiveIn.PhysReg, Reg)) { + IsNotLiveIn = false; + MBB.addLiveIn(Reg); + break; + } + assert(TRI->getRegSizeInBits(*TRI->getMinimalPhysRegClass(Reg)) == 8 && "Invalid register size"); diff --git a/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp b/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp index 5511d53dfa312..ed9fd6b04fb9a 100644 --- a/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp +++ b/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp @@ -366,6 +366,8 @@ template <> bool AVRDAGToDAGISel::select(SDNode *N) { int ProgMemBank = AVR::getProgramMemoryBank(LD); if (ProgMemBank < 0 || ProgMemBank > 5) report_fatal_error("unexpected program memory bank"); + if (ProgMemBank > 0 && !Subtarget->hasELPM()) + report_fatal_error("unexpected program memory bank"); // This is a flash memory load, move the pointer into R31R30 and emit // the lpm instruction. @@ -398,8 +400,9 @@ template <> bool AVRDAGToDAGISel::select(SDNode *N) { switch (VT.SimpleTy) { case MVT::i8: if (ProgMemBank == 0) { + unsigned Opc = Subtarget->hasLPMX() ? AVR::LPMRdZ : AVR::LPMBRdZ; ResNode = - CurDAG->getMachineNode(AVR::LPMRdZ, DL, MVT::i8, MVT::Other, Ptr); + CurDAG->getMachineNode(Opc, DL, MVT::i8, MVT::Other, Ptr); } else { // Do not combine the LDI instruction into the ELPM pseudo instruction, // since it may be reused by other ELPM pseudo instructions. diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index ee2c489179714..ed1f94080a6a9 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -427,6 +427,33 @@ SDValue AVRTargetLowering::LowerShifts(SDValue Op, SelectionDAG &DAG) const { Victim = DAG.getNode(AVRISD::ASRBN, dl, VT, Victim, DAG.getConstant(7, dl, VT)); ShiftAmount = 0; + } else if (Op.getOpcode() == ISD::ROTL && ShiftAmount == 3) { + // Optimize left rotation 3 bits to swap then right rotation 1 bit. + Victim = DAG.getNode(AVRISD::SWAP, dl, VT, Victim); + Victim = + DAG.getNode(AVRISD::ROR, dl, VT, Victim, DAG.getConstant(1, dl, VT)); + ShiftAmount = 0; + } else if (Op.getOpcode() == ISD::ROTR && ShiftAmount == 3) { + // Optimize right rotation 3 bits to swap then left rotation 1 bit. + Victim = DAG.getNode(AVRISD::SWAP, dl, VT, Victim); + Victim = + DAG.getNode(AVRISD::ROL, dl, VT, Victim, DAG.getConstant(1, dl, VT)); + ShiftAmount = 0; + } else if (Op.getOpcode() == ISD::ROTL && ShiftAmount == 7) { + // Optimize left rotation 7 bits to right rotation 1 bit. + Victim = + DAG.getNode(AVRISD::ROR, dl, VT, Victim, DAG.getConstant(1, dl, VT)); + ShiftAmount = 0; + } else if (Op.getOpcode() == ISD::ROTR && ShiftAmount == 7) { + // Optimize right rotation 7 bits to left rotation 1 bit. + Victim = + DAG.getNode(AVRISD::ROL, dl, VT, Victim, DAG.getConstant(1, dl, VT)); + ShiftAmount = 0; + } else if ((Op.getOpcode() == ISD::ROTR || Op.getOpcode() == ISD::ROTL) && + ShiftAmount >= 4) { + // Optimize left/right rotation with the SWAP instruction. + Victim = DAG.getNode(AVRISD::SWAP, dl, VT, Victim); + ShiftAmount -= 4; } } else if (VT.getSizeInBits() == 16) { if (Op.getOpcode() == ISD::SRA) @@ -634,11 +661,35 @@ SDValue AVRTargetLowering::getAVRCmp(SDValue LHS, SDValue RHS, SDValue Cmp; if (LHS.getSimpleValueType() == MVT::i16 && isa(RHS)) { - // Generate a CPI/CPC pair if RHS is a 16-bit constant. + uint64_t Imm = cast(RHS)->getZExtValue(); + // Generate a CPI/CPC pair if RHS is a 16-bit constant. Use the zero + // register for the constant RHS if its lower or higher byte is zero. SDValue LHSlo = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, LHS, DAG.getIntPtrConstant(0, DL)); SDValue LHShi = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, LHS, DAG.getIntPtrConstant(1, DL)); + SDValue RHSlo = (Imm & 0xff) == 0 + ? DAG.getRegister(Subtarget.getZeroRegister(), MVT::i8) + : DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, RHS, + DAG.getIntPtrConstant(0, DL)); + SDValue RHShi = (Imm & 0xff00) == 0 + ? DAG.getRegister(Subtarget.getZeroRegister(), MVT::i8) + : DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, RHS, + DAG.getIntPtrConstant(1, DL)); + Cmp = DAG.getNode(AVRISD::CMP, DL, MVT::Glue, LHSlo, RHSlo); + Cmp = DAG.getNode(AVRISD::CMPC, DL, MVT::Glue, LHShi, RHShi, Cmp); + } else if (RHS.getSimpleValueType() == MVT::i16 && isa(LHS)) { + // Generate a CPI/CPC pair if LHS is a 16-bit constant. Use the zero + // register for the constant LHS if its lower or higher byte is zero. + uint64_t Imm = cast(LHS)->getZExtValue(); + SDValue LHSlo = (Imm & 0xff) == 0 + ? DAG.getRegister(Subtarget.getZeroRegister(), MVT::i8) + : DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, LHS, + DAG.getIntPtrConstant(0, DL)); + SDValue LHShi = (Imm & 0xff00) == 0 + ? DAG.getRegister(Subtarget.getZeroRegister(), MVT::i8) + : DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, LHS, + DAG.getIntPtrConstant(1, DL)); SDValue RHSlo = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, RHS, DAG.getIntPtrConstant(0, DL)); SDValue RHShi = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, RHS, @@ -1104,9 +1155,15 @@ bool AVRTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op, return false; } else if (const StoreSDNode *ST = dyn_cast(N)) { VT = ST->getMemoryVT(); - if (AVR::isProgramMemoryAccess(ST)) { + // We can not store to program memory. + if (AVR::isProgramMemoryAccess(ST)) + return false; + // Since the high byte need to be stored first, we can not emit + // i16 post increment store like: + // st X+, r24 + // st X+, r25 + if (VT == MVT::i16 && !Subtarget.hasLowByteFirst()) return false; - } } else { return false; } @@ -1127,6 +1184,12 @@ bool AVRTargetLowering::getPostIndexedAddressParts(SDNode *N, SDNode *Op, return false; } + // FIXME: We temporarily disable post increment load from program memory, + // due to bug https://github.com/llvm/llvm-project/issues/59914. + if (const LoadSDNode *LD = dyn_cast(N)) + if (AVR::isProgramMemoryAccess(LD)) + return false; + Base = Op->getOperand(0); Offset = DAG.getConstant(RHSC, DL, MVT::i8); AM = ISD::POST_INC; @@ -1711,11 +1774,11 @@ AVRTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, //===----------------------------------------------------------------------===// MachineBasicBlock *AVRTargetLowering::insertShift(MachineInstr &MI, - MachineBasicBlock *BB) const { + MachineBasicBlock *BB, + bool Tiny) const { unsigned Opc; const TargetRegisterClass *RC; bool HasRepeatedOperand = false; - bool HasZeroOperand = false; MachineFunction *F = BB->getParent(); MachineRegisterInfo &RI = F->getRegInfo(); const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); @@ -1750,9 +1813,8 @@ MachineBasicBlock *AVRTargetLowering::insertShift(MachineInstr &MI, RC = &AVR::DREGSRegClass; break; case AVR::Rol8: - Opc = AVR::ROLBRd; + Opc = Tiny ? AVR::ROLBRdR17 : AVR::ROLBRdR1; RC = &AVR::GPR8RegClass; - HasZeroOperand = true; break; case AVR::Rol16: Opc = AVR::ROLWRd; @@ -1814,8 +1876,6 @@ MachineBasicBlock *AVRTargetLowering::insertShift(MachineInstr &MI, auto ShiftMI = BuildMI(LoopBB, dl, TII.get(Opc), ShiftReg2).addReg(ShiftReg); if (HasRepeatedOperand) ShiftMI.addReg(ShiftReg); - if (HasZeroOperand) - ShiftMI.addReg(Subtarget.getZeroRegister()); // CheckBB: // ShiftReg = phi [%SrcReg, BB], [%ShiftReg2, LoopBB] @@ -2296,6 +2356,7 @@ MachineBasicBlock * AVRTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) const { int Opc = MI.getOpcode(); + const AVRSubtarget &STI = MBB->getParent()->getSubtarget(); // Pseudo shift instructions with a non constant shift amount are expanded // into a loop. @@ -2310,7 +2371,7 @@ AVRTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, case AVR::Ror16: case AVR::Asr8: case AVR::Asr16: - return insertShift(MI, MBB); + return insertShift(MI, MBB, STI.hasTinyEncoding()); case AVR::Lsl32: case AVR::Lsr32: case AVR::Asr32: diff --git a/llvm/lib/Target/AVR/AVRISelLowering.h b/llvm/lib/Target/AVR/AVRISelLowering.h index 80d94dc188a50..54c3769a4a4d9 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.h +++ b/llvm/lib/Target/AVR/AVRISelLowering.h @@ -194,7 +194,8 @@ class AVRTargetLowering : public TargetLowering { const AVRSubtarget &Subtarget; private: - MachineBasicBlock *insertShift(MachineInstr &MI, MachineBasicBlock *BB) const; + MachineBasicBlock *insertShift(MachineInstr &MI, MachineBasicBlock *BB, + bool Tiny) const; MachineBasicBlock *insertWideShift(MachineInstr &MI, MachineBasicBlock *BB) const; MachineBasicBlock *insertMul(MachineInstr &MI, MachineBasicBlock *BB) const; diff --git a/llvm/lib/Target/AVR/AVRInstrFormats.td b/llvm/lib/Target/AVR/AVRInstrFormats.td index 96b48a504376a..653c7276ba7f0 100644 --- a/llvm/lib/Target/AVR/AVRInstrFormats.td +++ b/llvm/lib/Target/AVR/AVRInstrFormats.td @@ -432,6 +432,8 @@ class FBRsk s, dag outs, dag ins, string asmstr, let Inst{10} = f; let Inst{9 - 3} = k; let Inst{2 - 0} = s; + + let DecoderMethod = "decodeCondBranch"; } //===----------------------------------------------------------------------===// @@ -561,6 +563,8 @@ class FSK pattern> let Inst{3} = k{0}; let Inst{2 - 0} = s; + + let DecoderMethod = "decodeCondBranch"; } class ExtensionPseudo pattern> diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.cpp b/llvm/lib/Target/AVR/AVRInstrInfo.cpp index a1bc865ffb8a1..fdfa3315e2037 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.cpp +++ b/llvm/lib/Target/AVR/AVRInstrInfo.cpp @@ -58,16 +58,21 @@ void AVRInstrInfo::copyPhysReg(MachineBasicBlock &MBB, TRI.splitReg(DestReg, DestLo, DestHi); TRI.splitReg(SrcReg, SrcLo, SrcHi); + // Emit the copies. + // The original instruction was for a register pair, of which only one + // register might have been live. Add 'undef' to satisfy the machine + // verifier, when subreg liveness is enabled. + // TODO: Eliminate these unnecessary copies. if (DestLo == SrcHi) { BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestHi) - .addReg(SrcHi, getKillRegState(KillSrc)); + .addReg(SrcHi, getKillRegState(KillSrc) | RegState::Undef); BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestLo) - .addReg(SrcLo, getKillRegState(KillSrc)); + .addReg(SrcLo, getKillRegState(KillSrc) | RegState::Undef); } else { BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestLo) - .addReg(SrcLo, getKillRegState(KillSrc)); + .addReg(SrcLo, getKillRegState(KillSrc) | RegState::Undef); BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestHi) - .addReg(SrcHi, getKillRegState(KillSrc)); + .addReg(SrcHi, getKillRegState(KillSrc) | RegState::Undef); } } } else { diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td index 05ee94be79263..b9a04caf48721 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -1690,6 +1690,16 @@ let canFoldAsLoad = 1, isReMaterializable = 1, mayLoad = 1, : F16<0b1001010111001000, (outs), (ins), "lpm", []>, 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 : $rd), @@ -1708,14 +1718,6 @@ let canFoldAsLoad = 1, isReMaterializable = 1, mayLoad = 1, "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 @@ -1742,17 +1744,20 @@ let mayLoad = 1, hasSideEffects = 0 in { Requires<[HasELPMX]>; } - // These pseudos are combination of the OUT and ELPM instructions. - let Defs = [R31R30], hasSideEffects = 1 in { + // 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<[HasELPMX]>; + Requires<[HasELPM]>; 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]>; @@ -2023,13 +2028,21 @@ let Constraints = "$src = $rd", Defs = [SREG] in { def ASRWLoRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), "asrwlo\t$rd", [(set i16:$rd, (AVRasrlo i16:$src)), (implicit SREG)]>; - - let hasSideEffects=0 in - def ROLBRd : Pseudo<(outs GPR8 - : $rd), - (ins GPR8:$src, GPR8:$zero), - "rolb\t$rd", - []>; + let Uses = [R1] in + def ROLBRdR1 : Pseudo<(outs GPR8:$rd), + (ins GPR8:$src), + "rolb\t$rd", + [(set i8:$rd, (AVRrol i8:$src)), + (implicit SREG)]>, + Requires<[HasNonTinyEncoding]>; + + let Uses = [R17] in + def ROLBRdR17 : Pseudo<(outs GPR8:$rd), + (ins GPR8:$src), + "rolb\t$rd", + [(set i8:$rd, (AVRrol i8:$src)), + (implicit SREG)]>, + Requires<[HasTinyEncoding]>; def RORBRd : Pseudo<(outs GPR8 : $rd), diff --git a/llvm/lib/Target/AVR/AVRShiftExpand.cpp b/llvm/lib/Target/AVR/AVRShiftExpand.cpp index b7dcd860467d3..f549ae62c8b2e 100644 --- a/llvm/lib/Target/AVR/AVRShiftExpand.cpp +++ b/llvm/lib/Target/AVR/AVRShiftExpand.cpp @@ -7,9 +7,10 @@ //===----------------------------------------------------------------------===// // /// \file -/// Expand 32-bit shift instructions (shl, lshr, ashr) to inline loops, just -/// like avr-gcc. This must be done in IR because otherwise the type legalizer -/// will turn 32-bit shifts into (non-existing) library calls such as __ashlsi3. +/// Expand non-8-bit and non-16-bit shift instructions (shl, lshr, ashr) to +/// inline loops, just like avr-gcc. This must be done in IR because otherwise +/// the type legalizer will turn 32-bit shifts into (non-existing) library calls +/// such as __ashlsi3. // //===----------------------------------------------------------------------===// @@ -51,8 +52,9 @@ bool AVRShiftExpand::runOnFunction(Function &F) { if (!I.isShift()) // Only expand shift instructions (shl, lshr, ashr). continue; - if (I.getType() != Type::getInt32Ty(Ctx)) - // Only expand plain i32 types. + if (I.getType() == Type::getInt8Ty(Ctx) || I.getType() == Type::getInt16Ty(Ctx)) + // Only expand non-8-bit and non-16-bit shifts, since those are expanded + // directly during isel. continue; if (isa(I.getOperand(1))) // Only expand when the shift amount is not known. @@ -75,7 +77,7 @@ bool AVRShiftExpand::runOnFunction(Function &F) { void AVRShiftExpand::expand(BinaryOperator *BI) { auto &Ctx = BI->getContext(); IRBuilder<> Builder(BI); - Type *Int32Ty = Type::getInt32Ty(Ctx); + Type *InputTy = cast(BI)->getType(); Type *Int8Ty = Type::getInt8Ty(Ctx); Value *Int8Zero = ConstantInt::get(Int8Ty, 0); @@ -101,7 +103,7 @@ void AVRShiftExpand::expand(BinaryOperator *BI) { Builder.SetInsertPoint(LoopBB); PHINode *ShiftAmountPHI = Builder.CreatePHI(Int8Ty, 2); ShiftAmountPHI->addIncoming(ShiftAmount, BB); - PHINode *ValuePHI = Builder.CreatePHI(Int32Ty, 2); + PHINode *ValuePHI = Builder.CreatePHI(InputTy, 2); ValuePHI->addIncoming(BI->getOperand(0), BB); // Subtract the shift amount by one, as we're shifting one this loop @@ -116,13 +118,13 @@ void AVRShiftExpand::expand(BinaryOperator *BI) { Value *ValueShifted; switch (BI->getOpcode()) { case Instruction::Shl: - ValueShifted = Builder.CreateShl(ValuePHI, ConstantInt::get(Int32Ty, 1)); + ValueShifted = Builder.CreateShl(ValuePHI, ConstantInt::get(InputTy, 1)); break; case Instruction::LShr: - ValueShifted = Builder.CreateLShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); + ValueShifted = Builder.CreateLShr(ValuePHI, ConstantInt::get(InputTy, 1)); break; case Instruction::AShr: - ValueShifted = Builder.CreateAShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); + ValueShifted = Builder.CreateAShr(ValuePHI, ConstantInt::get(InputTy, 1)); break; default: llvm_unreachable("asked to expand an instruction that is not a shift"); @@ -137,7 +139,7 @@ void AVRShiftExpand::expand(BinaryOperator *BI) { // Collect the resulting value. This is necessary in the IR but won't produce // any actual instructions. Builder.SetInsertPoint(BI); - PHINode *Result = Builder.CreatePHI(Int32Ty, 2); + PHINode *Result = Builder.CreatePHI(InputTy, 2); Result->addIncoming(BI->getOperand(0), BB); Result->addIncoming(ValueShifted, LoopBB); diff --git a/llvm/lib/Target/AVR/AVRSubtarget.h b/llvm/lib/Target/AVR/AVRSubtarget.h index 25046e6cf1eaf..5c7c600ebbf14 100644 --- a/llvm/lib/Target/AVR/AVRSubtarget.h +++ b/llvm/lib/Target/AVR/AVRSubtarget.h @@ -73,7 +73,6 @@ class AVRSubtarget : public AVRGenSubtargetInfo { bool hasLPMX() const { return m_hasLPMX; } bool hasELPM() const { return m_hasELPM; } bool hasELPMX() const { return m_hasELPMX; } - bool hasPROGMEM() const { return m_hasPROGMEM; } bool hasSPM() const { return m_hasSPM; } bool hasSPMX() const { return m_hasSPMX; } bool hasDES() const { return m_hasDES; } @@ -82,9 +81,12 @@ class AVRSubtarget : public AVRGenSubtargetInfo { 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; } + bool enableSubRegLiveness() const override { return true; } + /// Gets the ELF architecture for the e_flags field /// of an ELF object file. unsigned getELFArch() const { @@ -128,7 +130,6 @@ class AVRSubtarget : public AVRGenSubtargetInfo { bool m_hasLPMX = false; bool m_hasELPM = false; bool m_hasELPMX = false; - bool m_hasPROGMEM = false; bool m_hasSPM = false; bool m_hasSPMX = false; bool m_hasDES = false; @@ -136,6 +137,7 @@ class AVRSubtarget : public AVRGenSubtargetInfo { 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/lib/Target/AVR/Disassembler/AVRDisassembler.cpp b/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp index 7674d9e354faf..df047ed8ecae0 100644 --- a/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp +++ b/llvm/lib/Target/AVR/Disassembler/AVRDisassembler.cpp @@ -16,6 +16,9 @@ #include "MCTargetDesc/AVRMCTargetDesc.h" #include "TargetInfo/AVRTargetInfo.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" + #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDecoderOps.h" @@ -126,6 +129,10 @@ static DecodeStatus decodeMemri(MCInst &Inst, unsigned Insn, uint64_t Address, static DecodeStatus decodeFBRk(MCInst &Inst, unsigned Insn, uint64_t Address, const MCDisassembler *Decoder); +static DecodeStatus decodeCondBranch(MCInst &Inst, unsigned Insn, + uint64_t Address, + const MCDisassembler *Decoder); + static DecodeStatus decodeLoadStore(MCInst &Inst, unsigned Insn, uint64_t Address, const MCDisassembler *Decoder); @@ -287,6 +294,40 @@ static DecodeStatus decodeFBRk(MCInst &Inst, unsigned Insn, uint64_t Address, return MCDisassembler::Success; } +static DecodeStatus decodeCondBranch(MCInst &Inst, unsigned Insn, + uint64_t Address, + const MCDisassembler *Decoder) { + // These 8 instructions are not defined as aliases of BRBS/BRBC. + DenseMap brInsts = { + {0x000, AVR::BRLOk}, {0x400, AVR::BRSHk}, {0x001, AVR::BREQk}, + {0x401, AVR::BRNEk}, {0x002, AVR::BRMIk}, {0x402, AVR::BRPLk}, + {0x004, AVR::BRLTk}, {0x404, AVR::BRGEk}}; + + // Get the relative offset. + int16_t Offset = ((int16_t)((Insn & 0x3f8) << 6)) >> 8; + + // Search the instruction pattern. + auto NotAlias = [&Insn](const std::pair &I) { + return (Insn & 0x407) != I.first; + }; + llvm::partition(brInsts, NotAlias); + auto It = llvm::partition_point(brInsts, NotAlias); + + // Decode the instruction. + if (It != brInsts.end()) { + // This instruction is not an alias of BRBC/BRBS. + Inst.setOpcode(It->second); + Inst.addOperand(MCOperand::createImm(Offset)); + } else { + // Fall back to an ordinary BRBS/BRBC. + Inst.setOpcode(Insn & 0x400 ? AVR::BRBCsk : AVR::BRBSsk); + Inst.addOperand(MCOperand::createImm(Insn & 7)); + Inst.addOperand(MCOperand::createImm(Offset)); + } + + return MCDisassembler::Success; +} + static DecodeStatus decodeLoadStore(MCInst &Inst, unsigned Insn, uint64_t Address, const MCDisassembler *Decoder) { diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp index cf87106ec5a3d..c94469c8d9f3d 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRAsmBackend.cpp @@ -514,6 +514,12 @@ bool AVRAsmBackend::shouldForceRelocation(const MCAssembler &Asm, // Fixups which should always be recorded as relocations. case AVR::fixup_7_pcrel: case AVR::fixup_13_pcrel: + // Do not force relocation for PC relative branch like 'rjmp .', + // 'rcall . - off' and 'breq . + off'. + if (const auto *SymA = Target.getSymA()) + if (SymA->getSymbol().getName().size() == 0) + return false; + [[fallthrough]]; case AVR::fixup_call: return true; } diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRELFStreamer.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRELFStreamer.cpp index ade5df18c3b9e..3900a1f3ba50b 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRELFStreamer.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRELFStreamer.cpp @@ -61,6 +61,7 @@ AVRELFStreamer::AVRELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI) unsigned EFlags = MCA.getELFHeaderEFlags(); EFlags |= getEFlagsForFeatureSet(STI.getFeatureBits()); + EFlags |= ELF::EF_AVR_LINKRELAX_PREPARED; MCA.setELFHeaderEFlags(EFlags); } diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp index c8bb410e48829..9edd8bb0d10f6 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp @@ -146,7 +146,8 @@ unsigned AVRMCCodeEmitter::encodeMemri(const MCInst &MI, unsigned OpNo, switch (RegOp.getReg()) { default: - llvm_unreachable("Expected either Y or Z register"); + Ctx.reportError(MI.getLoc(), "Expected either Y or Z register"); + return 0; case AVR::R31R30: RegBit = 0; break; // Z register @@ -164,7 +165,7 @@ unsigned AVRMCCodeEmitter::encodeMemri(const MCInst &MI, unsigned OpNo, Fixups.push_back(MCFixup::create(0, OffsetOp.getExpr(), MCFixupKind(AVR::fixup_6), MI.getLoc())); } else { - llvm_unreachable("invalid value for offset"); + llvm_unreachable("Invalid value for offset"); } return (RegBit << 6) | OffsetBits; diff --git a/llvm/test/CodeGen/AVR/PR37143.ll b/llvm/test/CodeGen/AVR/PR37143.ll index c7cabd3cd0875..fe9529a24973f 100644 --- a/llvm/test/CodeGen/AVR/PR37143.ll +++ b/llvm/test/CodeGen/AVR/PR37143.ll @@ -2,8 +2,8 @@ ; CHECK: ld {{r[0-9]+}}, [[PTR:[XYZ]]] ; CHECK: ldd {{r[0-9]+}}, [[PTR]]+1 -; CHECK: st [[PTR2:[XYZ]]], {{r[0-9]+}} -; CHECK: std [[PTR2]]+1, {{r[0-9]+}} +; CHECK: std [[PTR2:[XYZ]]]+1, {{r[0-9]+}} +; CHECK: st [[PTR2]], {{r[0-9]+}} define void @load_store_16(i16* nocapture %ptr) local_unnamed_addr #1 { entry: %0 = load i16, i16* %ptr, align 2 diff --git a/llvm/test/CodeGen/AVR/alloca.ll b/llvm/test/CodeGen/AVR/alloca.ll index 37c0e62b55fde..30842f8244c61 100644 --- a/llvm/test/CodeGen/AVR/alloca.ll +++ b/llvm/test/CodeGen/AVR/alloca.ll @@ -46,12 +46,12 @@ define i16 @alloca_write(i16 %x) { entry: ; CHECK-LABEL: alloca_write: ; Small offset here -; CHECK: std Y+23, {{.*}} ; CHECK: std Y+24, {{.*}} +; CHECK: std Y+23, {{.*}} ; Big offset here ; CHECK: adiw r28, 57 -; CHECK: std Y+62, {{.*}} ; CHECK: std Y+63, {{.*}} +; CHECK: std Y+62, {{.*}} ; CHECK: sbiw r28, 57 %p = alloca [15 x i16] %k = alloca [14 x i16] @@ -71,8 +71,8 @@ define void @alloca_write_huge() { ; CHECK-LABEL: alloca_write_huge: ; CHECK: subi r28, 41 ; CHECK: sbci r29, 255 -; CHECK: std Y+62, {{.*}} ; CHECK: std Y+63, {{.*}} +; CHECK: std Y+62, {{.*}} ; CHECK: subi r28, 215 ; CHECK: sbci r29, 0 %k = alloca [140 x i16] diff --git a/llvm/test/CodeGen/AVR/atomics/load16.ll b/llvm/test/CodeGen/AVR/atomics/load16.ll index d019bf3750b8c..5046332688b34 100644 --- a/llvm/test/CodeGen/AVR/atomics/load16.ll +++ b/llvm/test/CodeGen/AVR/atomics/load16.ll @@ -33,8 +33,8 @@ define i16 @atomic_load_cmp_swap16(i16* %foo) { ; CHECK-NEXT: ldd [[RDH:r[0-9]+]], [[RR]]+1 ; CHECK-NEXT: add [[RR1L:r[0-9]+]], [[RDL]] ; CHECK-NEXT: adc [[RR1H:r[0-9]+]], [[RDH]] -; CHECK-NEXT: st [[RR]], [[RR1L]] ; CHECK-NEXT: std [[RR]]+1, [[RR1H]] +; CHECK-NEXT: st [[RR]], [[RR1L]] ; CHECK-NEXT: out 63, r0 define i16 @atomic_load_add16(i16* %foo) { %val = atomicrmw add i16* %foo, i16 13 seq_cst @@ -49,8 +49,8 @@ define i16 @atomic_load_add16(i16* %foo) { ; CHECK-NEXT: movw [[TMPL:r[0-9]+]], [[RDL]] ; CHECK-NEXT: sub [[TMPL]], [[RR1L:r[0-9]+]] ; CHECK-NEXT: sbc [[TMPH:r[0-9]+]], [[RR1H:r[0-9]+]] -; CHECK-NEXT: st [[RR]], [[TMPL]] ; CHECK-NEXT: std [[RR]]+1, [[TMPH]] +; CHECK-NEXT: st [[RR]], [[TMPL]] ; CHECK-NEXT: out 63, r0 define i16 @atomic_load_sub16(i16* %foo) { %val = atomicrmw sub i16* %foo, i16 13 seq_cst @@ -64,8 +64,8 @@ define i16 @atomic_load_sub16(i16* %foo) { ; CHECK-NEXT: ldd [[RDH:r[0-9]+]], [[RR]]+1 ; CHECK-NEXT: and [[RD1L:r[0-9]+]], [[RDL]] ; CHECK-NEXT: and [[RD1H:r[0-9]+]], [[RDH]] -; CHECK-NEXT: st [[RR]], [[RD1L]] ; CHECK-NEXT: std [[RR]]+1, [[RD1H]] +; CHECK-NEXT: st [[RR]], [[RD1L]] ; CHECK-NEXT: out 63, r0 define i16 @atomic_load_and16(i16* %foo) { %val = atomicrmw and i16* %foo, i16 13 seq_cst @@ -79,8 +79,8 @@ define i16 @atomic_load_and16(i16* %foo) { ; CHECK-NEXT: ldd [[RDH:r[0-9]+]], [[RR]]+1 ; CHECK-NEXT: or [[RD1L:r[0-9]+]], [[RDL]] ; CHECK-NEXT: or [[RD1H:r[0-9]+]], [[RDH]] -; CHECK-NEXT: st [[RR]], [[RD1L]] ; CHECK-NEXT: std [[RR]]+1, [[RD1H]] +; CHECK-NEXT: st [[RR]], [[RD1L]] ; CHECK-NEXT: out 63, r0 define i16 @atomic_load_or16(i16* %foo) { %val = atomicrmw or i16* %foo, i16 13 seq_cst @@ -94,8 +94,8 @@ define i16 @atomic_load_or16(i16* %foo) { ; CHECK-NEXT: ldd [[RDH:r[0-9]+]], [[RR]]+1 ; CHECK-NEXT: eor [[RD1L:r[0-9]+]], [[RDL]] ; CHECK-NEXT: eor [[RD1H:r[0-9]+]], [[RDH]] -; CHECK-NEXT: st [[RR]], [[RD1L]] ; CHECK-NEXT: std [[RR]]+1, [[RD1H]] +; CHECK-NEXT: st [[RR]], [[RD1L]] ; CHECK-NEXT: out 63, r0 define i16 @atomic_load_xor16(i16* %foo) { %val = atomicrmw xor i16* %foo, i16 13 seq_cst diff --git a/llvm/test/CodeGen/AVR/atomics/store.ll b/llvm/test/CodeGen/AVR/atomics/store.ll index e1231c21e7d79..0eb6b51b67515 100644 --- a/llvm/test/CodeGen/AVR/atomics/store.ll +++ b/llvm/test/CodeGen/AVR/atomics/store.ll @@ -13,8 +13,8 @@ define void @atomic_store8(i8* %foo) { ; CHECK-LABEL: atomic_store16 ; CHECK: in r0, 63 ; CHECK-NEXT: cli -; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] -; CHECK-NEXT: std [[RD]]+1, [[RR:r[0-9]+]] +; CHECK-NEXT: std [[RD:(X|Y|Z)]]+1, [[RR:r[0-9]+]] +; CHECK-NEXT: st [[RD]], [[RR:r[0-9]+]] ; CHECK-NEXT: out 63, r0 define void @atomic_store16(i16* %foo) { store atomic i16 1, i16* %foo unordered, align 2 diff --git a/llvm/test/CodeGen/AVR/atomics/store16.ll b/llvm/test/CodeGen/AVR/atomics/store16.ll index 610a53fad736d..779ea188be771 100644 --- a/llvm/test/CodeGen/AVR/atomics/store16.ll +++ b/llvm/test/CodeGen/AVR/atomics/store16.ll @@ -3,8 +3,8 @@ ; CHECK-LABEL: atomic_store16 ; CHECK: in r0, 63 ; CHECK-NEXT: cli -; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] ; CHECK-NEXT: std [[RD:(X|Y|Z)]]+1, [[RR:r[0-9]+]] +; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] ; CHECK-NEXT: out 63, r0 define void @atomic_store16(i16* %foo) { store atomic i16 1, i16* %foo unordered, align 2 @@ -14,8 +14,8 @@ define void @atomic_store16(i16* %foo) { ; CHECK-LABEL: monotonic ; CHECK: in r0, 63 ; CHECK-NEXT: cli -; CHECK-NEXT: st Z, r24 ; CHECK-NEXT: std Z+1, r25 +; CHECK-NEXT: st Z, r24 ; CHECK-NEXT: out 63, r0 define void @monotonic(i16) { entry-block: diff --git a/llvm/test/CodeGen/AVR/bug-56423.ll b/llvm/test/CodeGen/AVR/bug-56423.ll new file mode 100644 index 0000000000000..1fd3cf0eb6d15 --- /dev/null +++ b/llvm/test/CodeGen/AVR/bug-56423.ll @@ -0,0 +1,135 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=avr -mcpu=atmega328 | FileCheck %s --check-prefix=AVR51 +; RUN: llc < %s -mtriple=avr -mcpu=at90s8515 | FileCheck %s --check-prefix=AVR2 + +; Test for bug https://github.com/llvm/llvm-project/issues/56423 + +define i32 @foo(i32 %x, i32 %in_min, i32 %in_max, i32 %out_min, i32 %out_max) { +; AVR51-LABEL: foo: +; AVR51: ; %bb.0: ; %entry +; AVR51-NEXT: push r6 +; AVR51-NEXT: push r7 +; AVR51-NEXT: push r8 +; AVR51-NEXT: push r9 +; AVR51-NEXT: push r14 +; AVR51-NEXT: push r15 +; AVR51-NEXT: push r16 +; AVR51-NEXT: push r17 +; AVR51-NEXT: push r28 +; AVR51-NEXT: push r29 +; AVR51-NEXT: in r28, 61 +; AVR51-NEXT: in r29, 62 +; AVR51-NEXT: movw r8, r20 +; AVR51-NEXT: movw r6, r18 +; AVR51-NEXT: movw r20, r24 +; AVR51-NEXT: movw r18, r22 +; AVR51-NEXT: ldd r22, Y+13 +; AVR51-NEXT: ldd r23, Y+14 +; AVR51-NEXT: ldd r24, Y+15 +; AVR51-NEXT: ldd r25, Y+16 +; AVR51-NEXT: sub r22, r10 +; AVR51-NEXT: sbc r23, r11 +; AVR51-NEXT: sbc r24, r12 +; AVR51-NEXT: sbc r25, r13 +; AVR51-NEXT: sub r18, r6 +; AVR51-NEXT: sbc r19, r7 +; AVR51-NEXT: sbc r20, r8 +; AVR51-NEXT: sbc r21, r9 +; AVR51-NEXT: call __mulsi3 +; AVR51-NEXT: sub r14, r6 +; AVR51-NEXT: sbc r15, r7 +; AVR51-NEXT: sbc r16, r8 +; AVR51-NEXT: sbc r17, r9 +; AVR51-NEXT: movw r18, r14 +; AVR51-NEXT: movw r20, r16 +; AVR51-NEXT: call __divmodsi4 +; AVR51-NEXT: add r18, r10 +; AVR51-NEXT: adc r19, r11 +; AVR51-NEXT: adc r20, r12 +; AVR51-NEXT: adc r21, r13 +; AVR51-NEXT: movw r22, r18 +; AVR51-NEXT: movw r24, r20 +; AVR51-NEXT: pop r29 +; AVR51-NEXT: pop r28 +; AVR51-NEXT: pop r17 +; AVR51-NEXT: pop r16 +; AVR51-NEXT: pop r15 +; AVR51-NEXT: pop r14 +; AVR51-NEXT: pop r9 +; AVR51-NEXT: pop r8 +; AVR51-NEXT: pop r7 +; AVR51-NEXT: pop r6 +; AVR51-NEXT: ret +; +; AVR2-LABEL: foo: +; AVR2: ; %bb.0: ; %entry +; AVR2-NEXT: push r6 +; AVR2-NEXT: push r7 +; AVR2-NEXT: push r8 +; AVR2-NEXT: push r9 +; AVR2-NEXT: push r14 +; AVR2-NEXT: push r15 +; AVR2-NEXT: push r16 +; AVR2-NEXT: push r17 +; AVR2-NEXT: push r28 +; AVR2-NEXT: push r29 +; AVR2-NEXT: in r28, 61 +; AVR2-NEXT: in r29, 62 +; AVR2-NEXT: mov r8, r20 +; AVR2-NEXT: mov r9, r21 +; AVR2-NEXT: mov r6, r18 +; AVR2-NEXT: mov r7, r19 +; AVR2-NEXT: mov r20, r24 +; AVR2-NEXT: mov r21, r25 +; AVR2-NEXT: mov r18, r22 +; AVR2-NEXT: mov r19, r23 +; AVR2-NEXT: ldd r22, Y+13 +; AVR2-NEXT: ldd r23, Y+14 +; AVR2-NEXT: ldd r24, Y+15 +; AVR2-NEXT: ldd r25, Y+16 +; AVR2-NEXT: sub r22, r10 +; AVR2-NEXT: sbc r23, r11 +; AVR2-NEXT: sbc r24, r12 +; AVR2-NEXT: sbc r25, r13 +; AVR2-NEXT: sub r18, r6 +; AVR2-NEXT: sbc r19, r7 +; AVR2-NEXT: sbc r20, r8 +; AVR2-NEXT: sbc r21, r9 +; AVR2-NEXT: rcall __mulsi3 +; AVR2-NEXT: sub r14, r6 +; AVR2-NEXT: sbc r15, r7 +; AVR2-NEXT: sbc r16, r8 +; AVR2-NEXT: sbc r17, r9 +; AVR2-NEXT: mov r18, r14 +; AVR2-NEXT: mov r19, r15 +; AVR2-NEXT: mov r20, r16 +; AVR2-NEXT: mov r21, r17 +; AVR2-NEXT: rcall __divmodsi4 +; AVR2-NEXT: add r18, r10 +; AVR2-NEXT: adc r19, r11 +; AVR2-NEXT: adc r20, r12 +; AVR2-NEXT: adc r21, r13 +; AVR2-NEXT: mov r22, r18 +; AVR2-NEXT: mov r23, r19 +; AVR2-NEXT: mov r24, r20 +; AVR2-NEXT: mov r25, r21 +; AVR2-NEXT: pop r29 +; AVR2-NEXT: pop r28 +; AVR2-NEXT: pop r17 +; AVR2-NEXT: pop r16 +; AVR2-NEXT: pop r15 +; AVR2-NEXT: pop r14 +; AVR2-NEXT: pop r9 +; AVR2-NEXT: pop r8 +; AVR2-NEXT: pop r7 +; AVR2-NEXT: pop r6 +; AVR2-NEXT: ret +entry: + %sub = sub nsw i32 %x, %in_min + %sub1 = sub nsw i32 %out_max, %out_min + %mul = mul nsw i32 %sub1, %sub + %sub2 = sub nsw i32 %in_max, %in_min + %div = sdiv i32 %mul, %sub2 + %add = add nsw i32 %div, %out_min + ret i32 %add +} diff --git a/llvm/test/CodeGen/AVR/call.ll b/llvm/test/CodeGen/AVR/call.ll index ec91480163b1e..39382bc6a34b5 100644 --- a/llvm/test/CodeGen/AVR/call.ll +++ b/llvm/test/CodeGen/AVR/call.ll @@ -35,8 +35,8 @@ define i8 @calli8_stack() { ; CHECK-LABEL: calli8_stack: ; CHECK: ldi [[REG1:r[0-9]+]], 10 ; CHECK: ldi [[REG2:r[0-9]+]], 11 -; CHECK: std Z+1, [[REG1]] ; CHECK: std Z+2, [[REG2]] +; CHECK: std Z+1, [[REG1]] ; AVR6: call foo8_3 ; AVR2: rcall foo8_3 %result1 = call i8 @foo8_3(i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11) @@ -59,12 +59,12 @@ define i16 @calli16_stack() { ; CHECK-LABEL: calli16_stack: ; CHECK: ldi [[REG1:r[0-9]+]], 10 ; CHECK: ldi [[REG2:r[0-9]+]], 2 -; CHECK: std Z+3, [[REG1]] ; CHECK: std Z+4, [[REG2]] +; CHECK: std Z+3, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 9 ; CHECK: ldi [[REG2:r[0-9]+]], 2 -; CHECK: std Z+1, [[REG1]] ; CHECK: std Z+2, [[REG2]] +; CHECK: std Z+1, [[REG1]] ; AVR6: call foo16_2 ; AVR2: rcall foo16_2 %result1 = call i16 @foo16_2(i16 512, i16 513, i16 514, i16 515, i16 516, i16 517, i16 518, i16 519, i16 520, i16 521, i16 522) @@ -91,12 +91,12 @@ define i32 @calli32_stack() { ; CHECK-LABEL: calli32_stack: ; CHECK: ldi [[REG1:r[0-9]+]], 15 ; CHECK: ldi [[REG2:r[0-9]+]], 2 -; CHECK: std Z+3, [[REG1]] ; CHECK: std Z+4, [[REG2]] +; CHECK: std Z+3, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 64 ; CHECK: ldi [[REG2:r[0-9]+]], 66 -; CHECK: std Z+1, [[REG1]] ; CHECK: std Z+2, [[REG2]] +; CHECK: std Z+1, [[REG1]] ; AVR6: call foo32_2 ; AVR2: rcall foo32_2 %result1 = call i32 @foo32_2(i32 1, i32 2, i32 3, i32 4, i32 34554432) @@ -124,20 +124,20 @@ define i64 @calli64_stack() { ; CHECK: ldi [[REG1:r[0-9]+]], 31 ; CHECK: ldi [[REG2:r[0-9]+]], 242 -; CHECK: std Z+7, [[REG1]] ; CHECK: std Z+8, [[REG2]] +; CHECK: std Z+7, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 76 ; CHECK: ldi [[REG2:r[0-9]+]], 73 -; CHECK: std Z+5, [[REG1]] ; CHECK: std Z+6, [[REG2]] +; CHECK: std Z+5, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 155 ; CHECK: ldi [[REG2:r[0-9]+]], 88 -; CHECK: std Z+3, [[REG1]] ; CHECK: std Z+4, [[REG2]] +; CHECK: std Z+3, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 255 ; CHECK: ldi [[REG2:r[0-9]+]], 255 -; CHECK: std Z+1, [[REG1]] ; CHECK: std Z+2, [[REG2]] +; CHECK: std Z+1, [[REG1]] ; AVR6: call foo64_2 ; AVR2: rcall foo64_2 %result1 = call i64 @foo64_2(i64 1, i64 2, i64 17446744073709551615) @@ -157,20 +157,20 @@ define void @testcallprologue() { ; CHECK: std Y+9, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 11 ; CHECK: ldi [[REG2:r[0-9]+]], 10 -; CHECK: std Y+7, [[REG1]] ; CHECK: std Y+8, [[REG2]] +; CHECK: std Y+7, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 13 ; CHECK: ldi [[REG2:r[0-9]+]], 12 -; CHECK: std Y+5, [[REG1]] ; CHECK: std Y+6, [[REG2]] +; CHECK: std Y+5, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 15 ; CHECK: ldi [[REG2:r[0-9]+]], 14 -; CHECK: std Y+3, [[REG1]] ; CHECK: std Y+4, [[REG2]] +; CHECK: std Y+3, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 8 ; CHECK: ldi [[REG2:r[0-9]+]], 9 -; CHECK: std Y+1, [[REG1]] ; CHECK: std Y+2, [[REG2]] +; CHECK: std Y+1, [[REG1]] ; CHECK: pop r29 ; CHECK: pop r28 %p = alloca [8 x i16] diff --git a/llvm/test/CodeGen/AVR/cmp.ll b/llvm/test/CodeGen/AVR/cmp.ll index e9769068f911d..715a3f1c9c18e 100644 --- a/llvm/test/CodeGen/AVR/cmp.ll +++ b/llvm/test/CodeGen/AVR/cmp.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s -march=avr | FileCheck %s +; RUN: llc < %s -mtriple=avr -mcpu=attiny10 | FileCheck --check-prefix=TINY %s declare void @f1(i8) declare void @f2(i8) @@ -202,3 +203,98 @@ if.else: if.end: ret void } + +define i16 @cmp_i16_gt_0(i16 %0) { +; CHECK-LABEL: cmp_i16_gt_0: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldi r18, 1 +; CHECK-NEXT: cp r1, r24 +; CHECK-NEXT: cpc r1, r25 +; CHECK-NEXT: brlt .LBB11_2 +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: mov r18, r1 +; CHECK-NEXT: .LBB11_2: +; CHECK-NEXT: mov r24, r18 +; CHECK-NEXT: clr r25 +; CHECK-NEXT: ret +; +; TINY-LABEL: cmp_i16_gt_0: +; TINY: ; %bb.0: +; TINY-NEXT: ldi r20, 1 +; TINY-NEXT: cp r17, r24 +; TINY-NEXT: cpc r17, r25 +; TINY-NEXT: brlt .LBB11_2 +; TINY-NEXT: ; %bb.1: +; TINY-NEXT: mov r20, r17 +; TINY-NEXT: .LBB11_2: +; TINY-NEXT: mov r24, r20 +; TINY-NEXT: clr r25 +; TINY-NEXT: ret + %2 = icmp sgt i16 %0, 0 + %3 = zext i1 %2 to i16 + ret i16 %3 +} + +define i16 @cmp_i16_gt_126(i16 %0) { +; CHECK-LABEL: cmp_i16_gt_126: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldi r18, 1 +; CHECK-NEXT: cpi r24, 127 +; CHECK-NEXT: cpc r25, r1 +; CHECK-NEXT: brge .LBB12_2 +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: mov r18, r1 +; CHECK-NEXT: .LBB12_2: +; CHECK-NEXT: mov r24, r18 +; CHECK-NEXT: clr r25 +; CHECK-NEXT: ret +; +; TINY-LABEL: cmp_i16_gt_126: +; TINY: ; %bb.0: +; TINY-NEXT: ldi r20, 1 +; TINY-NEXT: cpi r24, 127 +; TINY-NEXT: cpc r25, r17 +; TINY-NEXT: brge .LBB12_2 +; TINY-NEXT: ; %bb.1: +; TINY-NEXT: mov r20, r17 +; TINY-NEXT: .LBB12_2: +; TINY-NEXT: mov r24, r20 +; TINY-NEXT: clr r25 +; TINY-NEXT: ret + %2 = icmp sgt i16 %0, 126 + %3 = zext i1 %2 to i16 + ret i16 %3 +} + +define i16 @cmp_i16_gt_1023(i16 %0) { +; CHECK-LABEL: cmp_i16_gt_1023: +; CHECK: ; %bb.0: +; CHECK-NEXT: ldi r19, 4 +; CHECK-NEXT: ldi r18, 1 +; CHECK-NEXT: cp r24, r1 +; CHECK-NEXT: cpc r25, r19 +; CHECK-NEXT: brge .LBB13_2 +; CHECK-NEXT: ; %bb.1: +; CHECK-NEXT: mov r18, r1 +; CHECK-NEXT: .LBB13_2: +; CHECK-NEXT: mov r24, r18 +; CHECK-NEXT: clr r25 +; CHECK-NEXT: ret +; +; TINY-LABEL: cmp_i16_gt_1023: +; TINY: ; %bb.0: +; TINY-NEXT: ldi r21, 4 +; TINY-NEXT: ldi r20, 1 +; TINY-NEXT: cp r24, r17 +; TINY-NEXT: cpc r25, r21 +; TINY-NEXT: brge .LBB13_2 +; TINY-NEXT: ; %bb.1: +; TINY-NEXT: mov r20, r17 +; TINY-NEXT: .LBB13_2: +; TINY-NEXT: mov r24, r20 +; TINY-NEXT: clr r25 +; TINY-NEXT: ret + %2 = icmp sgt i16 %0, 1023 + %3 = zext i1 %2 to i16 + ret i16 %3 +} diff --git a/llvm/test/CodeGen/AVR/dynalloca.ll b/llvm/test/CodeGen/AVR/dynalloca.ll index 7f69966159761..28c743c5bdc67 100644 --- a/llvm/test/CodeGen/AVR/dynalloca.ll +++ b/llvm/test/CodeGen/AVR/dynalloca.ll @@ -19,8 +19,8 @@ define void @test1(i16 %x) { ; CHECK-NEXT: out 63, r0 ; CHECK-NEXT: out 61, {{.*}} ; Test writes -; CHECK: std Z+12, {{.*}} ; CHECK: std Z+13, {{.*}} +; CHECK: std Z+12, {{.*}} ; CHECK: std Z+7, {{.*}} ; CHECK-NOT: std ; Test SP restore @@ -66,14 +66,14 @@ define void @dynalloca2(i16 %x) { ; Store values on the stack ; CHECK: ldi r16, 0 ; CHECK: ldi r17, 0 -; CHECK: std Z+7, r16 ; CHECK: std Z+8, r17 -; CHECK: std Z+5, r16 +; CHECK: std Z+7, r16 ; CHECK: std Z+6, r17 -; CHECK: std Z+3, r16 +; CHECK: std Z+5, r16 ; CHECK: std Z+4, r17 -; CHECK: std Z+1, r16 +; CHECK: std Z+3, r16 ; CHECK: std Z+2, r17 +; CHECK: std Z+1, r16 ; CHECK: call ; Call frame restore ; CHECK-NEXT: in r30, 61 diff --git a/llvm/test/CodeGen/AVR/elpm.ll b/llvm/test/CodeGen/AVR/elpm.ll index a322ab773014a..21f956aa2cb9d 100644 --- a/llvm/test/CodeGen/AVR/elpm.ll +++ b/llvm/test/CodeGen/AVR/elpm.ll @@ -1,5 +1,8 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py -; RUN: llc < %s -mtriple=avr --mcpu=atmega2560 -verify-machineinstrs | FileCheck %s +; RUN: llc < %s -mtriple=avr -mattr=+movw -mattr=+elpm -mattr=+elpmx -mattr=+lpm -mattr=+lpmx -verify-machineinstrs \ +; RUN: | FileCheck %s +; RUN: llc < %s -mtriple=avr -mattr=+movw -mattr=+elpm -mattr=-elpmx -mattr=+lpm -mattr=-lpmx -verify-machineinstrs \ +; RUN: | FileCheck --check-prefix=NOX %s @arr0 = addrspace(1) constant [4 x i16] [i16 123, i16 24, i16 56, i16 37], align 1 @arr1 = addrspace(2) constant [4 x i16] [i16 123, i16 34, i16 46, i16 27], align 1 @@ -25,6 +28,32 @@ define i16 @foo0(i16 %a, i16 %b) { ; 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 @@ -56,6 +85,34 @@ define i16 @foo1(i16 %a, i16 %b) { ; 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 @@ -87,6 +144,34 @@ define i16 @foo2(i16 %a, i16 %b) { ; 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 @@ -120,6 +205,36 @@ define i16 @foo3(i16 %a, i16 %b) { ; 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 @@ -129,9 +244,9 @@ entry: ret i16 %sub } -@arrb1 = addrspace(1) constant [4 x i8] c"{\188%", align 1 -@arrb3 = addrspace(3) constant [4 x i8] c"{\22.\1B", align 1 -@arrb5 = addrspace(5) constant [4 x i8] c"{\17-\11", align 1 +@arrb1 = addrspace(1) constant [4 x i8] c"abcd", align 1 +@arrb3 = addrspace(3) constant [4 x i8] c"1234", align 1 +@arrb5 = addrspace(5) constant [4 x i8] c"HJLQ", align 1 define signext i8 @foob0(i16 %a, i16 %b) { ; CHECK-LABEL: foob0: @@ -149,6 +264,24 @@ define signext i8 @foob0(i16 %a, i16 %b) { ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob0: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r22, lo8(-(arrb1)) +; NOX-NEXT: sbci r23, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: lpm +; NOX-NEXT: mov r18, r0 +; NOX-NEXT: subi r24, lo8(-(arrb1)) +; NOX-NEXT: sbci r25, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: lpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: sub r24, r18 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(1)* @arrb1, i16 0, i16 %a %0 = load i8, i8 addrspace(1)* %arrayidx, align 1 @@ -176,6 +309,26 @@ define signext i8 @foob1(i16 %a, i16 %b) { ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob1: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r22, lo8(-(arrb3)) +; NOX-NEXT: sbci r23, hi8(-(arrb3)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: ldi r18, 2 +; NOX-NEXT: out 59, r18 +; NOX-NEXT: elpm +; NOX-NEXT: mov r18, r0 +; NOX-NEXT: subi r24, lo8(-(arrb1)) +; NOX-NEXT: sbci r25, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: lpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: sub r24, r18 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(1)* @arrb1, i16 0, i16 %a %0 = load i8, i8 addrspace(1)* %arrayidx, align 1 @@ -203,6 +356,26 @@ define signext i8 @foob2(i16 %a, i16 %b) { ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob2: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r24, lo8(-(arrb5)) +; NOX-NEXT: sbci r25, hi8(-(arrb5)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: ldi r24, 4 +; NOX-NEXT: out 59, r24 +; NOX-NEXT: elpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: subi r22, lo8(-(arrb1)) +; NOX-NEXT: sbci r23, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: lpm +; NOX-NEXT: mov r25, r0 +; NOX-NEXT: sub r24, r25 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(5)* @arrb5, i16 0, i16 %a %0 = load i8, i8 addrspace(5)* %arrayidx, align 1 @@ -232,6 +405,28 @@ define signext i8 @foob3(i16 %a, i16 %b) { ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob3: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r22, lo8(-(arrb5)) +; NOX-NEXT: sbci r23, hi8(-(arrb5)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: ldi r18, 4 +; NOX-NEXT: out 59, r18 +; NOX-NEXT: elpm +; NOX-NEXT: mov r18, r0 +; NOX-NEXT: subi r24, lo8(-(arrb3)) +; NOX-NEXT: sbci r25, hi8(-(arrb3)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: ldi r24, 2 +; NOX-NEXT: out 59, r24 +; NOX-NEXT: elpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: sub r24, r18 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(3)* @arrb3, i16 0, i16 %a %0 = load i8, i8 addrspace(3)* %arrayidx, align 1 @@ -260,6 +455,27 @@ define signext i8 @foob4(i16 %a, i16 %b) { ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob4: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r22, lo8(-(arrb3)) +; NOX-NEXT: sbci r23, hi8(-(arrb3)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: ldi r18, 2 +; NOX-NEXT: out 59, r18 +; NOX-NEXT: elpm +; NOX-NEXT: mov r19, r0 +; NOX-NEXT: subi r24, lo8(-(arrb3)) +; NOX-NEXT: sbci r25, hi8(-(arrb3)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: out 59, r18 +; NOX-NEXT: elpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: sub r24, r19 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(3)* @arrb3, i16 0, i16 %a %0 = load i8, i8 addrspace(3)* %arrayidx, align 1 diff --git a/llvm/test/CodeGen/AVR/hardware-mul.ll b/llvm/test/CodeGen/AVR/hardware-mul.ll index 40e36f9566f89..edfdc7e64e8f5 100644 --- a/llvm/test/CodeGen/AVR/hardware-mul.ll +++ b/llvm/test/CodeGen/AVR/hardware-mul.ll @@ -1,29 +1,40 @@ -; RUN: llc -mattr=mul,movw < %s -march=avr | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -mattr=mul,movw < %s -mtriple=avr | FileCheck %s ; Tests lowering of multiplication to hardware instructions. define i8 @mult8(i8 %a, i8 %b) { ; CHECK-LABEL: mult8: -; CHECK: muls r22, r24 -; CHECK: clr r1 -; CHECK: mov r24, r0 +; CHECK: ; %bb.0: +; CHECK-NEXT: muls r22, r24 +; CHECK-NEXT: clr r1 +; CHECK-NEXT: mov r24, r0 +; CHECK-NEXT: ret %mul = mul i8 %b, %a ret i8 %mul } define i16 @mult16(i16 %a, i16 %b) { ; CHECK-LABEL: mult16: -; CHECK: muls r22, r25 -; CHECK: mov r20, r0 -; CHECK: mul r22, r24 -; CHECK: mov r21, r0 -; CHECK: mov r18, r1 -; CHECK: clr r1 -; CHECK: add r18, r20 -; CHECK: muls r23, r24 -; CHECK: clr r1 -; CHECK: add r18, r0 -; :TODO: finish after reworking shift instructions +; CHECK: ; %bb.0: +; CHECK-NEXT: muls r22, r25 +; CHECK-NEXT: mov r25, r0 +; CHECK-NEXT: clr r1 +; CHECK-NEXT: mul r22, r24 +; CHECK-NEXT: mov r20, r0 +; CHECK-NEXT: mov r18, r1 +; CHECK-NEXT: clr r1 +; CHECK-NEXT: add r18, r25 +; CHECK-NEXT: muls r23, r24 +; CHECK-NEXT: clr r1 +; CHECK-NEXT: add r18, r0 +; CHECK-NEXT: mov r19, r18 +; CHECK-NEXT: clr r18 +; CHECK-NEXT: mov r24, r20 +; CHECK-NEXT: clr r25 +; CHECK-NEXT: or r24, r18 +; CHECK-NEXT: or r25, r19 +; CHECK-NEXT: ret %mul = mul nsw i16 %b, %a ret i16 %mul } diff --git a/llvm/test/CodeGen/AVR/inline-asm/inline-asm-invalid.ll b/llvm/test/CodeGen/AVR/inline-asm/inline-asm-invalid.ll index 98b0709fcc3e0..6e8260fb4dcb4 100644 --- a/llvm/test/CodeGen/AVR/inline-asm/inline-asm-invalid.ll +++ b/llvm/test/CodeGen/AVR/inline-asm/inline-asm-invalid.ll @@ -1,4 +1,6 @@ -; RUN: not llc < %s -march=avr -no-integrated-as 2>&1 | FileCheck %s +; RUN: not llc < %s -mtriple=avr -no-integrated-as 2>&1 | FileCheck %s +; RUN: not llc < %s -mtriple=avr -mcpu=avr6 -filetype=obj 2>&1 \ +; RUN: FileCheck %s --check-prefix=AVR6 define void @foo(i16 %a) { ; CHECK: error: invalid operand in inline asm: 'jl ${0:l}' @@ -13,3 +15,9 @@ define void @foo1() { call i16 asm sideeffect ";; ${0:C}", "=d"() ret void } + +define void @foo2() { + ; AVR6: error: expected either Y or Z register + call void asm sideeffect "ldd r24, X+2", ""() + ret void +} diff --git a/llvm/test/CodeGen/AVR/inline-asm/inline-asm3.ll b/llvm/test/CodeGen/AVR/inline-asm/inline-asm3.ll index 824be39f3a7d6..bf25c74cbf38f 100644 --- a/llvm/test/CodeGen/AVR/inline-asm/inline-asm3.ll +++ b/llvm/test/CodeGen/AVR/inline-asm/inline-asm3.ll @@ -227,14 +227,12 @@ define void @add_e_i8(i8 signext %0, i8 signext %1) { ; CHECK-NEXT: mov r30, r22 ; CHECK-NEXT: mov r22, r24 ; CHECK-NEXT: mov r26, r22 -; CHECK-NEXT: mov r27, r23 ; CHECK-NEXT: ;APP ; CHECK-NEXT: mov r26, r26 ; CHECK-NEXT: add r26, r30 ; CHECK-NEXT: ;NO_APP -; CHECK-NEXT: mov r24, r26 -; CHECK-NEXT: ; kill: def $r22 killed $r22 killed $r23r22 ; CHECK-NEXT: mov r20, r30 +; CHECK-NEXT: mov r24, r26 ; CHECK-NEXT: rcall foo8 ; CHECK-NEXT: ret %3 = tail call i8 asm sideeffect "mov $0, $1\0Aadd $0, $2", "=e,e,e"(i8 %0, i8 %1) @@ -294,7 +292,6 @@ define void @add_w_i8(i8 signext %0, i8 signext %1) { ; CHECK-NEXT: mov r24, r30 ; CHECK-NEXT: add r24, r26 ; CHECK-NEXT: ;NO_APP -; CHECK-NEXT: ; kill: def $r24 killed $r24 killed $r25r24 ; CHECK-NEXT: mov r22, r30 ; CHECK-NEXT: mov r20, r26 ; CHECK-NEXT: rcall foo8 @@ -345,9 +342,6 @@ define void @add_xyz_i8(i8 signext %0, i8 signext %1) { ; CHECK-NEXT: ;NO_APP ; CHECK-NEXT: mov r24, r30 ; CHECK-NEXT: mov r25, r31 -; CHECK-NEXT: ; kill: def $r24 killed $r24 killed $r25r24 -; CHECK-NEXT: ; kill: def $r22 killed $r22 killed $r23r22 -; CHECK-NEXT: ; kill: def $r20 killed $r20 killed $r21r20 ; CHECK-NEXT: rcall foo8 ; CHECK-NEXT: pop r29 ; CHECK-NEXT: pop r28 diff --git a/llvm/test/CodeGen/AVR/load.ll b/llvm/test/CodeGen/AVR/load.ll index 53748b3b100b9..efc7549adb651 100644 --- a/llvm/test/CodeGen/AVR/load.ll +++ b/llvm/test/CodeGen/AVR/load.ll @@ -140,3 +140,18 @@ while.end: ; preds = %while.body, %entry %r.0.lcssa = phi i16 [ 0, %entry ], [ %add, %while.body ] ret i16 %r.0.lcssa } + +define ptr addrspace(1) @load16_postinc_progmem(ptr addrspace(1) readonly %0) { +; CHECK-LABEL: load16_postinc_progmem: +; CHECK: movw r30, [[REG0:r[0-9]+]] +; CHECK: lpm [[REG1:r[0-9]+]], Z+ +; CHECK: lpm [[REG1:r[0-9]+]], Z +; CHECK: call foo +; CHECK: adiw [[REG0:r[0-9]+]], 2 + %2 = load i16, ptr addrspace(1) %0, align 1 + tail call addrspace(1) void @foo(i16 %2) + %3 = getelementptr inbounds i16, ptr addrspace(1) %0, i16 1 + ret ptr addrspace(1) %3 +} + +declare void @foo(i16) diff --git a/llvm/test/CodeGen/AVR/lpmx.ll b/llvm/test/CodeGen/AVR/lpmx.ll index e84caf40d0709..db8fdb458d01c 100644 --- a/llvm/test/CodeGen/AVR/lpmx.ll +++ b/llvm/test/CodeGen/AVR/lpmx.ll @@ -20,8 +20,8 @@ define i16 @foo0(i16 %a) addrspace(1) { ; CHECK-O0-NEXT: out 62, r29 ; CHECK-O0-NEXT: out 63, r0 ; CHECK-O0-NEXT: out 61, r28 -; CHECK-O0-NEXT: std Y+1, r24 ; CHECK-O0-NEXT: std Y+2, r25 +; CHECK-O0-NEXT: std Y+1, r24 ; CHECK-O0-NEXT: ldd r30, Y+1 ; CHECK-O0-NEXT: ldd r31, Y+2 ; CHECK-O0-NEXT: lsl r30 @@ -52,8 +52,8 @@ define i16 @foo0(i16 %a) addrspace(1) { ; CHECK-O3-NEXT: out 62, r29 ; CHECK-O3-NEXT: out 63, r0 ; CHECK-O3-NEXT: out 61, r28 -; CHECK-O3-NEXT: std Y+1, r24 ; CHECK-O3-NEXT: std Y+2, r25 +; CHECK-O3-NEXT: std Y+1, r24 ; CHECK-O3-NEXT: lsl r24 ; CHECK-O3-NEXT: rol r25 ; CHECK-O3-NEXT: subi r24, lo8(-(arr0)) @@ -92,8 +92,8 @@ define i8 @foo1(i16 %a) addrspace(1) { ; CHECK-O0-NEXT: out 62, r29 ; CHECK-O0-NEXT: out 63, r0 ; CHECK-O0-NEXT: out 61, r28 -; CHECK-O0-NEXT: std Y+1, r24 ; CHECK-O0-NEXT: std Y+2, r25 +; CHECK-O0-NEXT: std Y+1, r24 ; CHECK-O0-NEXT: ldd r30, Y+1 ; CHECK-O0-NEXT: ldd r31, Y+2 ; CHECK-O0-NEXT: subi r30, lo8(-(arr1)) @@ -121,8 +121,8 @@ define i8 @foo1(i16 %a) addrspace(1) { ; CHECK-O3-NEXT: out 62, r29 ; CHECK-O3-NEXT: out 63, r0 ; CHECK-O3-NEXT: out 61, r28 -; CHECK-O3-NEXT: std Y+1, r24 ; CHECK-O3-NEXT: std Y+2, r25 +; CHECK-O3-NEXT: std Y+1, r24 ; CHECK-O3-NEXT: subi r24, lo8(-(arr1)) ; CHECK-O3-NEXT: sbci r25, hi8(-(arr1)) ; CHECK-O3-NEXT: movw r30, r24 diff --git a/llvm/test/CodeGen/AVR/pr43443-ctor-alias.ll b/llvm/test/CodeGen/AVR/pr43443-ctor-alias.ll index 7aa680353b8b7..9175e8c73fe78 100644 --- a/llvm/test/CodeGen/AVR/pr43443-ctor-alias.ll +++ b/llvm/test/CodeGen/AVR/pr43443-ctor-alias.ll @@ -30,8 +30,8 @@ define void @_ZN3fooC2Ev(%struct.foo* dereferenceable(1) %this) { ; CHECK-NEXT: out 62, r29 ; CHECK-NEXT: out 63, r0 ; CHECK-NEXT: out 61, r28 -; CHECK-NEXT: std Y+1, r24 ; CHECK-NEXT: std Y+2, r25 +; CHECK-NEXT: std Y+1, r24 ; CHECK-NEXT: adiw r28, 2 ; CHECK-NEXT: in r0, 63 ; CHECK-NEXT: cli diff --git a/llvm/test/CodeGen/AVR/pseudo/ELPMBRdZ.mir b/llvm/test/CodeGen/AVR/pseudo/ELPMBRdZ.mir new file mode 100644 index 0000000000000..29dbd79c652a2 --- /dev/null +++ b/llvm/test/CodeGen/AVR/pseudo/ELPMBRdZ.mir @@ -0,0 +1,45 @@ +# RUN: llc -mtriple=avr -mattr=+elpm -mattr=+elpmx -start-before=greedy %s -o - \ +# RUN: | FileCheck %s +# RUN: llc -mtriple=avr -mattr=+elpm -mattr=-elpmx -start-before=greedy %s -o - \ +# RUN: | FileCheck --check-prefix=NOX %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 +# lead to undefined behavior). + +--- | + target triple = "avr--" + define void @test_elpmbrdz() { + entry: + ret void + } +... + +--- +name: test_elpmbrdz +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $r31r30 + + ; CHECK-LABEL: test_elpmbrdz + ; CHECK: ; %bb.0: + ; CHECK-NEXT: ldi r24, 1 + ; CHECK-NEXT: out + ; CHECK-NEXT: elpm r31, Z + ; CHECK-NEXT: ret + + ; NOX-LABEL: test_elpmbrdz + ; NOX: ; %bb.0: + ; NOX-NEXT: ldi r24, 1 + ; NOX-NEXT: out + ; NOX-NEXT: elpm + ; NOX-NEXT: mov r31, r0 + ; NOX-NEXT: ret + + %1:zreg = COPY killed $r31r30 + %2:ld8 = LDIRdK 1 + %3:gpr8 = ELPMBRdZ %1, %2, implicit-def dead $r0 + $r31 = COPY %3 + RET implicit $r31 +... diff --git a/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir b/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir index d4469fb467ac6..7a6bab0a03d68 100644 --- a/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir +++ b/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir @@ -1,4 +1,11 @@ -# 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 @@ -10,6 +17,10 @@ entry: ret void } + define void @test_elpmwrdz_2() { + entry: + ret void + } ... --- @@ -24,9 +35,79 @@ body: | ; 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 +... diff --git a/llvm/test/CodeGen/AVR/pseudo/LPMBRdZ.mir b/llvm/test/CodeGen/AVR/pseudo/LPMBRdZ.mir new file mode 100644 index 0000000000000..6eaa9435220ea --- /dev/null +++ b/llvm/test/CodeGen/AVR/pseudo/LPMBRdZ.mir @@ -0,0 +1,40 @@ +# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+lpmx -start-before=greedy %s -o - \ +# RUN: | FileCheck %s +# RUN: llc -mtriple=avr -mattr=+lpm -mattr=-lpmx -start-before=greedy %s -o - \ +# RUN: | FileCheck --check-prefix=NOX %s + +# This test checks the expansion of the 8-bit LPMBRdZ pseudo instruction and that +# the register allocator won't use R31R30 as an output register (which would +# lead to undefined behavior). + +--- | + target triple = "avr--" + define void @test_lpmbrdz() { + entry: + ret void + } +... + +--- +name: test_lpmbrdz +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $r31r30 + + ; CHECK-LABEL: test_lpmbrdz: + ; CHECK: ; %bb.0: + ; CHECK-NEXT: lpm r30, Z + ; CHECK-NEXT: ret + + ; NOX-LABEL: test_lpmbrdz + ; NOX: ; %bb.0: + ; NOX-NEXT: lpm + ; NOX-NEXT: mov r30, r0 + ; NOX-NEXT: ret + + %1:zreg = COPY killed $r31r30 + %2:gpr8 = LPMBRdZ %1, implicit-def dead $r0 + $r30 = COPY %2 + RET implicit $r30 +... diff --git a/llvm/test/CodeGen/AVR/pseudo/OUTWARr.mir b/llvm/test/CodeGen/AVR/pseudo/OUTWARr.mir index 3152085cd14e5..8b9c0fdc28f61 100644 --- 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 @@ body: | 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/ROLBRdR1.mir b/llvm/test/CodeGen/AVR/pseudo/ROLBRdR1.mir new file mode 100644 index 0000000000000..10f70413ebf28 --- /dev/null +++ b/llvm/test/CodeGen/AVR/pseudo/ROLBRdR1.mir @@ -0,0 +1,24 @@ +# RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s + +# This test checks the expansion of the 8-bit ROLB (rotate) pseudo instruction. + +--- | + target triple = "avr--" + define void @test_rolbrd() { + entry: + ret void + } +... + +--- +name: test_rolbrd +body: | + bb.0.entry: + liveins: $r14 + + ; CHECK-LABEL: test_rolbrd + ; CHECK: $r14 = ADDRdRr killed $r14, killed $r14, implicit-def $sreg + ; CHECK-NEXT: $r14 = ADCRdRr $r14, $r1, implicit-def dead $sreg, implicit killed $sreg + + $r14 = ROLBRdR1 $r14, implicit-def $sreg, implicit $r1 +... diff --git a/llvm/test/CodeGen/AVR/pseudo/ROLBRdR17.mir b/llvm/test/CodeGen/AVR/pseudo/ROLBRdR17.mir new file mode 100644 index 0000000000000..2a26d18c65240 --- /dev/null +++ b/llvm/test/CodeGen/AVR/pseudo/ROLBRdR17.mir @@ -0,0 +1,24 @@ +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mattr=+avrtiny %s -o - | FileCheck %s + +# This test checks the expansion of the 8-bit ROLB (rotate) pseudo instruction +# on AVRTiny. + +--- | + target triple = "avr--" + define void @test_rolbrd() { + entry: + ret void + } +... + +--- +name: test_rolbrd +body: | + bb.0.entry: + liveins: $r24 + + ; CHECK-LABEL: test_rolbrd + ; CHECK: $r24 = ADDRdRr killed $r24, killed $r24, implicit-def $sreg + ; CHECK-NEXT: $r24 = ADCRdRr $r24, $r17, implicit-def dead $sreg, implicit killed $sreg + $r24 = ROLBRdR17 $r24, implicit-def $sreg, implicit $r17 +... diff --git a/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir b/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir deleted file mode 100644 index bd3b5b74114f1..0000000000000 --- a/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir +++ /dev/null @@ -1,29 +0,0 @@ -# RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s - -# This test checks the expansion of the 8-bit ROLB (rotate) pseudo instruction. - ---- | - target triple = "avr--" - define void @test_rolbrd() { - entry: - ret void - } -... - ---- -name: test_rolbrd -body: | - bb.0.entry: - liveins: $r14 - - ; CHECK-LABEL: test_rolbrd - - ; CHECK: $r14 = ADDRdRr killed $r14, killed $r14, implicit-def $sreg - ; CHECK-NEXT: $r14 = ADCRdRr $r14, $r1, implicit-def dead $sreg, implicit killed $sreg - $r14 = ROLBRd $r14, $r1, implicit-def $sreg - - ; avrtiny variant - ; CHECK: $r14 = ADDRdRr killed $r14, killed $r14, implicit-def $sreg - ; CHECK-NEXT: $r14 = ADCRdRr $r14, $r17, implicit-def dead $sreg, implicit killed $sreg - $r14 = ROLBRd $r14, $r17, implicit-def $sreg -... diff --git a/llvm/test/CodeGen/AVR/pseudo/RORBrd.mir b/llvm/test/CodeGen/AVR/pseudo/RORBrd.mir new file mode 100644 index 0000000000000..d0f84b8a39f37 --- /dev/null +++ b/llvm/test/CodeGen/AVR/pseudo/RORBrd.mir @@ -0,0 +1,25 @@ +# RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s + +# This test checks the expansion of the 8-bit RORB (rotate) pseudo instruction. + +--- | + target triple = "avr--" + define void @test_rorbrd() { + entry: + ret void + } +... + +--- +name: test_rorbrd +body: | + bb.0.entry: + liveins: $r14 + + ; CHECK-LABEL: test_rorbrd + ; CHECK: BST $r14, 0, implicit-def $sreg + ; CHECK-NEXT: $r14 = RORRd $r14, implicit-def $sreg, implicit $sreg + ; CHECK-NEXT: $r14 = BLD $r14, 7, implicit $sreg + + $r14 = RORBRd $r14, implicit-def $sreg +... diff --git a/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir b/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir index 535296d53b37e..89b124da11833 100644 --- a/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir +++ b/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir @@ -1,5 +1,8 @@ # RUN: llc -O0 -run-pass=avr-expand-pseudo -verify-machineinstrs %s -o - | FileCheck %s -# RUN: llc -O0 -run-pass=avr-expand-pseudo -verify-machineinstrs -mattr=avrtiny %s -o - | FileCheck %s --check-prefix=CHECK-TINY +# RUN: llc -O0 -run-pass=avr-expand-pseudo -verify-machineinstrs -mattr=avrtiny %s -o - \ +# RUN: | FileCheck %s --check-prefix=CHECK-TINY +# RUN: llc -O0 -run-pass=avr-expand-pseudo -verify-machineinstrs -mattr=lowbytefirst %s -o - \ +# RUN: | FileCheck %s --check-prefix=CHECK-XMEGA --- | target triple = "avr--" @@ -17,29 +20,37 @@ body: | ; CHECK-LABEL: test ; Small displacement (<63): - ; CHECK: STDPtrQRr $r29r28, 3, $r0 - ; CHECK-NEXT: STDPtrQRr $r29r28, 4, $r1 - ; CHECK-TINY: $r28 = SUBIRdK killed $r28, 253, implicit-def $sreg - ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 255, implicit-def $sreg, implicit killed $sreg - ; CHECK-TINY-NEXT: early-clobber $r29r28 = STPtrPiRr killed $r29r28, $r0, 0 - ; CHECK-TINY-NEXT: early-clobber $r29r28 = STPtrPiRr killed $r29r28, $r1, 0 - ; CHECK-TINY-NEXT: $r28 = SUBIRdK killed $r28, 5, implicit-def $sreg - ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 0, implicit-def $sreg, implicit killed $sreg + ; CHECK: STDPtrQRr $r29r28, 4, $r1 + ; CHECK-NEXT: STDPtrQRr $r29r28, 3, $r0 + ; CHECK-XMEGA: STDPtrQRr $r29r28, 3, $r0 + ; CHECK-XMEGA-NEXT: STDPtrQRr $r29r28, 4, $r1 + ; CHECK-TINY: $r28 = SUBIRdK killed $r28, 253, implicit-def $sreg + ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 255, implicit-def $sreg, implicit killed $sreg + ; CHECK-TINY-NEXT: early-clobber $r29r28 = STPtrPiRr killed $r29r28, $r0, 0 + ; CHECK-TINY-NEXT: early-clobber $r29r28 = STPtrPiRr killed $r29r28, $r1, 0 + ; CHECK-TINY-NEXT: $r28 = SUBIRdK killed $r28, 5, implicit-def $sreg + ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 0, implicit-def $sreg, implicit killed $sreg STDWPtrQRr $r29r28, 3, $r1r0 ; Small displacement where the destination register is killed: - ; CHECK: STDPtrQRr $r29r28, 3, $r0 - ; CHECK-NEXT: STDPtrQRr killed $r29r28, 4, $r1 + ; CHECK-NEXT: STDPtrQRr $r29r28, 4, $r1 + ; CHECK-NEXT: STDPtrQRr killed $r29r28, 3, $r0 + ; CHECK-XMEGA-NEXT: STDPtrQRr $r29r28, 3, $r0 + ; CHECK-XMEGA-NEXT: STDPtrQRr killed $r29r28, 4, $r1 STDWPtrQRr killed $r29r28, 3, $r1r0 ; Small displacement where the source register is killed: - ; CHECK: STDPtrQRr $r29r28, 3, killed $r0 - ; CHECK-NEXT: STDPtrQRr $r29r28, 4, killed $r1 + ; CHECK: STDPtrQRr $r29r28, 4, killed $r1 + ; CHECK-NEXT: STDPtrQRr $r29r28, 3, killed $r0 + ; CHECK-XMEGA: STDPtrQRr $r29r28, 3, killed $r0 + ; CHECK-XMEGA-NEXT: STDPtrQRr $r29r28, 4, killed $r1 STDWPtrQRr $r29r28, 3, killed $r1r0 ; Small displacement, near the limit (=62): - ; CHECK: STDPtrQRr $r29r28, 62, $r0 - ; CHECK-NEXT: STDPtrQRr $r29r28, 63, $r1 + ; CHECK: STDPtrQRr $r29r28, 63, $r1 + ; CHECK-NEXT: STDPtrQRr $r29r28, 62, $r0 + ; CHECK-XMEGA: STDPtrQRr $r29r28, 62, $r0 + ; CHECK-XMEGA-NEXT: STDPtrQRr $r29r28, 63, $r1 STDWPtrQRr $r29r28, 62, $r1r0 ; Large displacement (>=63): diff --git a/llvm/test/CodeGen/AVR/pseudo/STSWKRr.mir b/llvm/test/CodeGen/AVR/pseudo/STSWKRr.mir index 96a648b5622e2..cd45febd012ae 100644 --- 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 @@ body: | 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 ... diff --git a/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir b/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir index b6449ae1d438c..3350e6cdf34ef 100644 --- a/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir +++ b/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir @@ -1,5 +1,8 @@ # RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s -# RUN: llc -O0 -run-pass=avr-expand-pseudo -mattr=avrtiny %s -o - | FileCheck %s --check-prefix=CHECK-TINY +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mattr=avrtiny %s -o - \ +# RUN: | FileCheck %s --check-prefix=CHECK-TINY +# RUN: llc -O0 -run-pass=avr-expand-pseudo -mattr=lowbytefirst %s -o - \ +# RUN: | FileCheck %s --check-prefix=CHECK-XMEGA # This test checks the expansion of the 16-bit STSWRdK pseudo instruction. @@ -18,14 +21,18 @@ body: | liveins: $r31r30, $r17r16 ; CHECK-LABEL: test_stwptrrr + ; CHECK: STDPtrQRr $r31r30, 1, $r17 + ; CHECK-NEXT: STPtrRr $r31r30, $r16 - ; CHECK: STPtrRr $r31r30, $r16 - ; CHECK-NEXT: STDPtrQRr $r31r30, 1, $r17 + ; CHECK-TINY-LABEL: test_stwptrrr + ; CHECK-TINY: $r31r30 = STPtrPiRr killed $r31r30, $r16, 0 + ; CHECK-TINY-NEXT: $r31r30 = STPtrPiRr killed $r31r30, $r17, 0 + ; CHECK-TINY-NEXT: $r30 = SUBIRdK killed $r30, 2, implicit-def $sreg + ; CHECK-TINY-NEXT: $r31 = SBCIRdK killed $r31, 0, implicit-def $sreg, implicit killed $sreg - ; CHECK-TINY: $r31r30 = STPtrPiRr killed $r31r30, $r16, 0 - ; CHECK-TINY-NEXT: $r31r30 = STPtrPiRr killed $r31r30, $r17, 0 - ; CHECK-TINY-NEXT: $r30 = SUBIRdK killed $r30, 2, implicit-def $sreg - ; CHECK-TINY-NEXT: $r31 = SBCIRdK killed $r31, 0, implicit-def $sreg, implicit killed $sreg + ; CHECK-XMEGA-LABEL: test_stwptrrr + ; CHECK-XMEGA: STPtrRr $r31r30, $r16 + ; CHECK-XMEGA-NEXT: STDPtrQRr $r31r30, 1, $r17 STWPtrRr $r31r30, $r17r16 ... diff --git a/llvm/test/CodeGen/AVR/rot.ll b/llvm/test/CodeGen/AVR/rot.ll index 210031588005d..1a6b917af95b7 100644 --- a/llvm/test/CodeGen/AVR/rot.ll +++ b/llvm/test/CodeGen/AVR/rot.ll @@ -1,58 +1,75 @@ -; RUN: llc < %s -march=avr | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s -mtriple=avr | FileCheck %s +; RUN: llc < %s -mtriple=avr -mattr=+avrtiny | FileCheck --check-prefix=TINY %s -; Bit rotation tests. - -; CHECK-LABEL: rol8: define i8 @rol8(i8 %val, i8 %amt) { - ; CHECK: andi r22, 7 - - ; CHECK-NEXT: dec r22 - ; CHECK-NEXT: brmi .LBB0_2 - -; CHECK-NEXT: .LBB0_1: - ; CHECK-NEXT: lsl r24 - ; CHECK-NEXT: adc r24, r1 - ; CHECK-NEXT: dec r22 - ; CHECK-NEXT: brpl .LBB0_1 - -; CHECK-NEXT: .LBB0_2: - ; CHECK-NEXT: ret +; CHECK-LABEL: rol8: +; CHECK: ; %bb.0: +; CHECK-NEXT: andi r22, 7 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brmi .LBB0_2 +; CHECK-NEXT: .LBB0_1: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brpl .LBB0_1 +; CHECK-NEXT: .LBB0_2: +; CHECK-NEXT: ret +; +; TINY-LABEL: rol8: +; TINY: ; %bb.0: +; TINY-NEXT: andi r22, 7 +; TINY-NEXT: dec r22 +; TINY-NEXT: brmi .LBB0_2 +; TINY-NEXT: .LBB0_1: ; =>This Inner Loop Header: Depth=1 +; TINY-NEXT: lsl r24 +; TINY-NEXT: adc r24, r17 +; TINY-NEXT: dec r22 +; TINY-NEXT: brpl .LBB0_1 +; TINY-NEXT: .LBB0_2: +; TINY-NEXT: ret %mod = urem i8 %amt, 8 - %inv = sub i8 8, %mod %parta = shl i8 %val, %mod %partb = lshr i8 %val, %inv - %rotl = or i8 %parta, %partb - ret i8 %rotl } -; CHECK-LABEL: ror8: define i8 @ror8(i8 %val, i8 %amt) { - ; CHECK: andi r22, 7 - - ; CHECK-NEXT: dec r22 - ; CHECK-NEXT: brmi .LBB1_2 - -; CHECK-NEXT: .LBB1_1: - ; CHECK-NEXT: bst r24, 0 - ; CHECK-NEXT: ror r24 - ; CHECK-NEXT: bld r24, 7 - ; CHECK-NEXT: dec r22 - ; CHECK-NEXT: brpl .LBB1_1 - -; CHECK-NEXT: .LBB1_2: - ; CHECK-NEXT: ret +; CHECK-LABEL: ror8: +; CHECK: ; %bb.0: +; CHECK-NEXT: andi r22, 7 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brmi .LBB1_2 +; CHECK-NEXT: .LBB1_1: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brpl .LBB1_1 +; CHECK-NEXT: .LBB1_2: +; CHECK-NEXT: ret +; +; TINY-LABEL: ror8: +; TINY: ; %bb.0: +; TINY-NEXT: andi r22, 7 +; TINY-NEXT: dec r22 +; TINY-NEXT: brmi .LBB1_2 +; TINY-NEXT: .LBB1_1: ; =>This Inner Loop Header: Depth=1 +; TINY-NEXT: bst r24, 0 +; TINY-NEXT: ror r24 +; TINY-NEXT: bld r24, 7 +; TINY-NEXT: dec r22 +; TINY-NEXT: brpl .LBB1_1 +; TINY-NEXT: .LBB1_2: +; TINY-NEXT: ret %mod = urem i8 %amt, 8 - %inv = sub i8 8, %mod %parta = lshr i8 %val, %mod %partb = shl i8 %val, %inv - %rotr = or i8 %parta, %partb - ret i8 %rotr } diff --git a/llvm/test/CodeGen/AVR/rotate.ll b/llvm/test/CodeGen/AVR/rotate.ll new file mode 100644 index 0000000000000..79ff7928b3a39 --- /dev/null +++ b/llvm/test/CodeGen/AVR/rotate.ll @@ -0,0 +1,270 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s -mtriple=avr | FileCheck %s + +define i8 @rotl8_1(i8 %x) { +; CHECK-LABEL: rotl8_1: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 1) + ret i8 %0 +} + +define i8 @rotl8_3(i8 %x) { +; CHECK-LABEL: rotl8_3: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: swap r24 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 3) + ret i8 %0 +} + +define i8 @rotl8_5(i8 %x) { +; CHECK-LABEL: rotl8_5: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: swap r24 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 5) + ret i8 %0 +} + +define i8 @rotl8_7(i8 %x) { +; CHECK-LABEL: rotl8_7: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 7) + ret i8 %0 +} + +define i8 @rotl8_dyn(i8 %x, i8 %y) { +; CHECK-LABEL: rotl8_dyn: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: andi r22, 7 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brmi .LBB4_2 +; CHECK-NEXT: .LBB4_1: ; %start +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brpl .LBB4_1 +; CHECK-NEXT: .LBB4_2: ; %start +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 %y) + ret i8 %0 +} + +define i8 @rotr8_1(i8 %x) { +; CHECK-LABEL: rotr8_1: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 1) + ret i8 %0 +} + +define i8 @rotr8_3(i8 %x) { +; CHECK-LABEL: rotr8_3: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: swap r24 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 3) + ret i8 %0 +} + +define i8 @rotr8_5(i8 %x) { +; CHECK-LABEL: rotr8_5: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: swap r24 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 5) + ret i8 %0 +} + +define i8 @rotr8_7(i8 %x) { +; CHECK-LABEL: rotr8_7: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 7) + ret i8 %0 +} + +define i8 @rotr8_dyn(i8 %x, i8 %y) { +; CHECK-LABEL: rotr8_dyn: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: andi r22, 7 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brmi .LBB9_2 +; CHECK-NEXT: .LBB9_1: ; %start +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: dec r22 +; CHECK-NEXT: brpl .LBB9_1 +; CHECK-NEXT: .LBB9_2: ; %start +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshr.i8(i8 %x, i8 %x, i8 %y) + ret i8 %0 +} + +define i16 @rotl16(i16 %x) { +; CHECK-LABEL: rotl16: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: mov r18, r24 +; CHECK-NEXT: mov r19, r25 +; CHECK-NEXT: lsl r18 +; CHECK-NEXT: rol r19 +; CHECK-NEXT: lsl r18 +; CHECK-NEXT: rol r19 +; CHECK-NEXT: mov r24, r25 +; CHECK-NEXT: swap r24 +; CHECK-NEXT: andi r24, 15 +; CHECK-NEXT: clr r25 +; CHECK-NEXT: lsr r24 +; CHECK-NEXT: lsr r24 +; CHECK-NEXT: or r24, r18 +; CHECK-NEXT: or r25, r19 +; CHECK-NEXT: ret +start: + %0 = call i16 @llvm.fshl.i16(i16 %x, i16 %x, i16 2) + ret i16 %0 +} + +define i16 @rotr16(i16 %x) { +; CHECK-LABEL: rotr16: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: mov r18, r24 +; CHECK-NEXT: mov r19, r25 +; CHECK-NEXT: lsr r19 +; CHECK-NEXT: ror r18 +; CHECK-NEXT: lsr r19 +; CHECK-NEXT: ror r18 +; CHECK-NEXT: mov r25, r24 +; CHECK-NEXT: swap r25 +; CHECK-NEXT: andi r25, 240 +; CHECK-NEXT: clr r24 +; CHECK-NEXT: lsl r25 +; CHECK-NEXT: lsl r25 +; CHECK-NEXT: or r24, r18 +; CHECK-NEXT: or r25, r19 +; CHECK-NEXT: ret +start: + %0 = call i16 @llvm.fshr.i16(i16 %x, i16 %x, i16 2) + ret i16 %0 +} + +define i32 @rotl32(i32 %x) { +; CHECK-LABEL: rotl32: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: mov r20, r22 +; CHECK-NEXT: mov r21, r23 +; CHECK-NEXT: lsl r20 +; CHECK-NEXT: rol r21 +; CHECK-NEXT: lsl r20 +; CHECK-NEXT: rol r21 +; CHECK-NEXT: mov r18, r24 +; CHECK-NEXT: mov r19, r25 +; CHECK-NEXT: mov r18, r19 +; CHECK-NEXT: swap r18 +; CHECK-NEXT: andi r18, 15 +; CHECK-NEXT: clr r19 +; CHECK-NEXT: lsr r18 +; CHECK-NEXT: lsr r18 +; CHECK-NEXT: or r18, r20 +; CHECK-NEXT: or r19, r21 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: rol r25 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: rol r25 +; CHECK-NEXT: mov r22, r23 +; CHECK-NEXT: swap r22 +; CHECK-NEXT: andi r22, 15 +; CHECK-NEXT: clr r23 +; CHECK-NEXT: lsr r22 +; CHECK-NEXT: lsr r22 +; CHECK-NEXT: or r24, r22 +; CHECK-NEXT: or r25, r23 +; CHECK-NEXT: mov r22, r18 +; CHECK-NEXT: mov r23, r19 +; CHECK-NEXT: ret +start: + %0 = call i32 @llvm.fshl.i32(i32 %x, i32 %x, i32 2) + ret i32 %0 +} + +define i32 @rotr32(i32 %x) { +; CHECK-LABEL: rotr32: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: mov r20, r22 +; CHECK-NEXT: mov r21, r23 +; CHECK-NEXT: lsr r21 +; CHECK-NEXT: ror r20 +; CHECK-NEXT: lsr r21 +; CHECK-NEXT: ror r20 +; CHECK-NEXT: mov r18, r24 +; CHECK-NEXT: mov r19, r25 +; CHECK-NEXT: mov r19, r18 +; CHECK-NEXT: swap r19 +; CHECK-NEXT: andi r19, 240 +; CHECK-NEXT: clr r18 +; CHECK-NEXT: lsl r19 +; CHECK-NEXT: lsl r19 +; CHECK-NEXT: or r18, r20 +; CHECK-NEXT: or r19, r21 +; CHECK-NEXT: lsr r25 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: lsr r25 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: mov r23, r22 +; CHECK-NEXT: swap r23 +; CHECK-NEXT: andi r23, 240 +; CHECK-NEXT: clr r22 +; CHECK-NEXT: lsl r23 +; CHECK-NEXT: lsl r23 +; CHECK-NEXT: or r24, r22 +; CHECK-NEXT: or r25, r23 +; CHECK-NEXT: mov r22, r18 +; CHECK-NEXT: mov r23, r19 +; CHECK-NEXT: ret +start: + %0 = call i32 @llvm.fshr.i32(i32 %x, i32 %x, i32 2) + ret i32 %0 +} + +declare i8 @llvm.fshl.i8(i8, i8, i8) +declare i8 @llvm.fshr.i8(i8, i8, i8) + +declare i16 @llvm.fshl.i16(i16, i16, i16) +declare i16 @llvm.fshr.i16(i16, i16, i16) + +declare i32 @llvm.fshl.i32(i32, i32, i32) +declare i32 @llvm.fshr.i32(i32, i32, i32) diff --git a/llvm/test/CodeGen/AVR/shift-expand.ll b/llvm/test/CodeGen/AVR/shift-expand.ll index 7baba06586a70..be075e5d30394 100644 --- a/llvm/test/CodeGen/AVR/shift-expand.ll +++ b/llvm/test/CodeGen/AVR/shift-expand.ll @@ -8,8 +8,17 @@ target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8" target triple = "avr" -define i32 @shl(i32 %value, i32 %amount) addrspace(1) { -; CHECK-LABEL: @shl( +define i16 @shl16(i16 %value, i16 %amount) addrspace(1) { +; CHECK-LABEL: @shl16( +; CHECK-NEXT: [[RESULT:%.*]] = shl i16 [[VALUE:%.*]], [[AMOUNT:%.*]] +; CHECK-NEXT: ret i16 [[RESULT]] +; + %result = shl i16 %value, %amount + ret i16 %result +} + +define i32 @shl32(i32 %value, i32 %amount) addrspace(1) { +; CHECK-LABEL: @shl32( ; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AMOUNT:%.*]] to i8 ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 ; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]] @@ -28,8 +37,39 @@ define i32 @shl(i32 %value, i32 %amount) addrspace(1) { ret i32 %result } -define i32 @lshr(i32 %value, i32 %amount) addrspace(1) { -; CHECK-LABEL: @lshr( +define i40 @shl40(i40 %value, i40 %amount) addrspace(1) { +; CHECK-LABEL: @shl40( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i40 [[AMOUNT:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]] +; CHECK: shift.loop: +; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP4:%.*]] = phi i40 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1 +; CHECK-NEXT: [[TMP6]] = shl i40 [[TMP4]], 1 +; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0 +; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]] +; CHECK: shift.done: +; CHECK-NEXT: [[TMP8:%.*]] = phi i40 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: ret i40 [[TMP8]] +; + %result = shl i40 %value, %amount + ret i40 %result +} + +; ------------------------------------------------------------------------------ + +define i16 @lshr16(i16 %value, i16 %amount) addrspace(1) { +; CHECK-LABEL: @lshr16( +; CHECK-NEXT: [[RESULT:%.*]] = lshr i16 [[VALUE:%.*]], [[AMOUNT:%.*]] +; CHECK-NEXT: ret i16 [[RESULT]] +; + %result = lshr i16 %value, %amount + ret i16 %result +} + +define i32 @lshr32(i32 %value, i32 %amount) addrspace(1) { +; CHECK-LABEL: @lshr32( ; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AMOUNT:%.*]] to i8 ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 ; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]] @@ -48,42 +88,73 @@ define i32 @lshr(i32 %value, i32 %amount) addrspace(1) { ret i32 %result } -define i32 @ashr(i32 %0, i32 %1) addrspace(1) { -; CHECK-LABEL: @ashr( -; CHECK-NEXT: [[TMP3:%.*]] = trunc i32 [[TMP1:%.*]] to i8 -; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i8 [[TMP3]], 0 -; CHECK-NEXT: br i1 [[TMP4]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]] +define i40 @lshr40(i40 %value, i40 %amount) addrspace(1) { +; CHECK-LABEL: @lshr40( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i40 [[AMOUNT:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]] ; CHECK: shift.loop: -; CHECK-NEXT: [[TMP5:%.*]] = phi i8 [ [[TMP3]], [[TMP2:%.*]] ], [ [[TMP7:%.*]], [[SHIFT_LOOP]] ] -; CHECK-NEXT: [[TMP6:%.*]] = phi i32 [ [[TMP0:%.*]], [[TMP2]] ], [ [[TMP8:%.*]], [[SHIFT_LOOP]] ] -; CHECK-NEXT: [[TMP7]] = sub i8 [[TMP5]], 1 -; CHECK-NEXT: [[TMP8]] = ashr i32 [[TMP6]], 1 -; CHECK-NEXT: [[TMP9:%.*]] = icmp eq i8 [[TMP7]], 0 -; CHECK-NEXT: br i1 [[TMP9]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]] +; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP4:%.*]] = phi i40 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1 +; CHECK-NEXT: [[TMP6]] = lshr i40 [[TMP4]], 1 +; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0 +; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]] ; CHECK: shift.done: -; CHECK-NEXT: [[TMP10:%.*]] = phi i32 [ [[TMP0]], [[TMP2]] ], [ [[TMP8]], [[SHIFT_LOOP]] ] -; CHECK-NEXT: ret i32 [[TMP10]] +; CHECK-NEXT: [[TMP8:%.*]] = phi i40 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: ret i40 [[TMP8]] ; - %3 = ashr i32 %0, %1 - ret i32 %3 + %result = lshr i40 %value, %amount + ret i40 %result } -; This function is not modified because it is not an i32. -define i40 @shl40(i40 %value, i40 %amount) addrspace(1) { -; CHECK-LABEL: @shl40( -; CHECK-NEXT: [[RESULT:%.*]] = shl i40 [[VALUE:%.*]], [[AMOUNT:%.*]] -; CHECK-NEXT: ret i40 [[RESULT]] +; ------------------------------------------------------------------------------ + +define i16 @ashr16(i16 %value, i16 %amount) addrspace(1) { +; CHECK-LABEL: @ashr16( +; CHECK-NEXT: [[RESULT:%.*]] = ashr i16 [[VALUE:%.*]], [[AMOUNT:%.*]] +; CHECK-NEXT: ret i16 [[RESULT]] ; - %result = shl i40 %value, %amount - ret i40 %result + %result = ashr i16 %value, %amount + ret i16 %result } -; This function isn't either, although perhaps it should. -define i24 @shl24(i24 %value, i24 %amount) addrspace(1) { -; CHECK-LABEL: @shl24( -; CHECK-NEXT: [[RESULT:%.*]] = shl i24 [[VALUE:%.*]], [[AMOUNT:%.*]] -; CHECK-NEXT: ret i24 [[RESULT]] +define i32 @ashr32(i32 %value, i32 %amount) addrspace(1) { +; CHECK-LABEL: @ashr32( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[AMOUNT:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]] +; CHECK: shift.loop: +; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP4:%.*]] = phi i32 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1 +; CHECK-NEXT: [[TMP6]] = ashr i32 [[TMP4]], 1 +; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0 +; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]] +; CHECK: shift.done: +; CHECK-NEXT: [[TMP8:%.*]] = phi i32 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: ret i32 [[TMP8]] +; + %result = ashr i32 %value, %amount + ret i32 %result +} + +define i40 @ashr40(i40 %value, i40 %amount) addrspace(1) { +; CHECK-LABEL: @ashr40( +; CHECK-NEXT: [[TMP1:%.*]] = trunc i40 [[AMOUNT:%.*]] to i8 +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 0 +; CHECK-NEXT: br i1 [[TMP2]], label [[SHIFT_DONE:%.*]], label [[SHIFT_LOOP:%.*]] +; CHECK: shift.loop: +; CHECK-NEXT: [[TMP3:%.*]] = phi i8 [ [[TMP1]], [[TMP0:%.*]] ], [ [[TMP5:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP4:%.*]] = phi i40 [ [[VALUE:%.*]], [[TMP0]] ], [ [[TMP6:%.*]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: [[TMP5]] = sub i8 [[TMP3]], 1 +; CHECK-NEXT: [[TMP6]] = ashr i40 [[TMP4]], 1 +; CHECK-NEXT: [[TMP7:%.*]] = icmp eq i8 [[TMP5]], 0 +; CHECK-NEXT: br i1 [[TMP7]], label [[SHIFT_DONE]], label [[SHIFT_LOOP]] +; CHECK: shift.done: +; CHECK-NEXT: [[TMP8:%.*]] = phi i40 [ [[VALUE]], [[TMP0]] ], [ [[TMP6]], [[SHIFT_LOOP]] ] +; CHECK-NEXT: ret i40 [[TMP8]] ; - %result = shl i24 %value, %amount - ret i24 %result + %result = ashr i40 %value, %amount + ret i40 %result } diff --git a/llvm/test/CodeGen/AVR/shift.ll b/llvm/test/CodeGen/AVR/shift.ll index 7d9198b1d7655..c0abc77c9b14a 100644 --- a/llvm/test/CodeGen/AVR/shift.ll +++ b/llvm/test/CodeGen/AVR/shift.ll @@ -54,10 +54,36 @@ define i64 @shift_i64_i64(i64 %a, i64 %b) { ; CHECK: ; %bb.0: ; CHECK-NEXT: push r16 ; CHECK-NEXT: push r17 -; CHECK-NEXT: mov r16, r10 -; CHECK-NEXT: mov r17, r11 -; CHECK-NEXT: andi r17, 0 -; CHECK-NEXT: rcall __ashldi3 +; CHECK-NEXT: mov r30, r10 +; CHECK-NEXT: mov r31, r11 +; CHECK-NEXT: cpi r30, 0 +; CHECK-NEXT: breq .LBB3_3 +; CHECK-NEXT: ; %bb.1: ; %shift.loop.preheader +; CHECK-NEXT: mov r27, r1 +; CHECK-NEXT: mov r16, r1 +; CHECK-NEXT: mov r17, r1 +; CHECK-NEXT: .LBB3_2: ; %shift.loop +; CHECK-NEXT: ; =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: mov r31, r21 +; CHECK-NEXT: lsl r31 +; CHECK-NEXT: mov r26, r1 +; CHECK-NEXT: rol r26 +; CHECK-NEXT: lsl r22 +; CHECK-NEXT: rol r23 +; CHECK-NEXT: rol r24 +; CHECK-NEXT: rol r25 +; CHECK-NEXT: or r24, r16 +; CHECK-NEXT: or r25, r17 +; CHECK-NEXT: or r22, r26 +; CHECK-NEXT: or r23, r27 +; CHECK-NEXT: lsl r18 +; CHECK-NEXT: rol r19 +; CHECK-NEXT: rol r20 +; CHECK-NEXT: rol r21 +; CHECK-NEXT: dec r30 +; CHECK-NEXT: cpi r30, 0 +; CHECK-NEXT: brne .LBB3_2 +; CHECK-NEXT: .LBB3_3: ; %shift.done ; CHECK-NEXT: pop r17 ; CHECK-NEXT: pop r16 ; CHECK-NEXT: ret diff --git a/llvm/test/CodeGen/AVR/shift32.ll b/llvm/test/CodeGen/AVR/shift32.ll index 5ccc80d5165d7..6f984bd192f20 100644 --- a/llvm/test/CodeGen/AVR/shift32.ll +++ b/llvm/test/CodeGen/AVR/shift32.ll @@ -208,12 +208,12 @@ define void @shl_i32_16_ptr(i32 %a, ptr %ptr) { ; CHECK-LABEL: shl_i32_16_ptr: ; CHECK: ; %bb.0: ; CHECK-NEXT: movw r30, r20 -; CHECK-NEXT: std Z+2, r22 ; CHECK-NEXT: std Z+3, r23 +; CHECK-NEXT: std Z+2, r22 ; CHECK-NEXT: ldi r24, 0 ; CHECK-NEXT: ldi r25, 0 -; CHECK-NEXT: st Z, r24 ; CHECK-NEXT: std Z+1, r25 +; CHECK-NEXT: st Z, r24 ; CHECK-NEXT: ret %res = shl i32 %a, 16 store i32 %res, ptr %ptr diff --git a/llvm/test/CodeGen/AVR/store.ll b/llvm/test/CodeGen/AVR/store.ll index 81bad77538745..8bfbcb0934ed1 100644 --- a/llvm/test/CodeGen/AVR/store.ll +++ b/llvm/test/CodeGen/AVR/store.ll @@ -9,8 +9,8 @@ define void @store8(i8* %x, i8 %y) { define void @store16(i16* %x, i16 %y) { ; CHECK-LABEL: store16: -; CHECK: st {{[YZ]}}, r22 ; CHECK: std {{[YZ]}}+1, r23 +; CHECK: st {{[YZ]}}, r22 store i16 %y, i16* %x ret void } @@ -36,8 +36,8 @@ define void @store8nodisp(i8* %x, i8 %y) { define void @store16disp(i16* %x, i16 %y) { ; CHECK-LABEL: store16disp: -; CHECK: std {{[YZ]}}+62, r22 ; CHECK: std {{[YZ]}}+63, r23 +; CHECK: std {{[YZ]}}+62, r22 %arrayidx = getelementptr inbounds i16, i16* %x, i16 31 store i16 %y, i16* %arrayidx ret void @@ -48,8 +48,8 @@ define void @store16nodisp(i16* %x, i16 %y) { ; CHECK: subi r24, 192 ; CHECK: sbci r25, 255 ; CHECK: movw r30, r24 -; CHECK: st {{[YZ]}}, r22 ; CHECK: std {{[YZ]}}+1, r23 +; CHECK: st {{[YZ]}}, r22 %arrayidx = getelementptr inbounds i16, i16* %x, i16 32 store i16 %y, i16* %arrayidx ret void @@ -75,8 +75,8 @@ while.end: ; preds = %while.body, %entry define void @store16postinc(i16* %x, i16 %y) { ; CHECK-LABEL: store16postinc: -; CHECK: st {{[XYZ]}}+, {{.*}} -; CHECK: st {{[XYZ]}}+, {{.*}} +; CHECK: std {{[XYZ]}}+1, {{.*}} +; CHECK: st {{[XYZ]}}, {{.*}} entry: %tobool3 = icmp eq i16 %y, 0 br i1 %tobool3, label %while.end, label %while.body diff --git a/llvm/test/CodeGen/AVR/struct.ll b/llvm/test/CodeGen/AVR/struct.ll index 3d1eb83253c6f..1064236030e68 100644 --- a/llvm/test/CodeGen/AVR/struct.ll +++ b/llvm/test/CodeGen/AVR/struct.ll @@ -11,23 +11,23 @@ define void @foo10(%struct.s10* sret(%struct.s10) %0, i16 %1, i16 %2, i16 %3) ad ; CHECKA: ; %bb.0: ; CHECKA-NEXT: mov r30, r24 ; CHECKA-NEXT: mov r31, r25 -; CHECKA-NEXT: std Z+4, r22 ; CHECKA-NEXT: std Z+5, r23 -; CHECKA-NEXT: std Z+2, r20 +; CHECKA-NEXT: std Z+4, r22 ; CHECKA-NEXT: std Z+3, r21 -; CHECKA-NEXT: st Z, r18 +; CHECKA-NEXT: std Z+2, r20 ; CHECKA-NEXT: std Z+1, r19 +; CHECKA-NEXT: st Z, r18 ; CHECKA-NEXT: ret ; ; CHECKB-LABEL: foo10: ; CHECKB: ; %bb.0: ; CHECKB-NEXT: movw r30, r24 -; CHECKB-NEXT: std Z+4, r22 ; CHECKB-NEXT: std Z+5, r23 -; CHECKB-NEXT: std Z+2, r20 +; CHECKB-NEXT: std Z+4, r22 ; CHECKB-NEXT: std Z+3, r21 -; CHECKB-NEXT: st Z, r18 +; CHECKB-NEXT: std Z+2, r20 ; CHECKB-NEXT: std Z+1, r19 +; CHECKB-NEXT: st Z, r18 ; CHECKB-NEXT: ret %5 = getelementptr inbounds %struct.s10, %struct.s10* %0, i16 0, i32 0 store i16 %3, i16* %5 diff --git a/llvm/test/CodeGen/AVR/varargs.ll b/llvm/test/CodeGen/AVR/varargs.ll index 5bd5cba0a2635..00952af02adb5 100644 --- a/llvm/test/CodeGen/AVR/varargs.ll +++ b/llvm/test/CodeGen/AVR/varargs.ll @@ -42,16 +42,16 @@ define void @varargcall() { ; CHECK-LABEL: varargcall: ; CHECK: ldi [[REG1:r[0-9]+]], 191 ; CHECK: ldi [[REG2:r[0-9]+]], 223 -; CHECK: std Z+5, [[REG1]] ; CHECK: std Z+6, [[REG2]] +; CHECK: std Z+5, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 189 ; CHECK: ldi [[REG2:r[0-9]+]], 205 -; CHECK: std Z+3, [[REG1]] ; CHECK: std Z+4, [[REG2]] +; CHECK: std Z+3, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 205 ; CHECK: ldi [[REG2:r[0-9]+]], 171 -; CHECK: std Z+1, [[REG1]] ; CHECK: std Z+2, [[REG2]] +; CHECK: std Z+1, [[REG1]] ; CHECK: call ; CHECK: adiw r30, 6 tail call void (i16, ...) @var1223(i16 -21555, i16 -12867, i16 -8257) diff --git a/llvm/test/MC/AVR/elf_header.s b/llvm/test/MC/AVR/elf_header.s new file mode 100644 index 0000000000000..e82d86442eb21 --- /dev/null +++ b/llvm/test/MC/AVR/elf_header.s @@ -0,0 +1,74 @@ +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=at90s8515 %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,AVR2 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=attiny13a %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,AVR25 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=attiny167 %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,AVR35 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=atmega88 %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,AVR4 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=atmega16 %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,AVR5 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=atmega128 %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,AVR51 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=attiny817 %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,XM3 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=atxmega256a3u %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,XM6 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=atxmega256a3u %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,XM6 %s +; RUN: llvm-mc -filetype=obj -triple avr -mcpu=attiny10 %s -o - \ +; RUN: | llvm-readobj -h - | FileCheck --check-prefixes=ALL,TINY %s + +; ALL: ElfHeader { +; ALL-NEXT: Ident { +; ALL-NEXT: Magic: (7F 45 4C 46) +; ALL-NEXT: Class: 32-bit (0x1) +; ALL-NEXT: DataEncoding: LittleEndian (0x1) +; ALL-NEXT: FileVersion: 1 +; ALL-NEXT: OS/ABI: SystemV (0x0) +; ALL-NEXT: ABIVersion: 0 +; ALL-NEXT: Unused: (00 00 00 00 00 00 00) +; ALL-NEXT: } +; ALL-NEXT: Type: Relocatable (0x1) +; ALL-NEXT: Machine: EM_AVR (0x53) +; ALL-NEXT: Version: 1 +; ALL-NEXT: Entry: 0x0 +; ALL-NEXT: ProgramHeaderOffset: 0x0 +; ALL-NEXT: SectionHeaderOffset: 0x5C + +; AVR2: Flags [ (0x82) +; AVR2-NEXT: EF_AVR_ARCH_AVR2 (0x2) + +; AVR25: Flags [ (0x99) +; AVR25-NEXT: EF_AVR_ARCH_AVR25 (0x19) + +; AVR35: Flags [ (0xA3) +; AVR35-NEXT: EF_AVR_ARCH_AVR35 (0x23) + +; AVR4: Flags [ (0x84) +; AVR4-NEXT: EF_AVR_ARCH_AVR4 (0x4) + +; AVR5: Flags [ (0x85) +; AVR5-NEXT: EF_AVR_ARCH_AVR5 (0x5) + +; AVR51: Flags [ (0xB3) +; AVR51-NEXT: EF_AVR_ARCH_AVR51 (0x33) + +; XM3: Flags [ (0xE7) +; XM3-NEXT: EF_AVR_ARCH_XMEGA3 (0x67) + +; XM6: Flags [ (0xEA) +; XM6-NEXT: EF_AVR_ARCH_XMEGA6 (0x6A) + +; TINY: Flags [ (0xE4) +; TINY-NEXT: EF_AVR_ARCH_AVRTINY (0x64) + +; ALL: EF_AVR_LINKRELAX_PREPARED (0x80) +; ALL-NEXT: ] +; ALL-NEXT: HeaderSize: 52 +; ALL-NEXT: ProgramHeaderEntrySize: 0 +; ALL-NEXT: ProgramHeaderCount: 0 +; ALL-NEXT: SectionHeaderEntrySize: 40 +; ALL-NEXT: SectionHeaderCount: 4 +; ALL-NEXT: StringTableSectionIndex: 1 +; ALL-NEXT: } diff --git a/llvm/test/MC/AVR/inst-brbc.s b/llvm/test/MC/AVR/inst-brbc.s index 2f92416a40700..4d7d684da4468 100644 --- a/llvm/test/MC/AVR/inst-brbc.s +++ b/llvm/test/MC/AVR/inst-brbc.s @@ -1,12 +1,24 @@ ; RUN: llvm-mc -triple avr -show-encoding < %s | FileCheck %s - +; RUN: llvm-mc -filetype=obj -triple avr < %s \ +; RUN: | llvm-objdump -d - | FileCheck --check-prefix=INST %s foo: brbc 3, .+8 brbc 0, .-16 + .short 0xf759 + .short 0xf752 + .short 0xf74c + .short 0xf4c7 ; CHECK: brvc .Ltmp0+8 ; encoding: [0bAAAAA011,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: .Ltmp0+8, kind: fixup_7_pcrel ; CHECK: brcc .Ltmp1-16 ; encoding: [0bAAAAA000,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: .Ltmp1-16, kind: fixup_7_pcrel + +; INST: 23 f4 brvc .+8 +; INST: c0 f7 brsh .-16 +; INST: 59 f7 brne .-42 +; INST: 52 f7 brpl .-44 +; INST: 4c f7 brge .-46 +; INST: c7 f4 brid .+48 diff --git a/llvm/test/MC/AVR/inst-brbs.s b/llvm/test/MC/AVR/inst-brbs.s index bf2ca31f88318..7987feeec654a 100644 --- a/llvm/test/MC/AVR/inst-brbs.s +++ b/llvm/test/MC/AVR/inst-brbs.s @@ -1,12 +1,24 @@ ; RUN: llvm-mc -triple avr -show-encoding < %s | FileCheck %s - +; RUN: llvm-mc -filetype=obj -triple avr < %s \ +; RUN: | llvm-objdump -d - | FileCheck --check-prefix=INST %s foo: brbs 3, .+8 brbs 0, .-12 + .short 0xf359 + .short 0xf352 + .short 0xf34c + .short 0xf077 ; CHECK: brvs .Ltmp0+8 ; encoding: [0bAAAAA011,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: .Ltmp0+8, kind: fixup_7_pcrel ; CHECK: brcs .Ltmp1-12 ; encoding: [0bAAAAA000,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: .Ltmp1-12, kind: fixup_7_pcrel + +; INST: 23 f0 brvs .+8 +; INST: d0 f3 brlo .-12 +; INST: 59 f3 breq .-42 +; INST: 52 f3 brmi .-44 +; INST: 4c f3 brlt .-46 +; INST: 77 f0 brie .+28 diff --git a/llvm/test/MC/AVR/inst-family-cond-branch.s b/llvm/test/MC/AVR/inst-family-cond-branch.s index c4edc18fb4020..dc36425a884f3 100644 --- a/llvm/test/MC/AVR/inst-family-cond-branch.s +++ b/llvm/test/MC/AVR/inst-family-cond-branch.s @@ -20,9 +20,9 @@ foo: ; CHECK: ; fixup A - offset: 0, value: baz, kind: fixup_7_pcrel ; INST-LABEL: : -; INST: breq .+0 -; INST: breq .+0 -; INST: breq .+0 +; INST: breq .-18 +; INST: breq .-12 +; INST: breq .-18 ; INST: breq .+0 ; BRNE @@ -40,9 +40,9 @@ foo: ; CHECK: brbc 1, bar ; encoding: [0bAAAAA001,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: bar, kind: fixup_7_pcrel -; INST: brne .+0 -; INST: brne .+0 -; INST: brne .+0 +; INST: brne .+10 +; INST: brne .+2 +; INST: brne .+10 ; INST: brne .+0 bar: @@ -62,9 +62,9 @@ bar: ; CHECK: ; fixup A - offset: 0, value: end, kind: fixup_7_pcrel ; INST-LABEL: : -; INST: brlo .+0 -; INST: brlo .+0 -; INST: brlo .+0 +; INST: brlo .+8 +; INST: brlo .+4 +; INST: brlo .+8 ; INST: brlo .+0 ; BRCC @@ -82,9 +82,9 @@ bar: ; CHECK: brcc baz ; encoding: [0bAAAAA000,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: baz, kind: fixup_7_pcrel -; INST: brsh .+0 -; INST: brsh .+0 -; INST: brsh .+0 +; INST: brsh .+66 +; INST: brsh .-22 +; INST: brsh .+66 ; INST: brsh .+0 ; BRSH @@ -99,8 +99,8 @@ bar: ; CHECK: brsh car ; encoding: [0bAAAAA000,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: car, kind: fixup_7_pcrel -; INST: brsh .+0 -; INST: brsh .+0 +; INST: brsh .+32 +; INST: brsh .+70 ; INST: brsh .+0 baz: @@ -118,8 +118,8 @@ baz: ; CHECK: ; fixup A - offset: 0, value: car, kind: fixup_7_pcrel ; INST-LABEL: : -; INST: brlo .+0 -; INST: brlo .+0 +; INST: brlo .+12 +; INST: brlo .+28 ; INST: brlo .+0 ; BRMI @@ -134,8 +134,8 @@ baz: ; CHECK: brmi car ; encoding: [0bAAAAA010,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: car, kind: fixup_7_pcrel -; INST: brmi .+0 -; INST: brmi .+0 +; INST: brmi .+66 +; INST: brmi .+58 ; INST: brmi .+0 ; BRPL @@ -150,8 +150,8 @@ baz: ; CHECK: brpl car ; encoding: [0bAAAAA010,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: car, kind: fixup_7_pcrel -; INST: brpl .+0 -; INST: brpl .+0 +; INST: brpl .-12 +; INST: brpl .+18 ; INST: brpl .+0 ; BRGE @@ -166,8 +166,8 @@ baz: ; CHECK: brge car ; encoding: [0bAAAAA100,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: car, kind: fixup_7_pcrel -; INST: brge .+0 -; INST: brge .+0 +; INST: brge .+50 +; INST: brge .+42 ; INST: brge .+0 car: @@ -184,8 +184,8 @@ car: ; CHECK: ; fixup A - offset: 0, value: end, kind: fixup_7_pcrel ; INST-LABEL: : -; INST: brlt .+0 -; INST: brlt .+0 +; INST: brlt .+16 +; INST: brlt .+2 ; INST: brlt .+0 ; BRHS @@ -200,8 +200,8 @@ car: ; CHECK: brhs just_another_label ; encoding: [0bAAAAA101,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: just_another_label, kind: fixup_7_pcrel -; INST: brhs .+0 -; INST: brhs .+0 +; INST: brhs .-66 +; INST: brhs .+14 ; INST: brhs .+0 ; BRHC @@ -216,8 +216,8 @@ car: ; CHECK: brhc just_another_label ; encoding: [0bAAAAA101,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: just_another_label, kind: fixup_7_pcrel -; INST: brhc .+0 -; INST: brhc .+0 +; INST: brhc .+12 +; INST: brhc .+14 ; INST: brhc .+0 ; BRTS @@ -232,8 +232,8 @@ car: ; CHECK: brts just_another_label ; encoding: [0bAAAAA110,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: just_another_label, kind: fixup_7_pcrel -; INST: brts .+0 -; INST: brts .+0 +; INST: brts .+18 +; INST: brts .+22 ; INST: brts .+0 just_another_label: @@ -250,8 +250,8 @@ just_another_label: ; CHECK: ; fixup A - offset: 0, value: end, kind: fixup_7_pcrel ; INST-LABEL: : -; INST: brtc .+0 -; INST: brtc .+0 +; INST: brtc .+52 +; INST: brtc .+50 ; INST: brtc .+0 ; BRVS @@ -266,8 +266,8 @@ just_another_label: ; CHECK: brvs end ; encoding: [0bAAAAA011,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: end, kind: fixup_7_pcrel -; INST: brvs .+0 -; INST: brvs .+0 +; INST: brvs .+18 +; INST: brvs .+32 ; INST: brvs .+0 ; BRVC @@ -282,8 +282,8 @@ just_another_label: ; CHECK: brvc end ; encoding: [0bAAAAA011,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: end, kind: fixup_7_pcrel -; INST: brvc .+0 -; INST: brvc .+0 +; INST: brvc .-28 +; INST: brvc .-62 ; INST: brvc .+0 ; BRIE @@ -298,8 +298,8 @@ just_another_label: ; CHECK: brie end ; encoding: [0bAAAAA111,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: end, kind: fixup_7_pcrel -; INST: brie .+0 -; INST: brie .+0 +; INST: brie .+20 +; INST: brie .+40 ; INST: brie .+0 ; BRID @@ -314,8 +314,8 @@ just_another_label: ; CHECK: brid end ; encoding: [0bAAAAA111,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: end, kind: fixup_7_pcrel -; INST: brid .+0 -; INST: brid .+0 +; INST: brid .+42 +; INST: brid .+62 ; INST: brid .+0 end: diff --git a/llvm/test/MC/AVR/inst-rcall.s b/llvm/test/MC/AVR/inst-rcall.s index 4e75620b147e7..006013aa6ea94 100644 --- a/llvm/test/MC/AVR/inst-rcall.s +++ b/llvm/test/MC/AVR/inst-rcall.s @@ -20,8 +20,8 @@ foo: ; CHECK: rcall .Ltmp3+46 ; encoding: [A,0b1101AAAA] ; CHECK: ; fixup A - offset: 0, value: .Ltmp3+46, kind: fixup_13_pcrel -; INST: rcall .+0 -; INST: rcall .+0 -; INST: rcall .+0 -; INST: rcall .+0 -; INST: rcall .-44 +; INST: 00 d0 rcall .+0 +; INST: fc df rcall .-8 +; INST: 06 d0 rcall .+12 +; INST: 17 d0 rcall .+46 +; INST: ea df rcall .-44 diff --git a/llvm/test/MC/AVR/inst-rjmp.s b/llvm/test/MC/AVR/inst-rjmp.s index 472d654ce3460..3dbac39e055dd 100644 --- a/llvm/test/MC/AVR/inst-rjmp.s +++ b/llvm/test/MC/AVR/inst-rjmp.s @@ -37,13 +37,13 @@ x: ; CHECK: rjmp x ; encoding: [A,0b1100AAAA] ; CHECK: ; fixup A - offset: 0, value: x, kind: fixup_13_pcrel -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+0 -; INST: rjmp .+30 +; INST: 01 c0 rjmp .+2 +; INST: ff cf rjmp .-2 +; INST: 00 c0 rjmp .+0 +; INST: 04 c0 rjmp .+8 +; INST: 00 c0 rjmp .+0 +; INST: 00 c0 rjmp .+0 +; INST: fe cf rjmp .-4 +; INST: fd cf rjmp .-6 +; INST: 00 c0 rjmp .+0 +; INST: 0f c0 rjmp .+30