From d1041be1e4578412a9ccc121614a71c717a94877 Mon Sep 17 00:00:00 2001 From: John Brawn Date: Tue, 8 Oct 2024 12:57:42 +0100 Subject: [PATCH 1/2] [ARM] Fix problems with register list in vscclrm The register list in vscclrm is unusual in two ways: * The encoded size can be zero, meaning the list contains only vpr. * Double-precision registers past d15 are permitted even when the subtarget doesn't have them, they are instead ignored when the instruction executes. Fixing this also incidentally changes a vlldm/vlstm error message: when the first register is in the range d16-d31 we now get the "operand must be exactly..." error instead of "register expected". --- .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 15 +++++-- .../ARM/Disassembler/ARMDisassembler.cpp | 34 +++++++++------- .../ARM/MCTargetDesc/ARMMCCodeEmitter.cpp | 2 +- llvm/test/MC/ARM/vlstm-vlldm-diag.s | 8 ++++ llvm/test/MC/ARM/vscclrm-asm.s | 30 ++++++++++++++ llvm/test/MC/Disassembler/ARM/vscclrm.txt | 39 ++++++++++++++++++- 6 files changed, 107 insertions(+), 21 deletions(-) diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 75fb90477f885..1cf9844be2695 100644 --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -3810,6 +3810,10 @@ class ARMOperand : public MCParsedAsmOperand { Kind = k_FPSRegisterListWithVPR; else Kind = k_SPRRegisterList; + } else if (Regs.front().second == ARM::VPR) { + assert(Regs.size() == 1 && + "Register list starting with VPR expected to only contain VPR"); + Kind = k_FPSRegisterListWithVPR; } if (Kind == k_RegisterList && Regs.back().second == ARM::APSR) @@ -4617,15 +4621,15 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, // Check the first register in the list to see what register class // this is a list of. - MCRegister Reg = tryParseRegister(); + MCRegister Reg = tryParseRegister(AllowOutOfBoundReg); if (!Reg) return Error(RegLoc, "register expected"); if (!AllowRAAC && Reg == ARM::RA_AUTH_CODE) return Error(RegLoc, "pseudo-register not allowed"); - // The reglist instructions have at most 16 registers, so reserve + // The reglist instructions have at most 32 registers, so reserve // space for that many. int EReg = 0; - SmallVector, 16> Registers; + SmallVector, 32> Registers; // Allow Q regs and just interpret them as the two D sub-registers. if (ARMMCRegisterClasses[ARM::QPRRegClassID].contains(Reg)) { @@ -4644,6 +4648,8 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, RC = &ARMMCRegisterClasses[ARM::SPRRegClassID]; else if (ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID].contains(Reg)) RC = &ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID]; + else if (Reg == ARM::VPR) + RC = &ARMMCRegisterClasses[ARM::FPWithVPRRegClassID]; else return Error(RegLoc, "invalid register in register list"); @@ -6335,7 +6341,8 @@ bool ARMAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { case AsmToken::LBrac: return parseMemory(Operands); case AsmToken::LCurly: { - bool AllowOutOfBoundReg = Mnemonic == "vlldm" || Mnemonic == "vlstm"; + bool AllowOutOfBoundReg = + Mnemonic == "vlldm" || Mnemonic == "vlstm" || Mnemonic == "vscclrm"; return parseRegisterList(Operands, !Mnemonic.starts_with("clr"), false, AllowOutOfBoundReg); } diff --git a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp index 93b74905fc59f..fb08309aa3ab2 100644 --- a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -1528,15 +1528,19 @@ static const uint16_t DPRDecoderTable[] = { ARM::D28, ARM::D29, ARM::D30, ARM::D31 }; -static DecodeStatus DecodeDPRRegisterClass(MCInst &Inst, unsigned RegNo, - uint64_t Address, - const MCDisassembler *Decoder) { +// Does this instruction/subtarget permit use of registers d16-d31? +static bool PermitsD32(const MCInst &Inst, const MCDisassembler *Decoder) { + if (Inst.getOpcode() == ARM::VSCCLRMD) + return true; const FeatureBitset &featureBits = ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits(); + return featureBits[ARM::FeatureD32]; +} - bool hasD32 = featureBits[ARM::FeatureD32]; - - if (RegNo > 31 || (!hasD32 && RegNo > 15)) +static DecodeStatus DecodeDPRRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const MCDisassembler *Decoder) { + if (RegNo > (PermitsD32(Inst, Decoder) ? 31 : 15)) return MCDisassembler::Fail; unsigned Register = DPRDecoderTable[RegNo]; @@ -1815,10 +1819,11 @@ static DecodeStatus DecodeDPRRegListOperand(MCInst &Inst, unsigned Val, unsigned regs = fieldFromInstruction(Val, 1, 7); // In case of unpredictable encoding, tweak the operands. - if (regs == 0 || regs > 16 || (Vd + regs) > 32) { - regs = Vd + regs > 32 ? 32 - Vd : regs; + unsigned MaxReg = PermitsD32(Inst, Decoder) ? 32 : 16; + if (regs == 0 || (Vd + regs) > MaxReg) { + regs = Vd + regs > MaxReg ? MaxReg - Vd : regs; regs = std::max( 1u, regs); - regs = std::min(16u, regs); + regs = std::min(MaxReg, regs); S = MCDisassembler::SoftFail; } @@ -6446,16 +6451,17 @@ static DecodeStatus DecodeVSCCLRM(MCInst &Inst, unsigned Insn, uint64_t Address, Inst.addOperand(MCOperand::createImm(ARMCC::AL)); Inst.addOperand(MCOperand::createReg(0)); - if (Inst.getOpcode() == ARM::VSCCLRMD) { - unsigned reglist = (fieldFromInstruction(Insn, 1, 7) << 1) | - (fieldFromInstruction(Insn, 12, 4) << 8) | + unsigned regs = fieldFromInstruction(Insn, 0, 8); + if (regs == 0) { + // Register list contains only VPR + } else if (Inst.getOpcode() == ARM::VSCCLRMD) { + unsigned reglist = regs | (fieldFromInstruction(Insn, 12, 4) << 8) | (fieldFromInstruction(Insn, 22, 1) << 12); if (!Check(S, DecodeDPRRegListOperand(Inst, reglist, Address, Decoder))) { return MCDisassembler::Fail; } } else { - unsigned reglist = fieldFromInstruction(Insn, 0, 8) | - (fieldFromInstruction(Insn, 22, 1) << 8) | + unsigned reglist = regs | (fieldFromInstruction(Insn, 22, 1) << 8) | (fieldFromInstruction(Insn, 12, 4) << 9); if (!Check(S, DecodeSPRRegListOperand(Inst, reglist, Address, Decoder))) { return MCDisassembler::Fail; diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp index 92427b41f0bb3..53dad96a00f6d 100644 --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -1743,7 +1743,7 @@ getRegisterListOpValue(const MCInst &MI, unsigned Op, unsigned Binary = 0; - if (SPRRegs || DPRRegs) { + if (SPRRegs || DPRRegs || Reg == ARM::VPR) { // VLDM/VSTM/VSCCLRM unsigned RegNo = CTX.getRegisterInfo()->getEncodingValue(Reg); unsigned NumRegs = (MI.getNumOperands() - Op) & 0xff; diff --git a/llvm/test/MC/ARM/vlstm-vlldm-diag.s b/llvm/test/MC/ARM/vlstm-vlldm-diag.s index b57f535c6a25c..7aa48b96ff2f6 100644 --- a/llvm/test/MC/ARM/vlstm-vlldm-diag.s +++ b/llvm/test/MC/ARM/vlstm-vlldm-diag.s @@ -36,6 +36,14 @@ vlldm r8, {d3 - d31} // ERR: error: operand must be exactly {d0-d15} (T1) or {d0-d31} (T2) // ERR-NEXT: vlldm r8, {d3 - d31} +vlstm r8, {d31} +// ERR: error: operand must be exactly {d0-d15} (T1) or {d0-d31} (T2) +// ERR-NEXT: vlstm r8, {d31} + +vlldm r8, {d31} +// ERR: error: operand must be exactly {d0-d15} (T1) or {d0-d31} (T2) +// ERR-NEXT: vlldm r8, {d31} + vlstm r8, {d0 - d35} // ERR: error: register expected // ERR-NEXT: vlstm r8, {d0 - d35} diff --git a/llvm/test/MC/ARM/vscclrm-asm.s b/llvm/test/MC/ARM/vscclrm-asm.s index 0989b38b07c06..94d6d3c51dd8d 100644 --- a/llvm/test/MC/ARM/vscclrm-asm.s +++ b/llvm/test/MC/ARM/vscclrm-asm.s @@ -35,11 +35,41 @@ it hi // CHECK: vscclrmhi {s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, vpr} @ encoding: [0xdf,0xec,0x1d,0x1a] vscclrmhi {s3-s31, vpr} +// CHECK: vscclrm {vpr} @ encoding: [0x9f,0xec,0x00,0x0a] +vscclrm {vpr} + +// CHECK: vscclrm {d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} @ encoding: [0x9f,0xec,0x40,0x0b] +vscclrm {d0-d31, vpr} + +// CHECK: vscclrm {d31, vpr} @ encoding: [0xdf,0xec,0x02,0xfb] +vscclrm {d31, vpr} + // ERROR: non-contiguous register range vscclrm {s0, s3-s4, vpr} // ERROR: register expected vscclrm {s32, vpr} +// ERROR: register expected +vscclrm {d32, vpr} + +// ERROR: register expected +vscclrm {s31-s32, vpr} + +// ERROR: register expected +vscclrm {d31-d32, vpr} + // ERROR: invalid operand for instruction vscclrm {s0-s1} + +// ERROR: register list not in ascending order +vscclrm {vpr, s0} + +// ERROR: register list not in ascending order +vscclrm {vpr, s31} + +// ERROR: register list not in ascending order +vscclrm {vpr, d0} + +// ERROR: register list not in ascending order +vscclrm {vpr, d31} diff --git a/llvm/test/MC/Disassembler/ARM/vscclrm.txt b/llvm/test/MC/Disassembler/ARM/vscclrm.txt index 8a89cfb76e4a4..b7adaaf69d30a 100644 --- a/llvm/test/MC/Disassembler/ARM/vscclrm.txt +++ b/llvm/test/MC/Disassembler/ARM/vscclrm.txt @@ -1,5 +1,7 @@ -# RUN: llvm-mc -disassemble -triple=thumbv8.1m.main-none-eabi -mattr=+8msecext -show-encoding %s 2>&1 | FileCheck %s -# RUN: llvm-mc -disassemble -triple=thumbv8.1m.main-none-eabi -mattr=+mve.fp,+8msecext -show-encoding %s 2>&1 | FileCheck %s +# RUN: llvm-mc -disassemble -triple=thumbv8.1m.main-none-eabi -mattr=+8msecext -show-encoding %s 2> %t | FileCheck %s +# RUN: FileCheck --check-prefix=WARN < %t %s +# RUN: llvm-mc -disassemble -triple=thumbv8.1m.main-none-eabi -mattr=+mve.fp,+8msecext -show-encoding %s 2> %t | FileCheck %s +# RUN: FileCheck --check-prefix=WARN < %t %s [0x9f 0xec 0x04 0x0a] # CHECK: vscclrm {s0, s1, s2, s3, vpr} @@ -27,3 +29,36 @@ [0xdf 0xec 0x1d 0x1a] # CHECK: vscclrmhi {s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, vpr} + +# If the list size is zero then we get a list of only vpr, and the Vd register +# doesn't matter. + +[0x9f,0xec,0x00,0x0b] +# CHECK: vscclrm {vpr} @ encoding: [0x9f,0xec,0x00,0x0b] + +[0xdf,0xec,0x00,0xfb] +# CHECK: vscclrm {vpr} @ encoding: [0x9f,0xec,0x00,0x0b] + +[0x9f,0xec,0x00,0x0a] +# CHECK: vscclrm {vpr} @ encoding: [0x9f,0xec,0x00,0x0a] + +[0xdf,0xec,0x00,0xfa] +# CHECK: vscclrm {vpr} @ encoding: [0x9f,0xec,0x00,0x0a] + +# If Vd+size goes past 31 the excess registers are ignored and we get a warning. + +[0x9f,0xec,0xfe,0x0b] +# WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding +# CHECK: vscclrm {d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} @ encoding: [0x9f,0xec,0x40,0x0b] + +[0xdf,0xec,0x04,0xfb] +# WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding +# CHECK: vscclrm {d31, vpr} @ encoding: [0xdf,0xec,0x02,0xfb] + +[0x9f,0xec,0xff,0x0a] +# WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding +# CHECK: vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, vpr} @ encoding: [0x9f,0xec,0x20,0x0a] + +[0xdf,0xec,0x02,0xfa] +# WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding +# CHECK: vscclrm {s31, vpr} @ encoding: [0xdf,0xec,0x01,0xfa] From d2749af0c182257d21b50a78f9fe20c8223a115f Mon Sep 17 00:00:00 2001 From: John Brawn Date: Mon, 14 Oct 2024 17:55:41 +0100 Subject: [PATCH 2/2] Allow double-precision registers in VSCCLRMS --- llvm/lib/Target/ARM/ARMRegisterInfo.td | 4 +- .../lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 40 ++++++++++++++----- .../ARM/Disassembler/ARMDisassembler.cpp | 22 +++++++--- .../ARM/MCTargetDesc/ARMInstPrinter.cpp | 2 +- .../ARM/MCTargetDesc/ARMMCCodeEmitter.cpp | 17 +++++++- llvm/test/MC/ARM/vscclrm-asm.s | 21 ++++++++++ llvm/test/MC/Disassembler/ARM/vscclrm.txt | 18 ++++++++- 7 files changed, 101 insertions(+), 23 deletions(-) diff --git a/llvm/lib/Target/ARM/ARMRegisterInfo.td b/llvm/lib/Target/ARM/ARMRegisterInfo.td index 212f22651f9f9..f37d0fe542b4f 100644 --- a/llvm/lib/Target/ARM/ARMRegisterInfo.td +++ b/llvm/lib/Target/ARM/ARMRegisterInfo.td @@ -200,9 +200,9 @@ def FPEXC : ARMReg<8, "fpexc">; def FPINST : ARMReg<9, "fpinst">; def FPINST2 : ARMReg<10, "fpinst2">; // These encodings aren't actual instruction encodings, their encoding depends -// on the instruction they are used in and for VPR 32 was chosen such that it +// on the instruction they are used in and for VPR 64 was chosen such that it // always comes last in spr_reglist_with_vpr. -def VPR : ARMReg<32, "vpr">; +def VPR : ARMReg<64, "vpr">; def FPSCR_NZCVQC : ARMReg<2, "fpscr_nzcvqc">; def P0 : ARMReg<13, "p0">; diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 1cf9844be2695..000fc0470dcbc 100644 --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -446,8 +446,8 @@ class ARMAsmParser : public MCTargetAsmParser { int tryParseShiftRegister(OperandVector &); std::optional tryParseShiftToken(); bool parseRegisterList(OperandVector &, bool EnforceOrder = true, - bool AllowRAAC = false, - bool AllowOutOfBoundReg = false); + bool AllowRAAC = false, bool IsLazyLoadStore = false, + bool IsVSCCLRM = false); bool parseMemory(OperandVector &); bool parseOperand(OperandVector &, StringRef Mnemonic); bool parseImmExpr(int64_t &Out); @@ -4611,7 +4611,8 @@ insertNoDuplicates(SmallVectorImpl> &Regs, /// Parse a register list. bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, - bool AllowRAAC, bool AllowOutOfBoundReg) { + bool AllowRAAC, bool IsLazyLoadStore, + bool IsVSCCLRM) { MCAsmParser &Parser = getParser(); if (Parser.getTok().isNot(AsmToken::LCurly)) return TokError("Token is not a Left Curly Brace"); @@ -4621,6 +4622,7 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, // Check the first register in the list to see what register class // this is a list of. + bool AllowOutOfBoundReg = IsLazyLoadStore || IsVSCCLRM; MCRegister Reg = tryParseRegister(AllowOutOfBoundReg); if (!Reg) return Error(RegLoc, "register expected"); @@ -4631,6 +4633,13 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, int EReg = 0; SmallVector, 32> Registers; + // Single-precision VSCCLRM can have double-precision registers in the + // register list. When VSCCLRMAdjustEncoding is true then we've switched from + // single-precision to double-precision and we pretend that these registers + // are encoded as S32 onwards, which we can do by adding 16 to the encoding + // value. + bool VSCCLRMAdjustEncoding = false; + // Allow Q regs and just interpret them as the two D sub-registers. if (ARMMCRegisterClasses[ARM::QPRRegClassID].contains(Reg)) { Reg = getDRegFromQReg(Reg); @@ -4690,6 +4699,8 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, while (Reg != EndReg) { Reg = getNextRegister(Reg); EReg = MRI->getEncodingValue(Reg); + if (VSCCLRMAdjustEncoding) + EReg += 16; if (!insertNoDuplicates(Registers, EReg, Reg)) { Warning(AfterMinusLoc, StringRef("duplicated register (") + ARMInstPrinter::getRegisterName(Reg) + @@ -4701,6 +4712,7 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, Parser.Lex(); // Eat the comma. RegLoc = Parser.getTok().getLoc(); MCRegister OldReg = Reg; + int EOldReg = EReg; const AsmToken RegTok = Parser.getTok(); Reg = tryParseRegister(AllowOutOfBoundReg); if (!Reg) @@ -4732,6 +4744,12 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, } continue; } + // VSCCLRM can switch from single-precision to double-precision only when + // S31 is followed by D16. + if (IsVSCCLRM && OldReg == ARM::S31 && Reg == ARM::D16) { + VSCCLRMAdjustEncoding = true; + RC = &ARMMCRegisterClasses[ARM::FPWithVPRRegClassID]; + } // The register must be in the same register class as the first. if ((Reg == ARM::RA_AUTH_CODE && RC != &ARMMCRegisterClasses[ARM::GPRRegClassID]) || @@ -4741,8 +4759,10 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, // exception is CLRM, which is order-independent anyway, so // there's no potential for confusion if you write clrm {r2,r1} // instead of clrm {r1,r2}. - if (EnforceOrder && - MRI->getEncodingValue(Reg) < MRI->getEncodingValue(OldReg)) { + EReg = MRI->getEncodingValue(Reg); + if (VSCCLRMAdjustEncoding) + EReg += 16; + if (EnforceOrder && EReg < EOldReg) { if (ARMMCRegisterClasses[ARM::GPRRegClassID].contains(Reg)) Warning(RegLoc, "register list not in ascending order"); else if (!ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID].contains(Reg)) @@ -4751,9 +4771,9 @@ bool ARMAsmParser::parseRegisterList(OperandVector &Operands, bool EnforceOrder, // VFP register lists must also be contiguous. if (RC != &ARMMCRegisterClasses[ARM::GPRRegClassID] && RC != &ARMMCRegisterClasses[ARM::GPRwithAPSRnospRegClassID] && - Reg != OldReg + 1) + EReg != EOldReg + 1) return Error(RegLoc, "non-contiguous register range"); - EReg = MRI->getEncodingValue(Reg); + if (!insertNoDuplicates(Registers, EReg, Reg)) { Warning(RegLoc, "duplicated register (" + RegTok.getString() + ") in register list"); @@ -6341,10 +6361,10 @@ bool ARMAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { case AsmToken::LBrac: return parseMemory(Operands); case AsmToken::LCurly: { - bool AllowOutOfBoundReg = - Mnemonic == "vlldm" || Mnemonic == "vlstm" || Mnemonic == "vscclrm"; + bool IsLazyLoadStore = Mnemonic == "vlldm" || Mnemonic == "vlstm"; + bool IsVSCCLRM = Mnemonic == "vscclrm"; return parseRegisterList(Operands, !Mnemonic.starts_with("clr"), false, - AllowOutOfBoundReg); + IsLazyLoadStore, IsVSCCLRM); } case AsmToken::Dollar: case AsmToken::Hash: { diff --git a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp index fb08309aa3ab2..d13a7ea76b398 100644 --- a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -1530,7 +1530,7 @@ static const uint16_t DPRDecoderTable[] = { // Does this instruction/subtarget permit use of registers d16-d31? static bool PermitsD32(const MCInst &Inst, const MCDisassembler *Decoder) { - if (Inst.getOpcode() == ARM::VSCCLRMD) + if (Inst.getOpcode() == ARM::VSCCLRMD || Inst.getOpcode() == ARM::VSCCLRMS) return true; const FeatureBitset &featureBits = ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits(); @@ -6461,11 +6461,21 @@ static DecodeStatus DecodeVSCCLRM(MCInst &Inst, unsigned Insn, uint64_t Address, return MCDisassembler::Fail; } } else { - unsigned reglist = regs | (fieldFromInstruction(Insn, 22, 1) << 8) | - (fieldFromInstruction(Insn, 12, 4) << 9); - if (!Check(S, DecodeSPRRegListOperand(Inst, reglist, Address, Decoder))) { - return MCDisassembler::Fail; - } + unsigned Vd = (fieldFromInstruction(Insn, 12, 4) << 1) | + fieldFromInstruction(Insn, 22, 1); + // Registers past s31 are permitted and treated as being half of a d + // register, though both halves of each d register must be present. + unsigned max_reg = Vd + regs; + if (max_reg > 64 || (max_reg > 32 && (max_reg & 1))) + S = MCDisassembler::SoftFail; + unsigned max_sreg = std::min(32u, max_reg); + unsigned max_dreg = std::min(32u, max_reg / 2); + for (unsigned i = Vd; i < max_sreg; ++i) + if (!Check(S, DecodeSPRRegisterClass(Inst, i, Address, Decoder))) + return MCDisassembler::Fail; + for (unsigned i = 16; i < max_dreg; ++i) + if (!Check(S, DecodeDPRRegisterClass(Inst, i, Address, Decoder))) + return MCDisassembler::Fail; } Inst.addOperand(MCOperand::createReg(ARM::VPR)); diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp index 5636cc6287ac4..e4a2f8c8f2ea0 100644 --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp @@ -851,7 +851,7 @@ void ARMInstPrinter::printPKHASRShiftImm(const MCInst *MI, unsigned OpNum, void ARMInstPrinter::printRegisterList(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O) { - if (MI->getOpcode() != ARM::t2CLRM) { + if (MI->getOpcode() != ARM::t2CLRM && MI->getOpcode() != ARM::VSCCLRMS) { assert(is_sorted(drop_begin(*MI, OpNum), [&](const MCOperand &LHS, const MCOperand &RHS) { return MRI.getEncodingValue(LHS.getReg()) < diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp index 53dad96a00f6d..f24ac799b2dda 100644 --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -1749,9 +1749,22 @@ getRegisterListOpValue(const MCInst &MI, unsigned Op, unsigned NumRegs = (MI.getNumOperands() - Op) & 0xff; Binary |= (RegNo & 0x1f) << 8; - // Ignore VPR - if (MI.getOpcode() == ARM::VSCCLRMD || MI.getOpcode() == ARM::VSCCLRMS) + if (MI.getOpcode() == ARM::VSCCLRMD) + // Ignore VPR --NumRegs; + else if (MI.getOpcode() == ARM::VSCCLRMS) { + // The register list can contain both S registers and D registers, with D + // registers counting as two registers. VPR doesn't count towards the + // number of registers. + NumRegs = 0; + for (unsigned I = Op, E = MI.getNumOperands(); I < E; ++I) { + Reg = MI.getOperand(I).getReg(); + if (ARMMCRegisterClasses[ARM::SPRRegClassID].contains(Reg)) + NumRegs += 1; + else if (ARMMCRegisterClasses[ARM::DPRRegClassID].contains(Reg)) + NumRegs += 2; + } + } if (SPRRegs) Binary |= NumRegs; else diff --git a/llvm/test/MC/ARM/vscclrm-asm.s b/llvm/test/MC/ARM/vscclrm-asm.s index 94d6d3c51dd8d..0d2054df4fd34 100644 --- a/llvm/test/MC/ARM/vscclrm-asm.s +++ b/llvm/test/MC/ARM/vscclrm-asm.s @@ -44,9 +44,21 @@ vscclrm {d0-d31, vpr} // CHECK: vscclrm {d31, vpr} @ encoding: [0xdf,0xec,0x02,0xfb] vscclrm {d31, vpr} +// CHECK: vscclrm {s31, d16, vpr} @ encoding: [0xdf,0xec,0x03,0xfa] +vscclrm {s31, d16, vpr} + +// CHECK: vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} @ encoding: [0x9f,0xec,0x40,0x0a] +vscclrm {s0-s31, d16-d31, vpr} + +// CHECK: vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} @ encoding: [0x9f,0xec,0x40,0x0a] +vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} + // ERROR: non-contiguous register range vscclrm {s0, s3-s4, vpr} +// ERROR: non-contiguous register range +vscclrm {s31, d16, s30, vpr} + // ERROR: register expected vscclrm {s32, vpr} @@ -73,3 +85,12 @@ vscclrm {vpr, d0} // ERROR: register list not in ascending order vscclrm {vpr, d31} + +// ERROR: invalid register in register list +vscclrm {s0, d0, vpr} + +// ERROR: invalid register in register list +vscclrm {s0, d1, vpr} + +// ERROR: invalid register in register list +vscclrm {d16, s31, vpr} diff --git a/llvm/test/MC/Disassembler/ARM/vscclrm.txt b/llvm/test/MC/Disassembler/ARM/vscclrm.txt index b7adaaf69d30a..ef3868eb1569f 100644 --- a/llvm/test/MC/Disassembler/ARM/vscclrm.txt +++ b/llvm/test/MC/Disassembler/ARM/vscclrm.txt @@ -30,6 +30,12 @@ [0xdf 0xec 0x1d 0x1a] # CHECK: vscclrmhi {s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, vpr} +[0xdf,0xec,0x03,0xfa] +# CHECK: vscclrm {s31, d16, vpr} @ encoding: [0xdf,0xec,0x03,0xfa] + +[0x9f,0xec,0x40,0x0a] +# CHECK: vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} @ encoding: [0x9f,0xec,0x40,0x0a] + # If the list size is zero then we get a list of only vpr, and the Vd register # doesn't matter. @@ -45,7 +51,8 @@ [0xdf,0xec,0x00,0xfa] # CHECK: vscclrm {vpr} @ encoding: [0x9f,0xec,0x00,0x0a] -# If Vd+size goes past 31 the excess registers are ignored and we get a warning. +# In double-precision if Vd+size goes past 31 the excess registers are ignored +# and we get a warning. [0x9f,0xec,0xfe,0x0b] # WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding @@ -55,10 +62,17 @@ # WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding # CHECK: vscclrm {d31, vpr} @ encoding: [0xdf,0xec,0x02,0xfb] +# In single-precision if Vd+size goes past 63, or if the encoding suggests half +# a d registers, then the excess registers are ignored and we get a warning. + [0x9f,0xec,0xff,0x0a] # WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding -# CHECK: vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, vpr} @ encoding: [0x9f,0xec,0x20,0x0a] +# CHECK: vscclrm {s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} @ encoding: [0x9f,0xec,0x40,0x0a] [0xdf,0xec,0x02,0xfa] # WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding # CHECK: vscclrm {s31, vpr} @ encoding: [0xdf,0xec,0x01,0xfa] + +[0xdf,0xec,0x23,0xfa] +# WARN: [[@LINE-1]]:2: warning: potentially undefined instruction encoding +vscclrm {s31, d16, d17, d18, d19, d20, d21, d22, d23, d24, d25, d26, d27, d28, d29, d30, d31, vpr} @ encoding: [0xdf,0xec,0x21,0xfa]