diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.td b/llvm/lib/Target/ARM/ARMInstrInfo.td --- a/llvm/lib/Target/ARM/ARMInstrInfo.td +++ b/llvm/lib/Target/ARM/ARMInstrInfo.td @@ -491,6 +491,28 @@ let ParserMatchClass = powertwo_asmoperand; } +// Pair vector indexing +class MVEPairVectorIndexOperand : AsmOperandClass { + let Name = "MVEPairVectorIndex"#start; + let RenderMethod = "addMVEPairVectorIndexOperands"; + let PredicateMethod = "isMVEPairVectorIndex<"#start#", "#end#">"; +} + +class MVEPairVectorIndex : Operand { + let PrintMethod = "printVectorIndex"; + let EncoderMethod = "getMVEPairVectorIndexOpValue<"#opval#">"; + let DecoderMethod = "DecodeMVEPairVectorIndexOperand<"#opval#">"; + let MIOperandInfo = (ops i32imm); +} + +def MVEPairVectorIndex0 : MVEPairVectorIndex<"0"> { + let ParserMatchClass = MVEPairVectorIndexOperand<"0", "1">; +} + +def MVEPairVectorIndex2 : MVEPairVectorIndex<"2"> { + let ParserMatchClass = MVEPairVectorIndexOperand<"2", "3">; +} + // Vector indexing class MVEVectorIndexOperand : AsmOperandClass { let Name = "MVEVectorIndex"#size; diff --git a/llvm/lib/Target/ARM/ARMInstrMVE.td b/llvm/lib/Target/ARM/ARMInstrMVE.td --- a/llvm/lib/Target/ARM/ARMInstrMVE.td +++ b/llvm/lib/Target/ARM/ARMInstrMVE.td @@ -3191,6 +3191,41 @@ // end of mve_qDest_rSrc +// start of coproc mov + +class VMOVt1dreg + : VMOV { + bits<5> Rt; + bits<5> Rt2; + bits<4> Qd; + bit idx; + bit idx2; + + let Inst{31-23} = 0b111011000; + let Inst{22} = Qd{3}; + let Inst{21} = 0b0; + let Inst{20} = bit_20; + let Inst{19-16} = Rt2{3-0}; + let Inst{15-13} = Qd{2-0}; + let Inst{12-5} = 0b01111000; + let Inst{4} = idx2; + let Inst{3-0} = Rt{3-0}; +} + +def VMOVt1qtodreg : VMOVt1dreg<0b0, (outs rGPR:$Rt, rGPR:$Rt2), (ins MQPR:$Qd, + MVEPairVectorIndex2:$idx, MVEPairVectorIndex0:$idx2), + "$Rt, $Rt2, $Qd$idx, $Qd$idx2", ""> { + let DecoderMethod = "DecodeMVEVMOVQtoDReg"; + let AsmMatchConverter = "cvtMVEVMOVQtoDReg"; +} + +def VMOVt1dregtoq : VMOVt1dreg<0b1, (outs MQPR:$Qd), (ins MQPR:$QdSrc, rGPR:$Rt, rGPR:$Rt2, + MVEPairVectorIndex2:$idx, MVEPairVectorIndex0:$idx2), + "$Qd$idx, $QdSrc$idx2, $Rt, $Rt2", "$Qd = $QdSrc"> { + let DecoderMethod = "DecodeMVEVMOVDRegtoQ"; +} +// end of coproc mov + class t2VPT size, dag iops, string asm, list pattern=[]> : MVE_MI<(outs ), iops, NoItinerary, !strconcat("vpt", "${Mk}", ".", suffix), asm, "", pattern> { bits<3> fc; diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -587,6 +587,7 @@ // Asm Match Converter Methods void cvtThumbMultiply(MCInst &Inst, const OperandVector &); void cvtThumbBranches(MCInst &Inst, const OperandVector &); + void cvtMVEVMOVQtoDReg(MCInst &Inst, const OperandVector &); bool validateInstruction(MCInst &Inst, const OperandVector &Ops); bool processInstruction(MCInst &Inst, const OperandVector &Ops, MCStreamer &Out); @@ -1981,6 +1982,12 @@ return VectorIndex.Val < size; } + template + bool isMVEPairVectorIndex() const { + if (Kind != k_VectorIndex) return false; + return VectorIndex.Val == start || VectorIndex.Val == end; + } + bool isNEONi8splat() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); @@ -3031,6 +3038,11 @@ Inst.addOperand(MCOperand::createImm(getVectorIndex())); } + void addMVEPairVectorIndexOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createImm(getVectorIndex())); + } + void addNEONi8splatOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); // The immediate encodes the type of constant as well as the value. @@ -5396,6 +5408,21 @@ ((ARMOperand &)*Operands[CondOp]).addCondCodeOperands(Inst, 2); } +void ARMAsmParser::cvtMVEVMOVQtoDReg( + MCInst &Inst, const OperandVector &Operands) { + + // mnemonic, condition code, Rt, Rt2, Qd, idx, Qd again, idx2 + assert(Operands.size() == 8); + + ((ARMOperand &)*Operands[2]).addRegOperands(Inst, 1); // Rt + ((ARMOperand &)*Operands[3]).addRegOperands(Inst, 1); // Rt2 + ((ARMOperand &)*Operands[4]).addRegOperands(Inst, 1); // Qd + ((ARMOperand &)*Operands[5]).addMVEPairVectorIndexOperands(Inst, 1); // idx + // skip second copy of Qd in Operands[6] + ((ARMOperand &)*Operands[7]).addMVEPairVectorIndexOperands(Inst, 1); // idx2 + ((ARMOperand &)*Operands[1]).addCondCodeOperands(Inst, 2); // condition code +} + /// Parse an ARM memory expression, return false if successful else return true /// or an error. The first token must be a '[' when called. bool ARMAsmParser::parseMemory(OperandVector &Operands) { @@ -6394,18 +6421,34 @@ if (Mnemonic.startswith("vctp")) return false; - for (auto &Operand : Operands) { - // We check both QPR and MQPR to more accurately report errors when - // using Q registers outside of the allowed range. - if (static_cast(*Operand).isVectorIndex() || - (Operand->isReg() && - (ARMMCRegisterClasses[ARM::MQPRRegClassID].contains( - Operand->getReg()) || - ARMMCRegisterClasses[ARM::QPRRegClassID].contains( - Operand->getReg())))) - return false; + if (Mnemonic.startswith("vmov") && + !(Mnemonic.startswith("vmovl") || Mnemonic.startswith("vmovn") || + Mnemonic.startswith("vmovx"))) { + for (auto &Operand : Operands) { + if (static_cast(*Operand).isVectorIndex() || + ((*Operand).isReg() && + (ARMMCRegisterClasses[ARM::SPRRegClassID].contains( + (*Operand).getReg()) || + ARMMCRegisterClasses[ARM::DPRRegClassID].contains( + (*Operand).getReg())))) { + return true; + } + } + return false; + } else { + for (auto &Operand : Operands) { + // We check both QPR and MQPR to more accurately report errors when + // using Q registers outside of the allowed range. + if (static_cast(*Operand).isVectorIndex() || + (Operand->isReg() && + (ARMMCRegisterClasses[ARM::MQPRRegClassID].contains( + Operand->getReg()) || + ARMMCRegisterClasses[ARM::QPRRegClassID].contains( + Operand->getReg())))) + return false; + } + return true; } - return true; } static bool isDataTypeToken(StringRef Tok) { @@ -7614,6 +7657,22 @@ } break; } + case ARM::VMOVt1qtodreg: { + if (Operands[4]->getReg() != Operands[6]->getReg()) + return Error (Operands[4]->getStartLoc(), "Q-registers must be the same"); + if (static_cast(*Operands[5]).getVectorIndex() != + static_cast(*Operands[7]).getVectorIndex() + 2) + return Error (Operands[5]->getStartLoc(), "Q-register indexes must be 2 and 0 or 3 and 1"); + break; + } + case ARM::VMOVt1dregtoq: { + if (Operands[2]->getReg() != Operands[4]->getReg()) + return Error (Operands[2]->getStartLoc(), "Q-registers must be the same"); + if (static_cast(*Operands[3]).getVectorIndex() != + static_cast(*Operands[5]).getVectorIndex() + 2) + return Error (Operands[3]->getStartLoc(), "Q-register indexes must be 2 and 0 or 3 and 1"); + break; + } } return false; diff --git a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp --- a/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/llvm/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -530,6 +530,15 @@ static DecodeStatus DecodeMVEVectorIndexOperand(MCInst &Inst, unsigned Val, uint64_t Address, const void *Decoder); +static DecodeStatus DecodeMVEPairVectorIndexOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeMVEVMOVQtoDReg(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); +static DecodeStatus DecodeMVEVMOVDRegtoQ(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); static DecodeStatus DecodeMVEVSHLL(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder); static DecodeStatus DecodeMVEVCVTt1fp(MCInst &Inst, unsigned Insn, @@ -6200,6 +6209,65 @@ return S; } +template +static DecodeStatus DecodeMVEPairVectorIndexOperand(MCInst &Inst, unsigned Val, + uint64_t Address, + const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + + Inst.addOperand(MCOperand::createImm(start + Val)); + + return S; +} + +static DecodeStatus DecodeMVEVMOVQtoDReg(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + unsigned Rt = fieldFromInstruction(Insn, 0, 4); + unsigned Rt2 = fieldFromInstruction(Insn, 16, 4); + unsigned Qd = ((fieldFromInstruction(Insn, 22, 1) << 3) | + fieldFromInstruction(Insn, 13, 3)); + unsigned index = fieldFromInstruction(Insn, 4, 1); + + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt2, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<2>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<0>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + +static DecodeStatus DecodeMVEVMOVDRegtoQ(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + DecodeStatus S = MCDisassembler::Success; + unsigned Rt = fieldFromInstruction(Insn, 0, 4); + unsigned Rt2 = fieldFromInstruction(Insn, 16, 4); + unsigned Qd = ((fieldFromInstruction(Insn, 22, 1) << 3) | + fieldFromInstruction(Insn, 13, 3)); + unsigned index = fieldFromInstruction(Insn, 4, 1); + + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMQPRRegisterClass(Inst, Qd, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeGPRRegisterClass(Inst, Rt2, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<2>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + if (!Check(S, DecodeMVEPairVectorIndexOperand<0>(Inst, index, Address, Decoder))) + return MCDisassembler::Fail; + + return S; +} + static DecodeStatus DecodeMVEVSHLL(MCInst &Inst, unsigned Insn, uint64_t Address, const void *Decoder) { DecodeStatus S = MCDisassembler::Success; diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -466,6 +466,10 @@ uint32_t getMVEVectorIndexOpValue(const MCInst &MI, unsigned OpIdx, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + template + uint32_t getMVEPairVectorIndexOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; }; } // end anonymous namespace @@ -1963,6 +1967,18 @@ return Imm | (Div & 2) << 3 | (Div & 1) << 2; } +template +uint32_t ARMMCCodeEmitter:: +getMVEPairVectorIndexOpValue(const MCInst &MI, unsigned OpIdx, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand MO = MI.getOperand(OpIdx); + assert(MO.isImm() && "Unexpected operand type!"); + + int Value = MO.getImm(); + return Value - start; +} + #include "ARMGenMCCodeEmitter.inc" MCCodeEmitter *llvm::createARMLEMCCodeEmitter(const MCInstrInfo &MCII, diff --git a/llvm/test/MC/ARM/mve-vmov-pair.s b/llvm/test/MC/ARM/mve-vmov-pair.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/ARM/mve-vmov-pair.s @@ -0,0 +1,25 @@ +# RUN: not llvm-mc -triple=thumbv8.1m.main-none-eabi -mattr=+mve -show-encoding < %s \ +# RUN: | FileCheck --check-prefix=CHECK-NOFP %s +# RUN: not llvm-mc -triple=thumbv8.1m.main-none-eabi -mattr=+mve.fp,+fp64 -show-encoding < %s 2>%t \ +# RUN: | FileCheck --check-prefix=CHECK %s +# RUN: FileCheck --check-prefix=ERROR < %t %s + +# CHECK: vmov lr, r7, q4[2], q4[0] @ encoding: [0x07,0xec,0x0e,0x8f] +# CHECK-NOFP: vmov lr, r7, q4[2], q4[0] @ encoding: [0x07,0xec,0x0e,0x8f] +vmov lr, r7, q4[2], q4[0] + +# ERROR: [[@LINE+1]]:{{[0-9]+}}: {{error|note}}: Q-registers must be the same +vmov lr, r7, q5[2], q4[0] + +# ERROR: [[@LINE+1]]:{{[0-9]+}}: {{error|note}}: Q-register indexes must be 2 and 0 or 3 and 1 +vmov lr, r7, q4[2], q4[1] + +# CHECK: vmov q3[3], q3[1], r4, r1 @ encoding: [0x11,0xec,0x14,0x6f] +# CHECK-NOFP: vmov q3[3], q3[1], r4, r1 @ encoding: [0x11,0xec,0x14,0x6f] +vmov q3[3], q3[1], r4, r1 + +# ERROR: [[@LINE+1]]:{{[0-9]+}}: {{error|note}}: Q-registers must be the same +vmov q4[3], q3[1], r4, r1 + +# ERROR: [[@LINE+1]]:{{[0-9]+}}: {{error|note}}: Q-register indexes must be 2 and 0 or 3 and 1 +vmov q3[2], q3[1], r4, r1 diff --git a/llvm/test/MC/Disassembler/ARM/mve-vmov-pair.txt b/llvm/test/MC/Disassembler/ARM/mve-vmov-pair.txt new file mode 100644 --- /dev/null +++ b/llvm/test/MC/Disassembler/ARM/mve-vmov-pair.txt @@ -0,0 +1,20 @@ +# RUN: not llvm-mc -disassemble -triple=thumbv8.1m.main-none-eabi -mattr=+mve.fp,+fp64 -show-encoding %s 2> %t | FileCheck %s +# RUN: FileCheck --check-prefix=ERROR < %t %s +# RUN: not llvm-mc -disassemble -triple=thumbv8.1m.main-none-eabi -show-encoding %s &> %t +# RUN: FileCheck --check-prefix=CHECK-NOMVE < %t %s +# RUN: not llvm-mc -disassemble -triple=thumbv8.1m.main-none-eabi -mattr=+mve.fp,+fp64 -show-encoding %s 2> %t | FileCheck %s +# RUN: FileCheck --check-prefix=ERROR < %t %s + +# CHECK: vmov lr, r7, q4[2], q4[0] @ encoding: [0x07,0xec,0x0e,0x8f] +# CHECK-NOMVE: [[@LINE+1]]:2: warning: invalid instruction encoding +[0x07,0xec,0x0e,0x8f] + +# CHECK: vmov q3[3], q3[1], r4, r1 @ encoding: [0x11,0xec,0x14,0x6f] +# CHECK-NOMVE: [[@LINE+1]]:2: warning: invalid instruction encoding +[0x11,0xec,0x14,0x6f] + +# ERROR: [[@LINE+1]]:2: warning: invalid instruction encoding +[0x40,0xec,0x00,0x0f] + +# ERROR: [[@LINE+1]]:2: warning: invalid instruction encoding +[0x50,0xec,0x00,0x0f]