[llvm] [RISCV][Xqccmp] Correctly Parse/Disassemble pushfp (PR #133188)

via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 26 18:05:37 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mc

Author: Sam Elliott (lenary)

<details>
<summary>Changes</summary>

In the `qc.cm.pushfp` instruction, it is like `cm.pushfp` except in one important way - `qc.cm.pushfp {ra}, -N*16` is not a valid encoding, because this would update `s0`/`fp`/`x8` without saving it.

This change now correctly rejects this variant of the instruction, both during parsing and during disassembly. I also implemented validation for immediates that represent register lists (both kinds), which may help to catch bugs in the future.

---
Full diff: https://github.com/llvm/llvm-project/pull/133188.diff


12 Files Affected:

- (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+25-2) 
- (modified) llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp (+11) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h (+2) 
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp (+15) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.cpp (+6) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td (+39-1) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfoZc.td (+3-1) 
- (added) llvm/test/MC/Disassembler/RISCV/xqccmp-invalid-rlist.txt (+11) 
- (modified) llvm/test/MC/RISCV/rv32xqccmp-invalid.s (+4) 
- (modified) llvm/test/MC/RISCV/rv32xqccmp-valid.s (-8) 
- (modified) llvm/test/MC/RISCV/rv64e-xqccmp-valid.s (-4) 
- (modified) llvm/test/MC/RISCV/rv64xqccmp-valid.s (-4) 


``````````diff
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 0d40fb5614009..19f28a6287060 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -215,7 +215,10 @@ class RISCVAsmParser : public MCTargetAsmParser {
   ParseStatus parseGPRPair(OperandVector &Operands, bool IsRV64Inst);
   ParseStatus parseFRMArg(OperandVector &Operands);
   ParseStatus parseFenceArg(OperandVector &Operands);
+
+  template <bool MustIncludeS0 = false>
   ParseStatus parseReglist(OperandVector &Operands);
+
   ParseStatus parseRegReg(OperandVector &Operands);
   ParseStatus parseRetval(OperandVector &Operands);
   ParseStatus parseZcmpStackAdj(OperandVector &Operands,
@@ -474,6 +477,7 @@ struct RISCVOperand final : public MCParsedAsmOperand {
   bool isSystemRegister() const { return Kind == KindTy::SystemRegister; }
   bool isRegReg() const { return Kind == KindTy::RegReg; }
   bool isRlist() const { return Kind == KindTy::Rlist; }
+  bool isRlistS0() const { return Kind == KindTy::Rlist && Rlist.Val != 4; }
   bool isSpimm() const { return Kind == KindTy::Spimm; }
 
   bool isGPR() const {
@@ -2794,9 +2798,15 @@ ParseStatus RISCVAsmParser::parseRegReg(OperandVector &Operands) {
   return ParseStatus::Success;
 }
 
+template <bool MustIncludeS0>
 ParseStatus RISCVAsmParser::parseReglist(OperandVector &Operands) {
   // Rlist: {ra [, s0[-sN]]}
   // XRlist: {x1 [, x8[-x9][, x18[-xN]]]}
+
+  // When MustIncludeS0 = true (not the default) (used for `qc.cm.pushfp`) which
+  // must include `fp`/`s0` in the list:
+  // Rlist: {ra, s0[-sN]}
+  // XRlist: {x1, x8[-x9][, x18[-xN]]}
   SMLoc S = getLoc();
 
   if (parseToken(AsmToken::LCurly, "register list must start with '{'"))
@@ -2814,8 +2824,19 @@ ParseStatus RISCVAsmParser::parseReglist(OperandVector &Operands) {
     return Error(getLoc(), "register list must start from 'ra' or 'x1'");
   getLexer().Lex();
 
-  // parse case like ,s0
-  if (parseOptionalToken(AsmToken::Comma)) {
+  bool SeenComma = false;
+
+  // If you must include S0, then you need the comma. Fail now if it isn't there
+  if constexpr (MustIncludeS0) {
+    if (parseToken(AsmToken::Comma, "register list must include 's0' or 'x8'"))
+      return ParseStatus::Failure;
+    SeenComma = true;
+  } else {
+    SeenComma = parseOptionalToken(AsmToken::Comma);
+  }
+
+  // parse case like ,s0 - we may have already seen the comma in which case skip
+  if (SeenComma) {
     if (getLexer().isNot(AsmToken::Identifier))
       return Error(getLoc(), "invalid register");
     StringRef RegName = getLexer().getTok().getIdentifier();
@@ -2884,6 +2905,8 @@ ParseStatus RISCVAsmParser::parseReglist(OperandVector &Operands) {
 
   auto Encode = RISCVZC::encodeRlist(RegEnd, IsEABI);
   assert(Encode != RISCVZC::INVALID_RLIST);
+  if constexpr (MustIncludeS0)
+    assert(Encode != RISCVZC::RA);
   Operands.push_back(RISCVOperand::createRlist(Encode, S));
 
   return ParseStatus::Success;
diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index 93cbf662bfa32..d03351db5473b 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -507,6 +507,9 @@ static DecodeStatus decodeXTHeadMemPair(MCInst &Inst, uint32_t Insn,
 static DecodeStatus decodeZcmpRlist(MCInst &Inst, uint32_t Imm,
                                     uint64_t Address, const void *Decoder);
 
+static DecodeStatus decodeXqccmpRlistS0(MCInst &Inst, uint32_t Imm,
+                                        uint64_t Address, const void *Decoder);
+
 static DecodeStatus decodeRegReg(MCInst &Inst, uint32_t Insn, uint64_t Address,
                                  const MCDisassembler *Decoder);
 
@@ -621,6 +624,14 @@ static DecodeStatus decodeZcmpRlist(MCInst &Inst, uint32_t Imm,
   return MCDisassembler::Success;
 }
 
+static DecodeStatus decodeXqccmpRlistS0(MCInst &Inst, uint32_t Imm,
+                                        uint64_t Address, const void *Decoder) {
+  if (Imm <= 4)
+    return MCDisassembler::Fail;
+  Inst.addOperand(MCOperand::createImm(Imm));
+  return MCDisassembler::Success;
+}
+
 static DecodeStatus decodeRegReg(MCInst &Inst, uint32_t Insn, uint64_t Address,
                                  const MCDisassembler *Decoder) {
   uint32_t Rs1 = fieldFromInstruction(Insn, 0, 5);
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index db305b0083415..0ede9ba7cf712 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -343,6 +343,8 @@ enum OperandType : unsigned {
   OPERAND_RVKRNUM_0_7,
   OPERAND_RVKRNUM_1_10,
   OPERAND_RVKRNUM_2_14,
+  OPERAND_RLIST,
+  OPERAND_RLIST_S0,
   OPERAND_SPIMM,
   // Operand is a 3-bit rounding mode, '111' indicates FRM register.
   // Represents 'frm' argument passing to floating-point operations.
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index e589e6171d010..6163175347ca0 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -104,6 +104,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
                            SmallVectorImpl<MCFixup> &Fixups,
                            const MCSubtargetInfo &STI) const;
 
+  unsigned getRlistS0OpValue(const MCInst &MI, unsigned OpNo,
+                             SmallVectorImpl<MCFixup> &Fixups,
+                             const MCSubtargetInfo &STI) const;
+
   unsigned getRegReg(const MCInst &MI, unsigned OpNo,
                      SmallVectorImpl<MCFixup> &Fixups,
                      const MCSubtargetInfo &STI) const;
@@ -621,6 +625,17 @@ unsigned RISCVMCCodeEmitter::getRlistOpValue(const MCInst &MI, unsigned OpNo,
   return Imm;
 }
 
+unsigned
+RISCVMCCodeEmitter::getRlistS0OpValue(const MCInst &MI, unsigned OpNo,
+                                      SmallVectorImpl<MCFixup> &Fixups,
+                                      const MCSubtargetInfo &STI) const {
+  const MCOperand &MO = MI.getOperand(OpNo);
+  assert(MO.isImm() && "Rlist operand must be immediate");
+  auto Imm = MO.getImm();
+  assert(Imm >= 5 && "EABI is currently not implemented");
+  return Imm;
+}
+
 unsigned RISCVMCCodeEmitter::getRegReg(const MCInst &MI, unsigned OpNo,
                                        SmallVectorImpl<MCFixup> &Fixups,
                                        const MCSubtargetInfo &STI) const {
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 4fb11f278df97..7867fd7d516cc 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -2642,6 +2642,12 @@ bool RISCVInstrInfo::verifyInstruction(const MachineInstr &MI,
         case RISCVOp::OPERAND_RVKRNUM_2_14:
           Ok = Imm >= 2 && Imm <= 14;
           break;
+        case RISCVOp::OPERAND_RLIST:
+          Ok = Imm >= RISCVZC::RA && Imm <= RISCVZC::RA_S0_S11;
+          break;
+        case RISCVOp::OPERAND_RLIST_S0:
+          Ok = Imm >= RISCVZC::RA_S0 && Imm <= RISCVZC::RA_S0_S11;
+          break;
         case RISCVOp::OPERAND_SPIMM:
           Ok = (Imm & 0xf) == 0;
           break;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td
index 5bb9c1e4b228b..8cb1cf151f1ad 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td
@@ -25,6 +25,30 @@
 // Operand and SDNode transformation definitions.
 //===----------------------------------------------------------------------===//
 
+def RlistS0AsmOperand : AsmOperandClass {
+  let Name = "RlistS0";
+  let ParserMethod = "parseReglist<true>";
+  let RenderMethod = "addRlistOperands";
+  let DiagnosticType = "InvalidRlistS0";
+  let DiagnosticString = "operand must be {ra, s0[-sN]} or {x1, x8[-x9][, x18[-xN]]}";
+}
+
+def rlist_s0 : RISCVOp<OtherVT> {
+   let ParserMatchClass = RlistS0AsmOperand;
+   let PrintMethod = "printRlist";
+   let DecoderMethod = "decodeXqccmpRlistS0";
+   let EncoderMethod = "getRlistS0OpValue";
+   let MCOperandPredicate = [{
+    int64_t Imm;
+    if (!MCOp.evaluateAsConstantImm(Imm))
+      return false;
+    // 0~4 invalid for `qc.cm.pushfp`
+    return isUInt<4>(Imm) && Imm >= 5;
+  }];
+
+  string OperandType = "OPERAND_RLIST_S0";
+}
+
 //===----------------------------------------------------------------------===//
 // Instruction Formats
 //===----------------------------------------------------------------------===//
@@ -33,6 +57,20 @@
 // Instruction Class Templates
 //===----------------------------------------------------------------------===//
 
+class RVInstXqccmpCPPPFP<bits<5> funct5, string opcodestr,
+                         DAGOperand immtype = stackadj>
+    : RVInst16<(outs), (ins rlist_s0:$rlist, immtype:$stackadj),
+               opcodestr, "$rlist, $stackadj", [], InstFormatOther> {
+  bits<4> rlist;
+  bits<16> stackadj;
+
+  let Inst{1-0} = 0b10;
+  let Inst{3-2} = stackadj{5-4};
+  let Inst{7-4} = rlist;
+  let Inst{12-8} = funct5;
+  let Inst{15-13} = 0b101;
+}
+
 //===----------------------------------------------------------------------===//
 // Instructions
 //===----------------------------------------------------------------------===//
@@ -59,7 +97,7 @@ def QC_CM_PUSH : RVInstZcCPPP<0b11000, "qc.cm.push", negstackadj>,
                         ReadStoreData, ReadStoreData, ReadStoreData]>;
 
 let hasSideEffects = 0, mayLoad = 0, mayStore = 1, Uses = [X2], Defs = [X2, X8] in
-def QC_CM_PUSHFP : RVInstZcCPPP<0b11001, "qc.cm.pushfp", negstackadj>,
+def QC_CM_PUSHFP : RVInstXqccmpCPPPFP<0b11001, "qc.cm.pushfp", negstackadj>,
                    Sched<[WriteIALU, WriteIALU, ReadIALU, ReadStoreData, ReadStoreData,
                           ReadStoreData, ReadStoreData, ReadStoreData, ReadStoreData,
                           ReadStoreData, ReadStoreData, ReadStoreData, ReadStoreData,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZc.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZc.td
index efed74ca8c870..04c10220f5bf1 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZc.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZc.td
@@ -58,7 +58,7 @@ def NegStackAdjAsmOperand : AsmOperandClass {
   let RenderMethod = "addSpimmOperands";
 }
 
-def rlist : Operand<OtherVT> {
+def rlist : RISCVOp<OtherVT> {
    let ParserMatchClass = RlistAsmOperand;
    let PrintMethod = "printRlist";
    let DecoderMethod = "decodeZcmpRlist";
@@ -70,6 +70,8 @@ def rlist : Operand<OtherVT> {
     // 0~3 Reserved for EABI
     return isUInt<4>(Imm) && Imm >= 4;
   }];
+
+  let OperandType = "OPERAND_RLIST";
 }
 
 def stackadj : RISCVOp<OtherVT> {
diff --git a/llvm/test/MC/Disassembler/RISCV/xqccmp-invalid-rlist.txt b/llvm/test/MC/Disassembler/RISCV/xqccmp-invalid-rlist.txt
new file mode 100644
index 0000000000000..fe81c01a037af
--- /dev/null
+++ b/llvm/test/MC/Disassembler/RISCV/xqccmp-invalid-rlist.txt
@@ -0,0 +1,11 @@
+# RUN: not llvm-mc -disassemble -triple=riscv32 -mattr=+experimental-xqccmp %s \
+# RUN:   | FileCheck -check-prefixes=CHECK,CHECK-XQCCMP %s
+
+[0x00,0x00]
+# CHECK: unimp
+
+[0x42,0xb9]
+# CHECK-XQCCMP-NOT: qc.cm.pushfp {ra}, -{{[0-9]+}}
+
+[0x00,0x00]
+# CHECK: unimp
diff --git a/llvm/test/MC/RISCV/rv32xqccmp-invalid.s b/llvm/test/MC/RISCV/rv32xqccmp-invalid.s
index 74f96f076756c..059009de3d830 100644
--- a/llvm/test/MC/RISCV/rv32xqccmp-invalid.s
+++ b/llvm/test/MC/RISCV/rv32xqccmp-invalid.s
@@ -33,3 +33,7 @@ qc.cm.pushfp {ra, s0}, -12
 
 # CHECK-ERROR: error: stack adjustment for register list must be a multiple of 16 bytes in the range [16, 64]
 qc.cm.pop {ra, s0-s1}, -40
+
+# CHECK-ERROR: error: register list must include 's0' or 'x8'
+qc.cm.pushfp {ra}, -16
+
diff --git a/llvm/test/MC/RISCV/rv32xqccmp-valid.s b/llvm/test/MC/RISCV/rv32xqccmp-valid.s
index 5827777e524ca..b1f373ede247d 100644
--- a/llvm/test/MC/RISCV/rv32xqccmp-valid.s
+++ b/llvm/test/MC/RISCV/rv32xqccmp-valid.s
@@ -288,14 +288,6 @@ qc.cm.push {ra, s0-s11}, -112
 # CHECK-ASM: encoding: [0xfe,0xb8]
 qc.cm.push {x1, x8-x9, x18-x27}, -112
 
-# CHECK-ASM-AND-OBJ: qc.cm.pushfp {ra}, -16
-# CHECK-ASM: encoding: [0x42,0xb9]
-qc.cm.pushfp {ra}, -16
-
-# CHECK-ASM-AND-OBJ: qc.cm.pushfp {ra}, -16
-# CHECK-ASM: encoding: [0x42,0xb9]
-qc.cm.pushfp {x1}, -16
-
 # CHECK-ASM-AND-OBJ: qc.cm.pushfp {ra, s0}, -32
 # CHECK-ASM: encoding: [0x56,0xb9]
 qc.cm.pushfp {ra, s0}, -32
diff --git a/llvm/test/MC/RISCV/rv64e-xqccmp-valid.s b/llvm/test/MC/RISCV/rv64e-xqccmp-valid.s
index 8f9e3ce7ee533..003f9852b184a 100644
--- a/llvm/test/MC/RISCV/rv64e-xqccmp-valid.s
+++ b/llvm/test/MC/RISCV/rv64e-xqccmp-valid.s
@@ -72,10 +72,6 @@ qc.cm.push {ra, s0}, -32
 # CHECK-ASM: encoding: [0x62,0xb8]
 qc.cm.push {ra, s0-s1}, -32
 
-# CHECK-ASM-AND-OBJ: qc.cm.pushfp {ra}, -16
-# CHECK-ASM: encoding: [0x42,0xb9]
-qc.cm.pushfp {ra}, -16
-
 # CHECK-ASM-AND-OBJ: qc.cm.pushfp {ra, s0}, -32
 # CHECK-ASM: encoding: [0x56,0xb9]
 qc.cm.pushfp {ra, s0}, -32
diff --git a/llvm/test/MC/RISCV/rv64xqccmp-valid.s b/llvm/test/MC/RISCV/rv64xqccmp-valid.s
index 06ba33fe8a495..29cf6f0dfbdac 100644
--- a/llvm/test/MC/RISCV/rv64xqccmp-valid.s
+++ b/llvm/test/MC/RISCV/rv64xqccmp-valid.s
@@ -148,10 +148,6 @@ qc.cm.push {ra, s0-s11}, -112
 # CHECK-ASM: encoding: [0xf6,0xb8]
 qc.cm.push {ra, s0-s11}, -128
 
-# CHECK-ASM-AND-OBJ: qc.cm.pushfp {ra}, -16
-# CHECK-ASM: encoding: [0x42,0xb9]
-qc.cm.pushfp {ra}, -16
-
 # CHECK-ASM-AND-OBJ: qc.cm.pushfp {ra, s0}, -32
 # CHECK-ASM: encoding: [0x56,0xb9]
 qc.cm.pushfp {ra, s0}, -32

``````````

</details>


https://github.com/llvm/llvm-project/pull/133188


More information about the llvm-commits mailing list