From 3c4f5148829137b5bc30c982cb586b1791fc96e5 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sat, 7 Jan 2023 15:25:38 +0800 Subject: [PATCH 01/23] [AVR] Fix incorrect decoding of conditional branch instructions This patch fixes the inaccurate decoding of the offset operand of the conditional branch instructions. Reviewed By: aykevl Differential Revision: https://reviews.llvm.org/D140816 --- llvm/lib/Target/AVR/AVRInstrFormats.td | 4 ++ .../AVR/Disassembler/AVRDisassembler.cpp | 41 +++++++++++++++++++ llvm/test/MC/AVR/inst-brbc.s | 14 ++++++- llvm/test/MC/AVR/inst-brbs.s | 14 ++++++- 4 files changed, 71 insertions(+), 2 deletions(-) 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/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/test/MC/AVR/inst-brbc.s b/llvm/test/MC/AVR/inst-brbc.s index 2f92416a40700..bf9dba470f2aa 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: brvc .+0 +; INST: brsh .+0 +; INST: brne .-42 +; INST: brpl .-44 +; INST: brge .-46 +; INST: brid .+48 diff --git a/llvm/test/MC/AVR/inst-brbs.s b/llvm/test/MC/AVR/inst-brbs.s index bf2ca31f88318..ad4cc46871937 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: brvs .+0 +; INST: brlo .+0 +; INST: breq .-42 +; INST brmi .-44 +; INST brlt .-46 +; InST: brie .+28 From 6d0184a16ff9bebc9cdb605a982c0037e0a8f1f2 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 13 Jan 2023 17:37:00 +0800 Subject: [PATCH 02/23] [AVR][NFC] Remove redundant target feature PROGMEM The functionality of FeaturePROGMEM is all equivalant to FeatureLPM. Reviewed By: Chenbing.Zheng, aykevl Differential Revision: https://reviews.llvm.org/D141242 --- llvm/lib/Target/AVR/AVRAsmPrinter.cpp | 6 +++--- llvm/lib/Target/AVR/AVRDevices.td | 11 +++-------- llvm/lib/Target/AVR/AVRSubtarget.h | 2 -- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp index c625290751080..c0372e10ff9a3 100644 --- a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp +++ b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp @@ -259,9 +259,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..8ecb0a1e7b12e 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">; @@ -161,7 +156,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,13 +192,13 @@ def FamilyTiny FeatureSmallStack]>; def FamilyXMEGA3 : Family<"xmega3", - [FamilyAVR0, FeatureLPM, FeaturePROGMEM, FeatureIJMPCALL, + [FamilyAVR0, FeatureLPM, FeatureIJMPCALL, FeatureADDSUBIW, FeatureSRAM, FeatureJMPCALL, FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureBREAK]>; def FamilyXMEGA : Family<"xmega", - [FamilyAVR0, FeatureLPM, FeaturePROGMEM, FeatureIJMPCALL, + [FamilyAVR0, FeatureLPM, FeatureIJMPCALL, FeatureADDSUBIW, FeatureSRAM, FeatureJMPCALL, FeatureMultiplication, FeatureMOVW, FeatureLPMX, FeatureSPM, FeatureBREAK, FeatureEIJMPCALL, diff --git a/llvm/lib/Target/AVR/AVRSubtarget.h b/llvm/lib/Target/AVR/AVRSubtarget.h index 25046e6cf1eaf..78ffb4260de49 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; } @@ -128,7 +127,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; From 83205f569bdc5b0fc3763c938f46eaed9e4f5c0e Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 20 Jan 2023 10:34:08 +0800 Subject: [PATCH 03/23] [AVR][NFC] Refactor 'AVRAsmPrinter::PrintAsmOperand' Reviewed By: Chenbing.Zheng, aykevl Differential Revision: https://reviews.llvm.org/D142170 --- llvm/lib/Target/AVR/AVRAsmPrinter.cpp | 67 +++++++++++++-------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp index c0372e10ff9a3..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; } From e8a78a6362a18bed888ae10d40b8cbc4099b028e Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sat, 21 Jan 2023 21:42:25 +0800 Subject: [PATCH 04/23] [AVR] Optimize 16-bit comparison with a constant Fixes https://github.com/llvm/llvm-project/issues/30923 Reviewed By: jacquesguan, aykevl Differential Revision: https://reviews.llvm.org/D142281 --- llvm/lib/Target/AVR/AVRISelLowering.cpp | 26 ++++++- llvm/test/CodeGen/AVR/cmp.ll | 96 +++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index ee2c489179714..6c60d87c4704f 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -634,11 +634,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, 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 +} From 2f4a9d7171a313651948c87ac62364ca4d97a396 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 13 Feb 2023 19:46:02 +0800 Subject: [PATCH 05/23] [AVR] Fix inaccurate offsets in PC relative branch instructions In avr-gcc, the destination of "rjmp label + offset" is address 'label + offset', while destination of "rjmp . + offset" is 'address_of_rjmp + offset + 2'. Clang is in accordance with avr-gcc for "rjmp label + offset", but emits incorrect destination of "rjmp . + offset" to 'address_of_rjmp + offset', in which the expected offset 2 is missing. This patch fixes the above issue. Fixes https://github.com/llvm/llvm-project/issues/60019 Reviewed By: jacquesguan, aykevl Differential Revision: https://reviews.llvm.org/D143901 --- .../Target/AVR/MCTargetDesc/AVRAsmBackend.cpp | 6 ++ llvm/test/MC/AVR/inst-brbc.s | 12 +-- llvm/test/MC/AVR/inst-brbs.s | 12 +-- llvm/test/MC/AVR/inst-family-cond-branch.s | 80 +++++++++---------- llvm/test/MC/AVR/inst-rcall.s | 10 +-- llvm/test/MC/AVR/inst-rjmp.s | 20 ++--- 6 files changed, 73 insertions(+), 67 deletions(-) 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/test/MC/AVR/inst-brbc.s b/llvm/test/MC/AVR/inst-brbc.s index bf9dba470f2aa..4d7d684da4468 100644 --- a/llvm/test/MC/AVR/inst-brbc.s +++ b/llvm/test/MC/AVR/inst-brbc.s @@ -16,9 +16,9 @@ foo: ; CHECK: brcc .Ltmp1-16 ; encoding: [0bAAAAA000,0b111101AA] ; CHECK: ; fixup A - offset: 0, value: .Ltmp1-16, kind: fixup_7_pcrel -; INST: brvc .+0 -; INST: brsh .+0 -; INST: brne .-42 -; INST: brpl .-44 -; INST: brge .-46 -; INST: brid .+48 +; 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 ad4cc46871937..7987feeec654a 100644 --- a/llvm/test/MC/AVR/inst-brbs.s +++ b/llvm/test/MC/AVR/inst-brbs.s @@ -16,9 +16,9 @@ foo: ; CHECK: brcs .Ltmp1-12 ; encoding: [0bAAAAA000,0b111100AA] ; CHECK: ; fixup A - offset: 0, value: .Ltmp1-12, kind: fixup_7_pcrel -; INST: brvs .+0 -; INST: brlo .+0 -; INST: breq .-42 -; INST brmi .-44 -; INST brlt .-46 -; InST: brie .+28 +; 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 From 4d1912bb963462a964365b9526fcd759843930a0 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Thu, 23 Feb 2023 15:06:38 +0800 Subject: [PATCH 06/23] [AVR][MC] Add ELF flag 'EF_AVR_LINKRELAX_PREPARED' to OBJ files This is in accordance with avr-gcc, even '-mno-relax' is specified to avr-gcc, this flag will also be added to the output relocatables. With this flag set, the GNU ld will perform long call -> short call optimization for AVR, otherwise not. Fixes https://github.com/llvm/llvm-project/issues/54508 Reviewed By: MaskRay, jacquesguan, aykevl Differential Revision: https://reviews.llvm.org/D144617 --- lld/test/ELF/avr-flags.s | 6 +- lld/test/ELF/linkerscript/avr5.test | 2 +- .../AVR/MCTargetDesc/AVRELFStreamer.cpp | 1 + llvm/test/MC/AVR/elf_header.s | 74 +++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 llvm/test/MC/AVR/elf_header.s 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/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/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: } From 8dc25fad8ae26aeed6e11ab564057b7e2b2dd6e3 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Feb 2023 11:11:00 +0800 Subject: [PATCH 07/23] [AVR] Fix incorrect flags of livein registers when spilling them In AVRFrameLowering::spillCalleeSavedRegisters(), when a 16-bit livein register is spilled, two PUSH instructions are generated for the higher and lower 8-bit registers. But these two 8-bit registers are marked as killed in the two PUSH instructions, so any future use of them will cause a crash. This patch fixes the above issue by adding the two sub 8-bit registers to the livein list. Fixes https://github.com/llvm/llvm-project/issues/56423 Reviewed By: jacquesguan Differential Revision: https://reviews.llvm.org/D144720 --- llvm/lib/Target/AVR/AVRFrameLowering.cpp | 10 ++ llvm/test/CodeGen/AVR/bug-56423.ll | 135 +++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 llvm/test/CodeGen/AVR/bug-56423.ll 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/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 +} From 0b320f8f09e222a20f93051df2a1d61e00b2e843 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sun, 8 Jan 2023 20:35:23 +0800 Subject: [PATCH 08/23] [AVR] Fix incorrect expansion of the pseudo 'ELPMBRdZ' instruction The 'ELPM' instruction has three forms: -------------------------- | form | feature | | ----------- | -------- | | ELPM | hasELPM | | ELPM Rd, Z | hasELPMX | | ELPM Rd, Z+ | hasELPMX | -------------------------- The second form is always used in the expansion of the pseudo instruction 'ELPMBRdZ'. But for devices without ELPMX but only with ELPM, only the first form can be emitted. Reviewed By: jacquesguan Differential Revision: https://reviews.llvm.org/D141221 --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 19 +++++-- llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp | 2 + llvm/lib/Target/AVR/AVRInstrInfo.td | 10 ++-- llvm/test/CodeGen/AVR/elpm.ll | 54 ++++++++++++++++++-- llvm/test/CodeGen/AVR/pseudo/ELPMBRdZ.mir | 45 ++++++++++++++++ 5 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 llvm/test/CodeGen/AVR/pseudo/ELPMBRdZ.mir diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp index 2c97dea0bce03..06dc2b7c5b27b 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -871,11 +871,20 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { 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 (STI.hasELPMX()) { + auto MILB = buildMI(MBB, MBBI, AVR::ELPMRdZ) + .addReg(DstReg, RegState::Define) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + MILB.setMemRefs(MI.memoperands()); + } else { + // For the basic 'ELPM' instruction, its operand[0] is the implicit + // 'Z' register, and its operand[1] is the implicit 'R0' register. + auto MILB = buildMI(MBB, MBBI, AVR::ELPM); + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstReg, RegState::Define) + .addReg(AVR::R0, RegState::Kill); + MILB.setMemRefs(MI.memoperands()); + } MI.eraseFromParent(); return true; diff --git a/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp b/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp index 5511d53dfa312..03015a457a0d1 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. diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td index 05ee94be79263..c272711bb8663 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -1742,12 +1742,14 @@ let mayLoad = 1, hasSideEffects = 0 in { Requires<[HasELPMX]>; } + // This pseudo is combination of the OUT and ELPM instructions. + let Defs = [R0] in + def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p), + "elpmb\t$dst, $z, $p", []>, + Requires<[HasELPM]>; + // These pseudos are combination of the OUT and ELPM instructions. let Defs = [R31R30], hasSideEffects = 1 in { - def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p), - "elpmb\t$dst, $z, $p", []>, - Requires<[HasELPMX]>; - let Constraints = "@earlyclobber $dst" in def ELPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z, LD8:$p), "elpmw\t$dst, $z, $p", []>, diff --git a/llvm/test/CodeGen/AVR/elpm.ll b/llvm/test/CodeGen/AVR/elpm.ll index a322ab773014a..ba28bc814591d 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 @@ -129,9 +132,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: @@ -232,6 +235,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 +285,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/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 +... From 6582effc44b0dde23db61f7f48579cb4870a9c27 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 9 Jan 2023 12:05:00 +0800 Subject: [PATCH 09/23] [AVR] Do not emit 'LPM Rd, Z' on devices without FeatureLPMX The 'LPM' instruction has three forms: ------------------------ | form | feature | | ---------- | --------| | LPM | hasLPM | | LPM Rd, Z | hasLPMX | | LPM Rd, Z+ | hasLPMX | ------------------------ The second form is always selected in ISelDAGToDAG, even on devices without FeatureLPMX. This patch emits "LPM + MOV" on devices with only FeatureLPM. Reviewed By: jacquesguan Differential Revision: https://reviews.llvm.org/D141246 --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 33 ++++++++--- llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp | 3 +- llvm/lib/Target/AVR/AVRInstrInfo.td | 5 ++ llvm/test/CodeGen/AVR/elpm.ll | 58 ++++++++++++++++++++ llvm/test/CodeGen/AVR/pseudo/LPMBRdZ.mir | 40 ++++++++++++++ 5 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 llvm/test/CodeGen/AVR/pseudo/LPMBRdZ.mir diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp index 06dc2b7c5b27b..b29eb87a55a01 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -98,6 +98,8 @@ class AVRExpandPseudo : public MachineFunctionPass { // Common implementation of LPMWRdZ and ELPMWRdZ. bool expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt); + // Common implementation of LPMBRdZ and ELPMBRdZ. + bool expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt); }; char AVRExpandPseudo::ID = 0; @@ -858,28 +860,32 @@ 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 IsExt) { 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 HasX = IsExt ? 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 (IsExt) { + Register BankReg = MI.getOperand(2).getReg(); + buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg); + } // Load byte. - if (STI.hasELPMX()) { - auto MILB = buildMI(MBB, MBBI, AVR::ELPMRdZ) + if (HasX) { + unsigned Opc = IsExt ? 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' instruction, its operand[0] is the implicit + // For the basic ELPM/LPM instruction, its operand[0] is the implicit // 'Z' register, and its operand[1] is the implicit 'R0' register. - auto MILB = buildMI(MBB, MBBI, AVR::ELPM); + unsigned Opc = IsExt ? AVR::ELPM : AVR::LPM; + auto MILB = buildMI(MBB, MBBI, Opc); buildMI(MBB, MBBI, AVR::MOVRdRr) .addReg(DstReg, RegState::Define) .addReg(AVR::R0, RegState::Kill); @@ -890,6 +896,16 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { 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"); @@ -2437,6 +2453,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); diff --git a/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp b/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp index 03015a457a0d1..ed9fd6b04fb9a 100644 --- a/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp +++ b/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp @@ -400,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/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td index c272711bb8663..1e56f9447cb80 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -1690,6 +1690,11 @@ let canFoldAsLoad = 1, isReMaterializable = 1, mayLoad = 1, : F16<0b1001010111001000, (outs), (ins), "lpm", []>, Requires<[HasLPM]>; + // This pseudo is combination of LPM and MOV instructions. + let Defs = [R0] in + def LPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z), "lpmb\t$dst, $z", []>, + Requires<[HasLPM]>; + def LPMRdZ : FLPMX<0, 0, (outs GPR8 : $rd), diff --git a/llvm/test/CodeGen/AVR/elpm.ll b/llvm/test/CodeGen/AVR/elpm.ll index ba28bc814591d..2e989d1442ffd 100644 --- a/llvm/test/CodeGen/AVR/elpm.ll +++ b/llvm/test/CodeGen/AVR/elpm.ll @@ -152,6 +152,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 @@ -179,6 +197,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 @@ -206,6 +244,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 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 +... From 722c7b6b7febcd87b4fa53fd0a80e7f9736e5796 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 9 Jan 2023 16:15:41 +0800 Subject: [PATCH 10/23] [AVR] Fix incorrect expansion of pseudo instructions LPMWRdZ/ELPMWRdZ The 'ELPM' instruction has three forms: -------------------------- | form | feature | | ----------- | -------- | | ELPM | hasELPM | | ELPM Rd, Z | hasELPMX | | ELPM Rd, Z+ | hasELPMX | -------------------------- The second form is always used in the expansion of pseudo instructions LPMWRdZ/ELPMWRdZ. But for devices without ELPMX and with only ELPM, only the first form can be used. Reviewed By: aykevl, Miss_Grape Differential Revision: https://reviews.llvm.org/D141264 --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 113 +++++++++++++++---- llvm/lib/Target/AVR/AVRInstrInfo.td | 38 +++---- llvm/test/CodeGen/AVR/elpm.ll | 112 ++++++++++++++++++ llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir | 85 +++++++++++++- 4 files changed, 302 insertions(+), 46 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp index b29eb87a55a01..5cdd98da768c9 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -97,9 +97,9 @@ 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 IsExt); + bool expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsELPM); }; char AVRExpandPseudo::ID = 0; @@ -812,19 +812,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); @@ -833,18 +835,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; @@ -860,23 +925,23 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandLPMWELPMW(MBB, MBBI, true); } -bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt) { +bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsELPM) { MachineInstr &MI = *MBBI; Register DstReg = MI.getOperand(0).getReg(); Register SrcReg = MI.getOperand(1).getReg(); bool SrcIsKill = MI.getOperand(1).isKill(); const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); - bool HasX = IsExt ? STI.hasELPMX() : STI.hasLPMX(); + bool IsLPMRn = IsELPM ? STI.hasELPMX() : STI.hasLPMX(); // Set the I/O register RAMPZ for ELPM (out RAMPZ, rtmp). - if (IsExt) { + if (IsELPM) { Register BankReg = MI.getOperand(2).getReg(); buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg); } // Load byte. - if (HasX) { - unsigned Opc = IsExt ? AVR::ELPMRdZ : AVR::LPMRdZ; + if (IsLPMRn) { + unsigned Opc = IsELPM ? AVR::ELPMRdZ : AVR::LPMRdZ; auto MILB = buildMI(MBB, MBBI, Opc) .addReg(DstReg, RegState::Define) .addReg(SrcReg, getKillRegState(SrcIsKill)); @@ -884,7 +949,7 @@ bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt) { } else { // For the basic ELPM/LPM instruction, its operand[0] is the implicit // 'Z' register, and its operand[1] is the implicit 'R0' register. - unsigned Opc = IsExt ? AVR::ELPM : AVR::LPM; + unsigned Opc = IsELPM ? AVR::ELPM : AVR::LPM; auto MILB = buildMI(MBB, MBBI, Opc); buildMI(MBB, MBBI, AVR::MOVRdRr) .addReg(DstReg, RegState::Define) diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td index 1e56f9447cb80..e7f26f4d9f077 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -1690,10 +1690,15 @@ let canFoldAsLoad = 1, isReMaterializable = 1, mayLoad = 1, : F16<0b1001010111001000, (outs), (ins), "lpm", []>, Requires<[HasLPM]>; - // This pseudo is combination of LPM and MOV instructions. - let Defs = [R0] in - def LPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z), "lpmb\t$dst, $z", []>, - Requires<[HasLPM]>; + // These pseudo instructions are combination of the OUT and LPM instructions. + let Defs = [R0] in { + def LPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z), "lpmb\t$dst, $z", []>, + Requires<[HasLPM]>; + + let Constraints = "@earlyclobber $dst" in + def LPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z), "lpmw\t$dst, $z", []>, + Requires<[HasLPM]>; + } def LPMRdZ : FLPMX<0, 0, (outs GPR8 @@ -1713,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 @@ -1747,19 +1744,20 @@ let mayLoad = 1, hasSideEffects = 0 in { Requires<[HasELPMX]>; } - // This pseudo is combination of the OUT and ELPM instructions. - let Defs = [R0] in - def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p), - "elpmb\t$dst, $z, $p", []>, - Requires<[HasELPM]>; + // These pseudo instructions are combination of the OUT and ELPM instructions. + let Defs = [R0] in { + def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p), + "elpmb\t$dst, $z, $p", []>, + Requires<[HasELPM]>; - // These pseudos are combination of the OUT and ELPM instructions. - let Defs = [R31R30], hasSideEffects = 1 in { let Constraints = "@earlyclobber $dst" in def ELPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z, LD8:$p), "elpmw\t$dst, $z, $p", []>, - Requires<[HasELPMX]>; + Requires<[HasELPM]>; + } + // These pseudos are combination of the OUT and ELPM instructions. + let Defs = [R31R30], hasSideEffects = 1 in { def ELPMBRdZPi : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p), "elpmb\t$dst, $z+, $p", []>, Requires<[HasELPMX]>; diff --git a/llvm/test/CodeGen/AVR/elpm.ll b/llvm/test/CodeGen/AVR/elpm.ll index 2e989d1442ffd..21f956aa2cb9d 100644 --- a/llvm/test/CodeGen/AVR/elpm.ll +++ b/llvm/test/CodeGen/AVR/elpm.ll @@ -28,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 @@ -59,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 @@ -90,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 @@ -123,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 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 +... From d103fccbfa04dd77bb2fbd7ba624cdd649aa5e45 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sun, 9 Apr 2023 17:37:32 +0800 Subject: [PATCH 11/23] [AVR] Reject invalid LDD instruction with explicit error We should reject "ldd Rn, X" with explicit error message rather than "llvm_unreachable" in llvm's release build. Fixes https://github.com/llvm/llvm-project/issues/62012 Reviewed By: Miss_Grape Differential Revision: https://reviews.llvm.org/D147877 --- llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp | 5 +++-- llvm/test/CodeGen/AVR/inline-asm/inline-asm-invalid.ll | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) 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/inline-asm/inline-asm-invalid.ll b/llvm/test/CodeGen/AVR/inline-asm/inline-asm-invalid.ll index 98b0709fcc3e0..8d27c54392d90 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,5 @@ -; RUN: not llc < %s -march=avr -no-integrated-as 2>&1 | FileCheck %s +; RUN: not llc < %s -march=avr -mcpu=avr6 -filetype=obj -no-integrated-as 2>&1 \ +; RUN: | FileCheck %s define void @foo(i16 %a) { ; CHECK: error: invalid operand in inline asm: 'jl ${0:l}' @@ -13,3 +14,9 @@ define void @foo1() { call i16 asm sideeffect ";; ${0:C}", "=d"() ret void } + +define void @foo2() { + ; CHECK: error: expected either Y or Z register + call void asm sideeffect "ldd r24, X+2", ""() + ret void +} From ccd3588371987eecfc462cce19317d2030f05c7d Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 10 Apr 2023 11:13:07 +0800 Subject: [PATCH 12/23] [AVR][NFC] Fix errors in commit 6e57f68e41c92936b9ef3a4e6fb286e8805a9fbc --- llvm/test/CodeGen/AVR/inline-asm/inline-asm-invalid.ll | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 8d27c54392d90..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,5 +1,6 @@ -; RUN: not llc < %s -march=avr -mcpu=avr6 -filetype=obj -no-integrated-as 2>&1 \ -; RUN: | 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}' @@ -16,7 +17,7 @@ define void @foo1() { } define void @foo2() { - ; CHECK: error: expected either Y or Z register + ; AVR6: error: expected either Y or Z register call void asm sideeffect "ldd r24, X+2", ""() ret void } From 614a431069f934d30a2b3373c7ff97ca2ea493c5 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 7 Apr 2023 11:45:05 +0800 Subject: [PATCH 13/23] [AVR] Disable post increment load from program memory space We temporarily only allow post increment load/store from/to data memory, and disable post increment load from program space. Updates https://github.com/llvm/llvm-project/issues/59914 Reviewed By: mzh Differential Revision: https://reviews.llvm.org/D147761 --- llvm/lib/Target/AVR/AVRISelLowering.cpp | 6 ++++++ llvm/test/CodeGen/AVR/load.ll | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index 6c60d87c4704f..a03fb927f6704 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -1151,6 +1151,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; 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) From c5cadd179385c53550449c9055f20bab6cdc6a10 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sat, 14 Jan 2023 16:40:43 +0800 Subject: [PATCH 14/23] [AVR] Fix an issue of writing 16-bit ports For 16-bit ports, the normal devices reqiure writing high byte first and then low byte. But the XMEGA devices require the reverse order. Fixes https://github.com/llvm/llvm-project/issues/58395 Reviewed By: aykevl, jacquesguan Differential Revision: https://reviews.llvm.org/D141752 --- llvm/lib/Target/AVR/AVRDevices.td | 11 +- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 140 ++++++++++++------- llvm/lib/Target/AVR/AVRISelLowering.cpp | 10 +- llvm/lib/Target/AVR/AVRSubtarget.h | 2 + llvm/test/CodeGen/AVR/PR37143.ll | 4 +- llvm/test/CodeGen/AVR/alloca.ll | 6 +- llvm/test/CodeGen/AVR/atomics/load16.ll | 10 +- llvm/test/CodeGen/AVR/atomics/store.ll | 4 +- llvm/test/CodeGen/AVR/atomics/store16.ll | 4 +- llvm/test/CodeGen/AVR/call.ll | 26 ++-- llvm/test/CodeGen/AVR/dynalloca.ll | 10 +- llvm/test/CodeGen/AVR/lpmx.ll | 8 +- llvm/test/CodeGen/AVR/pr43443-ctor-alias.ll | 2 +- llvm/test/CodeGen/AVR/pseudo/OUTWARr.mir | 18 ++- llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir | 41 ++++-- llvm/test/CodeGen/AVR/pseudo/STSWKRr.mir | 9 +- llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir | 21 ++- llvm/test/CodeGen/AVR/shift32.ll | 4 +- llvm/test/CodeGen/AVR/store.ll | 10 +- llvm/test/CodeGen/AVR/struct.ll | 12 +- llvm/test/CodeGen/AVR/varargs.ll | 6 +- 21 files changed, 225 insertions(+), 133 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRDevices.td b/llvm/lib/Target/AVR/AVRDevices.td index 8ecb0a1e7b12e..f6b36dba7733b 100644 --- a/llvm/lib/Target/AVR/AVRDevices.td +++ b/llvm/lib/Target/AVR/AVRDevices.td @@ -120,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 " @@ -195,14 +201,15 @@ def FamilyXMEGA3 : Family<"xmega3", [FamilyAVR0, FeatureLPM, FeatureIJMPCALL, FeatureADDSUBIW, FeatureSRAM, FeatureJMPCALL, FeatureMultiplication, FeatureMOVW, FeatureLPMX, - FeatureBREAK]>; + FeatureBREAK, FeatureLowByteFirst]>; def FamilyXMEGA : Family<"xmega", [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 5cdd98da768c9..5b3bb9eed3eee 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -1057,18 +1057,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: { @@ -1076,26 +1073,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; @@ -1126,16 +1147,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(); @@ -1252,23 +1284,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(); @@ -1347,27 +1388,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()); diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index a03fb927f6704..dc975e3a440b0 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -1128,9 +1128,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; } diff --git a/llvm/lib/Target/AVR/AVRSubtarget.h b/llvm/lib/Target/AVR/AVRSubtarget.h index 78ffb4260de49..cabb95adaa604 100644 --- a/llvm/lib/Target/AVR/AVRSubtarget.h +++ b/llvm/lib/Target/AVR/AVRSubtarget.h @@ -81,6 +81,7 @@ 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; } @@ -134,6 +135,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/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/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/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/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/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/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/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) From 1c5fb0c9f72b5ee23cd0871dbbc243177c1db377 Mon Sep 17 00:00:00 2001 From: Patryk Wychowaniec Date: Sun, 4 Jun 2023 11:08:54 +0800 Subject: [PATCH 15/23] [AVR] Fix incorrect operands of pseudo instruction 'ROLBRd' Fixes https://github.com/llvm/llvm-project/issues/63098 Reviewed by: benshi001 Differential Revision: https://reviews.llvm.org/D152063 --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 4 +- llvm/lib/Target/AVR/AVRISelLowering.cpp | 4 - llvm/lib/Target/AVR/AVRInstrInfo.td | 9 +- llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir | 7 +- llvm/test/CodeGen/AVR/rotate.ll | 230 +++++++++++++++++++ 5 files changed, 240 insertions(+), 14 deletions(-) create mode 100644 llvm/test/CodeGen/AVR/rotate.ll diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp index 5b3bb9eed3eee..2f991ae47d4c7 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -1468,10 +1468,12 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { // multiple registers, but when we actually need to rotate stuff, we have // to explicitly add the carry bit. + const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); + MachineInstr &MI = *MBBI; unsigned OpShift, OpCarry; Register DstReg = MI.getOperand(0).getReg(); - Register ZeroReg = MI.getOperand(2).getReg(); + Register ZeroReg = STI.getZeroRegister(); bool DstIsDead = MI.getOperand(0).isDead(); bool DstIsKill = MI.getOperand(1).isKill(); OpShift = AVR::ADDRdRr; diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index dc975e3a440b0..e5afa734e4961 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -1751,7 +1751,6 @@ MachineBasicBlock *AVRTargetLowering::insertShift(MachineInstr &MI, unsigned Opc; const TargetRegisterClass *RC; bool HasRepeatedOperand = false; - bool HasZeroOperand = false; MachineFunction *F = BB->getParent(); MachineRegisterInfo &RI = F->getRegInfo(); const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); @@ -1788,7 +1787,6 @@ MachineBasicBlock *AVRTargetLowering::insertShift(MachineInstr &MI, case AVR::Rol8: Opc = AVR::ROLBRd; RC = &AVR::GPR8RegClass; - HasZeroOperand = true; break; case AVR::Rol16: Opc = AVR::ROLWRd; @@ -1850,8 +1848,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] diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td index e7f26f4d9f077..c49e3517e96d8 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -2029,12 +2029,15 @@ 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), + (ins GPR8 + : $src), "rolb\t$rd", - []>; + [(set i8 + : $rd, (AVRrol i8 + : $src)), + (implicit SREG)]>; def RORBRd : Pseudo<(outs GPR8 : $rd), diff --git a/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir b/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir index bd3b5b74114f1..023120b2bad2a 100644 --- a/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir +++ b/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir @@ -20,10 +20,5 @@ body: | ; 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 + $r14 = ROLBRd $r14, implicit-def $sreg ... diff --git a/llvm/test/CodeGen/AVR/rotate.ll b/llvm/test/CodeGen/AVR/rotate.ll new file mode 100644 index 0000000000000..100f4aac2a772 --- /dev/null +++ b/llvm/test/CodeGen/AVR/rotate.ll @@ -0,0 +1,230 @@ +; 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: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: ret +start: + %0 = call i8 @llvm.fshl.i8(i8 %x, i8 %x, i8 3) + 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 .LBB2_2 +; CHECK-NEXT: .LBB2_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 .LBB2_1 +; CHECK-NEXT: .LBB2_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: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; 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 3) + 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 .LBB5_2 +; CHECK-NEXT: .LBB5_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 .LBB5_1 +; CHECK-NEXT: .LBB5_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) From eeb4a656bfe765bd67d3dd5b3cc4ad3d02e1f7be Mon Sep 17 00:00:00 2001 From: Ben Shi <2283975856@qq.com> Date: Sun, 4 Jun 2023 11:22:10 +0800 Subject: [PATCH 16/23] [AVR][NFC][test] Suppement a test of the pseudo instruction RORBRd Reviewed By: aykevl, Patryk27 Differential Revision: https://reviews.llvm.org/D152087 --- llvm/test/CodeGen/AVR/pseudo/RORBrd.mir | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 llvm/test/CodeGen/AVR/pseudo/RORBrd.mir 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 +... From b0ad3bc58b8c6663e413e04628ee5e751b5f19d8 Mon Sep 17 00:00:00 2001 From: Ben Shi <2283975856@qq.com> Date: Mon, 5 Jun 2023 16:23:38 +0800 Subject: [PATCH 17/23] [AVR][NFC][test] Supplement more tests of 8-bit rotation Reviewed By: Patryk27, jacquesguan Differential Revision: https://reviews.llvm.org/D152129 --- llvm/test/CodeGen/AVR/rotate.ll | 112 +++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 8 deletions(-) diff --git a/llvm/test/CodeGen/AVR/rotate.ll b/llvm/test/CodeGen/AVR/rotate.ll index 100f4aac2a772..bf31fac0ed383 100644 --- a/llvm/test/CodeGen/AVR/rotate.ll +++ b/llvm/test/CodeGen/AVR/rotate.ll @@ -27,19 +27,61 @@ start: ret i8 %0 } +define i8 @rotl8_5(i8 %x) { +; CHECK-LABEL: rotl8_5: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; 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: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: adc r24, r1 +; 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 .LBB2_2 -; CHECK-NEXT: .LBB2_1: ; %start +; 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 .LBB2_1 -; CHECK-NEXT: .LBB2_2: ; %start +; 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) @@ -76,20 +118,74 @@ start: ret i8 %0 } +define i8 @rotr8_5(i8 %x) { +; CHECK-LABEL: rotr8_5: +; CHECK: ; %bb.0: ; %start +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; 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: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: bst r24, 0 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: bld r24, 7 +; 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 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 .LBB5_2 -; CHECK-NEXT: .LBB5_1: ; %start +; 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 .LBB5_1 -; CHECK-NEXT: .LBB5_2: ; %start +; 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) From 4f9f7c081222b1af445a919c66f9db397d6bf79f Mon Sep 17 00:00:00 2001 From: Ben Shi <2283975856@qq.com> Date: Sat, 10 Jun 2023 11:25:53 +0800 Subject: [PATCH 18/23] [AVR][NFC] Improve CodeGen tests Reviewed By: Patryk27 Differential Revision: https://reviews.llvm.org/D152605 --- llvm/test/CodeGen/AVR/hardware-mul.ll | 41 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/llvm/test/CodeGen/AVR/hardware-mul.ll b/llvm/test/CodeGen/AVR/hardware-mul.ll index 40e36f9566f89..7e72ead7204ff 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 r20, r0 +; CHECK-NEXT: clr r1 +; CHECK-NEXT: mul r22, r24 +; CHECK-NEXT: mov r21, r0 +; CHECK-NEXT: mov r18, r1 +; CHECK-NEXT: clr r1 +; CHECK-NEXT: add r18, r20 +; 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, r21 +; 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 } From 2f895d538f846e990f9754f34dc40b40686202e9 Mon Sep 17 00:00:00 2001 From: Ben Shi <2283975856@qq.com> Date: Sat, 10 Jun 2023 11:22:33 +0800 Subject: [PATCH 19/23] [AVR] Enable sub register liveness Reviewed By: Patryk27 Differential Revision: https://reviews.llvm.org/D152606 --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 19 +++++++++++++++++++ llvm/lib/Target/AVR/AVRInstrInfo.cpp | 13 +++++++++---- llvm/lib/Target/AVR/AVRSubtarget.h | 2 ++ llvm/test/CodeGen/AVR/hardware-mul.ll | 8 ++++---- .../CodeGen/AVR/inline-asm/inline-asm3.ll | 8 +------- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp index 2f991ae47d4c7..f9a013b6b153d 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); @@ -226,6 +227,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; @@ -247,6 +260,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)) { @@ -258,6 +274,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(); 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/AVRSubtarget.h b/llvm/lib/Target/AVR/AVRSubtarget.h index cabb95adaa604..5c7c600ebbf14 100644 --- a/llvm/lib/Target/AVR/AVRSubtarget.h +++ b/llvm/lib/Target/AVR/AVRSubtarget.h @@ -85,6 +85,8 @@ class AVRSubtarget : public AVRGenSubtargetInfo { 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 { diff --git a/llvm/test/CodeGen/AVR/hardware-mul.ll b/llvm/test/CodeGen/AVR/hardware-mul.ll index 7e72ead7204ff..edfdc7e64e8f5 100644 --- a/llvm/test/CodeGen/AVR/hardware-mul.ll +++ b/llvm/test/CodeGen/AVR/hardware-mul.ll @@ -18,19 +18,19 @@ define i16 @mult16(i16 %a, i16 %b) { ; CHECK-LABEL: mult16: ; CHECK: ; %bb.0: ; CHECK-NEXT: muls r22, r25 -; CHECK-NEXT: mov r20, r0 +; CHECK-NEXT: mov r25, r0 ; CHECK-NEXT: clr r1 ; CHECK-NEXT: mul r22, r24 -; CHECK-NEXT: mov r21, r0 +; CHECK-NEXT: mov r20, r0 ; CHECK-NEXT: mov r18, r1 ; CHECK-NEXT: clr r1 -; CHECK-NEXT: add r18, r20 +; 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, r21 +; CHECK-NEXT: mov r24, r20 ; CHECK-NEXT: clr r25 ; CHECK-NEXT: or r24, r18 ; CHECK-NEXT: or r25, r19 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 From b9b277b96558e9689cc13d76dad08932c4d98df4 Mon Sep 17 00:00:00 2001 From: Ben Shi <2283975856@qq.com> Date: Wed, 7 Jun 2023 17:59:02 +0800 Subject: [PATCH 20/23] [AVR] Fix incorrect expansion of pseudo instruction ROLBRd Since ROLBRd needs an implicit R1 (on AVR) or an implicit R17 (on AVRTiny), we split ROLBRd to ROLBRdR1 (on AVR) and ROLBRdR17 (on AVRTiny). Reviewed By: aykevl, Patryk27 Differential Revision: https://reviews.llvm.org/D152248 --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 22 +++-- llvm/lib/Target/AVR/AVRISelLowering.cpp | 8 +- llvm/lib/Target/AVR/AVRISelLowering.h | 3 +- llvm/lib/Target/AVR/AVRInstrInfo.td | 25 +++-- .../AVR/pseudo/{ROLBrd.mir => ROLBRdR1.mir} | 6 +- llvm/test/CodeGen/AVR/pseudo/ROLBRdR17.mir | 24 +++++ llvm/test/CodeGen/AVR/rot.ll | 93 +++++++++++-------- 7 files changed, 120 insertions(+), 61 deletions(-) rename llvm/test/CodeGen/AVR/pseudo/{ROLBrd.mir => ROLBRdR1.mir} (59%) create mode 100644 llvm/test/CodeGen/AVR/pseudo/ROLBRdR17.mir diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp index f9a013b6b153d..f257ccea6c50a 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -101,6 +101,8 @@ class AVRExpandPseudo : public MachineFunctionPass { 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; @@ -1479,20 +1481,17 @@ 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 // multiple registers, but when we actually need to rotate stuff, we have // to explicitly add the carry bit. - const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); - MachineInstr &MI = *MBBI; unsigned OpShift, OpCarry; Register DstReg = MI.getOperand(0).getReg(); - Register ZeroReg = STI.getZeroRegister(); + Register ZeroReg = MI.getOperand(3).getReg(); bool DstIsDead = MI.getOperand(0).isDead(); bool DstIsKill = MI.getOperand(1).isKill(); OpShift = AVR::ADDRdRr; @@ -1520,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 @@ -2604,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/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index e5afa734e4961..636d3e66fc2aa 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -1747,7 +1747,8 @@ 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; @@ -1785,7 +1786,7 @@ MachineBasicBlock *AVRTargetLowering::insertShift(MachineInstr &MI, RC = &AVR::DREGSRegClass; break; case AVR::Rol8: - Opc = AVR::ROLBRd; + Opc = Tiny ? AVR::ROLBRdR17 : AVR::ROLBRdR1; RC = &AVR::GPR8RegClass; break; case AVR::Rol16: @@ -2328,6 +2329,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. @@ -2342,7 +2344,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/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td index c49e3517e96d8..b9a04caf48721 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -2028,16 +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)]>; - - def ROLBRd : Pseudo<(outs GPR8 - : $rd), - (ins GPR8 - : $src), - "rolb\t$rd", - [(set i8 - : $rd, (AVRrol i8 - : $src)), - (implicit SREG)]>; + 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/test/CodeGen/AVR/pseudo/ROLBrd.mir b/llvm/test/CodeGen/AVR/pseudo/ROLBRdR1.mir similarity index 59% rename from llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir rename to llvm/test/CodeGen/AVR/pseudo/ROLBRdR1.mir index 023120b2bad2a..10f70413ebf28 100644 --- a/llvm/test/CodeGen/AVR/pseudo/ROLBrd.mir +++ b/llvm/test/CodeGen/AVR/pseudo/ROLBRdR1.mir @@ -17,8 +17,8 @@ body: | 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 - ; 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, implicit-def $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/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 } From 1201a28654e54269f186153ae5508ead4f36fa88 Mon Sep 17 00:00:00 2001 From: Ben Shi <2283975856@qq.com> Date: Sun, 11 Jun 2023 08:36:16 +0800 Subject: [PATCH 21/23] [AVR] Optimize 8-bit rotation when rotation bits >= 4 Fixes https://github.com/llvm/llvm-project/issues/63100 Reviewed By: aykevl, Patryk27, jacquesguan Differential Revision: https://reviews.llvm.org/D152130 --- llvm/lib/Target/AVR/AVRISelLowering.cpp | 15 ++++++ llvm/test/CodeGen/AVR/rotate.ll | 62 +++---------------------- 2 files changed, 22 insertions(+), 55 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index 636d3e66fc2aa..d043989bbe5c9 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -427,6 +427,21 @@ 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 == 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) diff --git a/llvm/test/CodeGen/AVR/rotate.ll b/llvm/test/CodeGen/AVR/rotate.ll index bf31fac0ed383..938b64f494376 100644 --- a/llvm/test/CodeGen/AVR/rotate.ll +++ b/llvm/test/CodeGen/AVR/rotate.ll @@ -30,14 +30,7 @@ start: define i8 @rotl8_5(i8 %x) { ; CHECK-LABEL: rotl8_5: ; CHECK: ; %bb.0: ; %start -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 +; CHECK-NEXT: swap r24 ; CHECK-NEXT: lsl r24 ; CHECK-NEXT: adc r24, r1 ; CHECK-NEXT: ret @@ -49,20 +42,9 @@ start: define i8 @rotl8_7(i8 %x) { ; CHECK-LABEL: rotl8_7: ; CHECK: ; %bb.0: ; %start -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 +; 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) @@ -121,18 +103,7 @@ start: define i8 @rotr8_5(i8 %x) { ; CHECK-LABEL: rotr8_5: ; CHECK: ; %bb.0: ; %start -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 +; CHECK-NEXT: swap r24 ; CHECK-NEXT: bst r24, 0 ; CHECK-NEXT: ror r24 ; CHECK-NEXT: bld r24, 7 @@ -145,27 +116,8 @@ start: define i8 @rotr8_7(i8 %x) { ; CHECK-LABEL: rotr8_7: ; CHECK: ; %bb.0: ; %start -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 +; 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) From 894df9dbb086cb1ab405d9788b38d7661348c571 Mon Sep 17 00:00:00 2001 From: Ben Shi <2283975856@qq.com> Date: Sun, 11 Jun 2023 08:41:44 +0800 Subject: [PATCH 22/23] [AVR] Optimize 8-bit rotation when rotation bits == 3 Fixes https://github.com/llvm/llvm-project/issues/63100 Reviewed By: aykevl Differential Revision: https://reviews.llvm.org/D152365 --- llvm/lib/Target/AVR/AVRISelLowering.cpp | 12 ++++++++++++ llvm/test/CodeGen/AVR/rotate.ll | 22 +++++++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index d043989bbe5c9..ed1f94080a6a9 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -427,6 +427,18 @@ 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 = diff --git a/llvm/test/CodeGen/AVR/rotate.ll b/llvm/test/CodeGen/AVR/rotate.ll index 938b64f494376..79ff7928b3a39 100644 --- a/llvm/test/CodeGen/AVR/rotate.ll +++ b/llvm/test/CodeGen/AVR/rotate.ll @@ -15,12 +15,10 @@ start: define i8 @rotl8_3(i8 %x) { ; CHECK-LABEL: rotl8_3: ; CHECK: ; %bb.0: ; %start -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 -; CHECK-NEXT: lsl r24 -; CHECK-NEXT: adc r24, r1 +; 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) @@ -85,15 +83,9 @@ start: define i8 @rotr8_3(i8 %x) { ; CHECK-LABEL: rotr8_3: ; CHECK: ; %bb.0: ; %start -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 -; CHECK-NEXT: bst r24, 0 -; CHECK-NEXT: ror r24 -; CHECK-NEXT: bld r24, 7 +; 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) From 8b9c3172ecd63b828c5dd3849b2b79f503c547c5 Mon Sep 17 00:00:00 2001 From: Patryk Wychowaniec Date: Wed, 19 Jul 2023 11:54:34 +0800 Subject: [PATCH 23/23] [AVR] Expand shifts of all types except int8 and int16 Currently our AVRShiftExpand pass expands only 32-bit shifts, with the assumption that other kinds of shifts (e.g. 64-bit ones) are automatically reduced to 8-bit ones by LLVM during ISel. However this is not always true and causes problems in the rust-lang runtime. This commit changes the logic a bit, so that instead of expanding only 32-bit shifts, we expand shifts of all types except 8-bit and 16-bit. This is not the most optimal solution, because 64-bit shifts can be expanded to 32-bit shifts which has been deeply optimized. I've checked the generated code using rustc + simavr, and all shifts seem to behave correctly. Spotted in the wild in rustc: https://github.com/rust-lang/compiler-builtins/issues/523 https://github.com/rust-lang/rust/issues/112140 Reviewed By: benshi001 Differential Revision: https://reviews.llvm.org/D154785 --- llvm/lib/Target/AVR/AVRShiftExpand.cpp | 24 +++-- llvm/test/CodeGen/AVR/shift-expand.ll | 137 +++++++++++++++++++------ llvm/test/CodeGen/AVR/shift.ll | 34 +++++- 3 files changed, 147 insertions(+), 48 deletions(-) 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/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