diff --git a/llvm/lib/Target/ARM/ARMInstrFormats.td b/llvm/lib/Target/ARM/ARMInstrFormats.td --- a/llvm/lib/Target/ARM/ARMInstrFormats.td +++ b/llvm/lib/Target/ARM/ARMInstrFormats.td @@ -2724,6 +2724,16 @@ let PrintMethod = "printComplexRotationOp<180, 90>"; } +def MveSaturateOperand : AsmOperandClass { + let PredicateMethod = "isMveSaturateOp"; + let DiagnosticString = "saturate operand must be 48 or 64"; + let Name = "MveSaturate"; +} +def saturateop : Operand { + let ParserMatchClass = MveSaturateOperand; + let PrintMethod = "printMveSaturateOp"; +} + // Data type suffix token aliases. Implements Table A7-3 in the ARM ARM. def : TokenAlias<".s8", ".i8">; def : TokenAlias<".u8", ".i8">; 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 @@ -427,6 +427,32 @@ let DecoderMethod = "DecodeMVEOverlappingLongShift"; } +class MVE_ScalarShiftDRegRegWithSat pattern=[]> + : MVE_ScalarShiftDoubleReg< + iname, (ins tGPREven:$RdaLo_src, tGPROdd:$RdaHi_src, rGPR:$Rm, saturateop:$sat), + "$RdaLo, $RdaHi, $sat, $Rm", "@earlyclobber $RdaHi,@earlyclobber $RdaLo," + "$RdaLo = $RdaLo_src,$RdaHi = $RdaHi_src", + pattern> { + bit sat; + bits<4> Rm; + + let Inst{16} = op16; + let Inst{15-12} = Rm{3-0}; + let Inst{7} = sat; + let Inst{6} = 0b0; + let Inst{5} = op5; + let Inst{4} = 0b0; + let Inst{3-0} = 0b1101; + + // Custom decoder method because of the following overlapping encodings: + // ASRL and SQRSHR + // LSLL and UQRSHL + // SQRSHRL and SQRSHR + // UQRSHLL and UQRSHL + let DecoderMethod = "DecodeMVEOverlappingLongShift"; +} + def MVE_ASRLr : MVE_ScalarShiftDRegReg<"asrl", 0b1, 0b0, [(set tGPREven:$RdaLo, tGPROdd:$RdaHi, (ARMasrl tGPREven:$RdaLo_src, tGPROdd:$RdaHi_src, rGPR:$Rm))]>; @@ -443,11 +469,11 @@ (ARMlsrl tGPREven:$RdaLo_src, tGPROdd:$RdaHi_src, (i32 imm:$imm)))]>; -def MVE_SQRSHRL : MVE_ScalarShiftDRegReg<"sqrshrl", 0b1, 0b1>; +def MVE_SQRSHRL : MVE_ScalarShiftDRegRegWithSat<"sqrshrl", 0b1, 0b1>; def MVE_SQSHLL : MVE_ScalarShiftDRegImm<"sqshll", 0b11, 0b1>; def MVE_SRSHRL : MVE_ScalarShiftDRegImm<"srshrl", 0b10, 0b1>; -def MVE_UQRSHLL : MVE_ScalarShiftDRegReg<"uqrshll", 0b0, 0b1>; +def MVE_UQRSHLL : MVE_ScalarShiftDRegRegWithSat<"uqrshll", 0b0, 0b1>; def MVE_UQSHLL : MVE_ScalarShiftDRegImm<"uqshll", 0b00, 0b1>; def MVE_URSHRL : MVE_ScalarShiftDRegImm<"urshrl", 0b01, 0b1>; 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 @@ -2275,6 +2275,14 @@ return Value >= 1 && Value <= 32; } + bool isMveSaturateOp() const { + if (!isImm()) return false; + const MCConstantExpr *CE = dyn_cast(getImm()); + if (!CE) return false; + uint64_t Value = CE->getValue(); + return Value == 48 || Value == 64; + } + bool isITCondCodeNoAL() const { if (!isITCondCode()) return false; ARMCC::CondCodes CC = getCondCode(); @@ -3370,6 +3378,14 @@ Inst.addOperand(MCOperand::createImm((CE->getValue() - 90) / 180)); } + void addMveSaturateOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCConstantExpr *CE = dyn_cast(getImm()); + unsigned Imm = CE->getValue(); + assert((Imm == 48 || Imm == 64) && "Invalid saturate operand"); + Inst.addOperand(MCOperand::createImm(Imm == 48 ? 1 : 0)); + } + void print(raw_ostream &OS) const override; static std::unique_ptr CreateITMask(unsigned Mask, SMLoc S) { 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 @@ -6503,6 +6503,13 @@ if (!Check(S, DecoderGPRRegisterClass(Inst, Rm, Address, Decoder))) return MCDisassembler::Fail; + if (Inst.getOpcode() == ARM::MVE_SQRSHRL || + Inst.getOpcode() == ARM::MVE_UQRSHLL) { + unsigned Saturate = fieldFromInstruction(Insn, 7, 1); + // Saturate, the bit position for saturation + Inst.addOperand(MCOperand::createImm(Saturate)); + } + return S; } diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h b/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.h @@ -262,7 +262,8 @@ const MCSubtargetInfo &STI, raw_ostream &O); void printExpandedImmOperand(const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, raw_ostream &O); - + void printMveSaturateOp(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, raw_ostream &O); private: unsigned DefaultAltIdx = ARM::NoRegAltName; }; diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMInstPrinter.cpp @@ -1676,3 +1676,11 @@ O.write_hex(Val); O << markup(">"); } + +void ARMInstPrinter::printMveSaturateOp(const MCInst *MI, unsigned OpNum, + const MCSubtargetInfo &STI, + raw_ostream &O) { + uint32_t Val = MI->getOperand(OpNum).getImm(); + assert(Val <= 1 && "Invalid MVE saturate operand"); + O << "#" << (Val == 1 ? 48 : 64); +} diff --git a/llvm/test/MC/ARM/mve-scalar-shift.s b/llvm/test/MC/ARM/mve-scalar-shift.s --- a/llvm/test/MC/ARM/mve-scalar-shift.s +++ b/llvm/test/MC/ARM/mve-scalar-shift.s @@ -111,9 +111,13 @@ # ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: instruction requires: mve sqrshr r11, r12 -# CHECK: sqrshrl lr, r3, r8 @ encoding: [0x5f,0xea,0x2d,0x83] +# CHECK: sqrshrl lr, r3, #64, r8 @ encoding: [0x5f,0xea,0x2d,0x83] # ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: instruction requires: mve -sqrshrl lr, r3, r8 +sqrshrl lr, r3, #64, r8 + +# ERROR: [[@LINE+2]]:{{[0-9]+}}: {{error|note}}: saturate operand must be 48 or 64 +# ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: invalid instruction +sqrshrl lr, r3, #32, r8 # CHECK: sqshl lr, #17 @ encoding: [0x5e,0xea,0x7f,0x4f] # ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: instruction requires: mve @@ -135,9 +139,13 @@ # ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: instruction requires: mve uqrshl lr, r1 -# CHECK: uqrshll lr, r1, r4 @ encoding: [0x5f,0xea,0x0d,0x41] +# CHECK: uqrshll lr, r1, #48, r4 @ encoding: [0x5f,0xea,0x8d,0x41] # ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: instruction requires: mve -uqrshll lr, r1, r4 +uqrshll lr, r1, #48, r4 + +# ERROR: [[@LINE+2]]:{{[0-9]+}}: {{error|note}}: saturate operand must be 48 or 64 +# ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: invalid instruction +uqrshll lr, r1, #0, r4 # CHECK: uqshl r0, #1 @ encoding: [0x50,0xea,0x4f,0x0f] # ERROR-NOMVE: [[@LINE+1]]:{{[0-9]+}}: error: instruction requires: mve diff --git a/llvm/test/MC/Disassembler/ARM/mve-scalar-shift.txt b/llvm/test/MC/Disassembler/ARM/mve-scalar-shift.txt --- a/llvm/test/MC/Disassembler/ARM/mve-scalar-shift.txt +++ b/llvm/test/MC/Disassembler/ARM/mve-scalar-shift.txt @@ -27,7 +27,11 @@ # CHECK-NOMVE: [[@LINE-2]]:2: warning: potentially undefined instruction encoding [0x5f 0xea 0x2d 0x83] -# CHECK: sqrshrl lr, r3, r8 @ encoding: [0x5f,0xea,0x2d,0x83] +# CHECK: sqrshrl lr, r3, #64, r8 @ encoding: [0x5f,0xea,0x2d,0x83] +# CHECK-NOMVE: [[@LINE-2]]:2: warning: invalid instruction encoding + +[0x5f 0xea 0xad 0x83] +# CHECK: sqrshrl lr, r3, #48, r8 @ encoding: [0x5f,0xea,0xad,0x83] # CHECK-NOMVE: [[@LINE-2]]:2: warning: invalid instruction encoding [0x5e 0xea 0x7f 0x4f] @@ -63,7 +67,11 @@ # CHECK-NOMVE: [[@LINE-2]]:2: warning: invalid instruction encoding [0x5f 0xea 0x0d 0x41] -# CHECK: uqrshll lr, r1, r4 @ encoding: [0x5f,0xea,0x0d,0x41] +# CHECK: uqrshll lr, r1, #64, r4 @ encoding: [0x5f,0xea,0x0d,0x41] +# CHECK-NOMVE: [[@LINE-2]]:2: warning: potentially undefined instruction encoding + +[0x5f 0xea 0x8d 0x41] +# CHECK: uqrshll lr, r1, #48, r4 @ encoding: [0x5f,0xea,0x8d,0x41] # CHECK-NOMVE: [[@LINE-2]]:2: warning: potentially undefined instruction encoding [0x50 0xea 0x4f 0x0f]