[llvm] [RISCV] Re-implement Zacas MC layer support to make it usable for CodeGen. (PR #77418)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 9 10:00:18 PST 2024


https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/77418

>From 20d6c25fa145dfde86c6b84910934edda890b793 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Fri, 5 Jan 2024 12:25:54 -0800
Subject: [PATCH 1/2] [RISCV] Re-implement Zacas MC layer support to make it
 usable for CodeGen.

This changes the register class to GPRPair and adds the destination
register as a source with a tied operand constraint.

Parsing for the paired register is done with a custom parser that
checks for even register and converts it to its pair version. A
bit of care needs to be taken so that we only parse as a pair register
based on which instruction we're parsing and the mode in the subtarget.
This allows amocas.w to be parsed correcty in both modes.

I've added a FIXME to note that we should be creating pair registers
for Zdinx on RV32 to match the instructions CodeGen generates.
---
 .../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 74 +++++++++++++------
 .../RISCV/Disassembler/RISCVDisassembler.cpp  |  4 +
 llvm/lib/Target/RISCV/RISCVInstrInfoZa.td     | 47 +++++++++++-
 llvm/test/MC/RISCV/rv32zacas-invalid.s        | 20 ++---
 llvm/test/MC/RISCV/rv64zacas-invalid.s        | 18 ++---
 5 files changed, 120 insertions(+), 43 deletions(-)

diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 4250950a917299..3bc7638db172c7 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -199,6 +199,9 @@ class RISCVAsmParser : public MCTargetAsmParser {
   ParseStatus parseInsnDirectiveOpcode(OperandVector &Operands);
   ParseStatus parseInsnCDirectiveOpcode(OperandVector &Operands);
   ParseStatus parseGPRAsFPR(OperandVector &Operands);
+  template <bool IsRV64Inst>
+  ParseStatus parseGPRPair(OperandVector &Operands);
+  ParseStatus parseGPRPair(OperandVector &Operands, bool IsRV64Inst);
   ParseStatus parseFRMArg(OperandVector &Operands);
   ParseStatus parseFenceArg(OperandVector &Operands);
   ParseStatus parseReglist(OperandVector &Operands);
@@ -466,6 +469,11 @@ struct RISCVOperand final : public MCParsedAsmOperand {
 
   bool isGPRAsFPR() const { return isGPR() && Reg.IsGPRAsFPR; }
 
+  bool isGPRPair() const {
+    return Kind == KindTy::Register &&
+           RISCVMCRegisterClasses[RISCV::GPRPairRegClassID].contains(Reg.RegNum);
+  }
+
   static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm,
                                   RISCVMCExpr::VariantKind &VK) {
     if (auto *RE = dyn_cast<RISCVMCExpr>(Expr)) {
@@ -1300,6 +1308,10 @@ unsigned RISCVAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
       assert(Op.isReg());
 
       MCRegister Reg = Op.getReg();
+      if (RISCVMCRegisterClasses[RISCV::GPRPairRegClassID].contains(Reg))
+        continue;
+
+      // FIXME: We should form a paired register during parsing/matching.
       if (((Reg.id() - RISCV::X0) & 1) != 0)
         return Match_RequiresEvenGPRs;
     }
@@ -2222,6 +2234,47 @@ ParseStatus RISCVAsmParser::parseGPRAsFPR(OperandVector &Operands) {
   return ParseStatus::Success;
 }
 
+template <bool IsRV64>
+ParseStatus RISCVAsmParser::parseGPRPair(OperandVector &Operands) {
+  return parseGPRPair(Operands, IsRV64);
+}
+
+ParseStatus RISCVAsmParser::parseGPRPair(OperandVector &Operands,
+                                         bool IsRV64Inst) {
+  // If this is not an RV64 GPRPair instruction, don't parse as a GPRPair on
+  // RV64 as it will prevent matching the RV64 version of the same instruction
+  // that doesn't use a GPRPair.
+  // If this is an RV64 GPRPair instruction, there is no RV32 version so we can
+  // still parse as a pair.
+  if (!IsRV64Inst && isRV64())
+    return ParseStatus::NoMatch;
+
+  if (getLexer().isNot(AsmToken::Identifier))
+    return ParseStatus::NoMatch;
+
+  StringRef Name = getLexer().getTok().getIdentifier();
+  MCRegister RegNo = matchRegisterNameHelper(isRVE(), Name);
+
+  if (!RegNo)
+    return ParseStatus::NoMatch;
+
+  if (!RISCVMCRegisterClasses[RISCV::GPRRegClassID].contains(RegNo))
+    return ParseStatus::NoMatch;
+
+  if ((RegNo - RISCV::X0) & 1)
+    return TokError("register must be even");
+
+  SMLoc S = getLoc();
+  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Name.size());
+  getLexer().Lex();
+
+  const MCRegisterInfo *RI = getContext().getRegisterInfo();
+  unsigned Pair = RI->getMatchingSuperReg(RegNo, RISCV::sub_gpr_even,
+                                          &RISCVMCRegisterClasses[RISCV::GPRPairRegClassID]);
+  Operands.push_back(RISCVOperand::createReg(Pair, S, E));
+  return ParseStatus::Success;
+}
+
 ParseStatus RISCVAsmParser::parseFRMArg(OperandVector &Operands) {
   if (getLexer().isNot(AsmToken::Identifier))
     return TokError(
@@ -3335,27 +3388,6 @@ bool RISCVAsmParser::validateInstruction(MCInst &Inst,
     return Error(Loc, "Operand must be constant 4.");
   }
 
-  bool IsAMOCAS_D = Opcode == RISCV::AMOCAS_D || Opcode == RISCV::AMOCAS_D_AQ ||
-                    Opcode == RISCV::AMOCAS_D_RL ||
-                    Opcode == RISCV::AMOCAS_D_AQ_RL;
-  bool IsAMOCAS_Q = Opcode == RISCV::AMOCAS_Q || Opcode == RISCV::AMOCAS_Q_AQ ||
-                    Opcode == RISCV::AMOCAS_Q_RL ||
-                    Opcode == RISCV::AMOCAS_Q_AQ_RL;
-  if ((!isRV64() && IsAMOCAS_D) || IsAMOCAS_Q) {
-    unsigned Rd = Inst.getOperand(0).getReg();
-    unsigned Rs2 = Inst.getOperand(2).getReg();
-    assert(Rd >= RISCV::X0 && Rd <= RISCV::X31);
-    if ((Rd - RISCV::X0) % 2 != 0) {
-      SMLoc Loc = Operands[1]->getStartLoc();
-      return Error(Loc, "The destination register must be even.");
-    }
-    assert(Rs2 >= RISCV::X0 && Rs2 <= RISCV::X31);
-    if ((Rs2 - RISCV::X0) % 2 != 0) {
-      SMLoc Loc = Operands[2]->getStartLoc();
-      return Error(Loc, "The source register must be even.");
-    }
-  }
-
   const MCInstrDesc &MCID = MII.get(Opcode);
   if (!(MCID.TSFlags & RISCVII::ConstraintMask))
     return false;
diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index bc65cf2403b262..4dd039159e29dc 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -546,6 +546,10 @@ DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
                       !STI.hasFeature(RISCV::Feature64Bit),
                   DecoderTableRV32Zdinx32,
                   "RV32Zdinx table (Double in Integer and rv32)");
+    TRY_TO_DECODE(STI.hasFeature(RISCV::FeatureStdExtZacas) &&
+                      !STI.hasFeature(RISCV::Feature64Bit),
+                  DecoderTableRV32Zacas32,
+                  "RV32Zacas table (Compare-And-Swap and rv32)");
     TRY_TO_DECODE_FEATURE(RISCV::FeatureStdExtZfinx, DecoderTableRVZfinx32,
                           "RVZfinx table (Float in Integer)");
     TRY_TO_DECODE_FEATURE(RISCV::FeatureVendorXVentanaCondOps,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZa.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZa.td
index a09f5715b24ff5..ea8046d119d042 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZa.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZa.td
@@ -17,13 +17,54 @@
 // Zacas (Atomic Compare-and-Swap)
 //===----------------------------------------------------------------------===//
 
+def GPRPairRV32Operand : AsmOperandClass {
+  let Name = "GPRPairRV32";
+  let ParserMethod = "parseGPRPair<false>";
+  let PredicateMethod = "isGPRPair";
+  let RenderMethod = "addRegOperands";
+}
+
+def GPRPairRV64Operand : AsmOperandClass {
+  let Name = "GPRPairRV64";
+  let ParserMethod = "parseGPRPair<true>";
+  let PredicateMethod = "isGPRPair";
+  let RenderMethod = "addRegOperands";
+}
+
+def GPRPairRV32 : RegisterOperand<GPRPair> {
+  let ParserMatchClass = GPRPairRV32Operand;
+}
+
+def GPRPairRV64 : RegisterOperand<GPRPair> {
+  let ParserMatchClass = GPRPairRV64Operand;
+}
+
+let hasSideEffects = 0, mayLoad = 1, mayStore = 1, Constraints = "$rd = $rd_wb" in
+class AMO_cas<bits<5> funct5, bit aq, bit rl, bits<3> funct3, string opcodestr,
+              DAGOperand RC>
+    : RVInstRAtomic<funct5, aq, rl, funct3, OPC_AMO,
+                    (outs RC:$rd_wb), (ins RC:$rd, GPRMemZeroOffset:$rs1, RC:$rs2),
+                    opcodestr, "$rd, $rs2, $rs1">;
+
+multiclass AMO_cas_aq_rl<bits<5> funct5, bits<3> funct3, string opcodestr,
+                         DAGOperand RC> {
+  def ""     : AMO_cas<funct5, 0, 0, funct3, opcodestr, RC>;
+  def _AQ    : AMO_cas<funct5, 1, 0, funct3, opcodestr # ".aq", RC>;
+  def _RL    : AMO_cas<funct5, 0, 1, funct3, opcodestr # ".rl", RC>;
+  def _AQ_RL : AMO_cas<funct5, 1, 1, funct3, opcodestr # ".aqrl", RC>;
+}
+
 let Predicates = [HasStdExtZacas] in {
-defm AMOCAS_W : AMO_rr_aq_rl<0b00101, 0b010, "amocas.w">;
-defm AMOCAS_D : AMO_rr_aq_rl<0b00101, 0b011, "amocas.d">;
+defm AMOCAS_W : AMO_cas_aq_rl<0b00101, 0b010, "amocas.w", GPR>;
 } // Predicates = [HasStdExtZacas]
 
+let Predicates = [HasStdExtZacas, IsRV32], DecoderNamespace = "RV32Zacas"  in {
+defm AMOCAS_D_RV32 : AMO_cas_aq_rl<0b00101, 0b011, "amocas.d", GPRPairRV32>;
+} // Predicates = [HasStdExtZacas, IsRV32]
+
 let Predicates = [HasStdExtZacas, IsRV64] in {
-defm AMOCAS_Q : AMO_rr_aq_rl<0b00101, 0b100, "amocas.q">;
+defm AMOCAS_D_RV64 : AMO_cas_aq_rl<0b00101, 0b011, "amocas.d", GPR>;
+defm AMOCAS_Q : AMO_cas_aq_rl<0b00101, 0b100, "amocas.q", GPRPairRV64>;
 } // Predicates = [HasStdExtZacas, IsRV64]
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/test/MC/RISCV/rv32zacas-invalid.s b/llvm/test/MC/RISCV/rv32zacas-invalid.s
index f6a5858d9b3ee9..b86246ca2ed180 100644
--- a/llvm/test/MC/RISCV/rv32zacas-invalid.s
+++ b/llvm/test/MC/RISCV/rv32zacas-invalid.s
@@ -2,17 +2,17 @@
 
 # Non-zero offsets not supported for the third operand (rs1).
 amocas.w a1, a3, 1(a5) # CHECK: :[[@LINE]]:18: error: optional integer offset must be 0
-amocas.d a1, a3, 2(a5) # CHECK: :[[@LINE]]:18: error: optional integer offset must be 0
+amocas.d a0, a2, 2(a5) # CHECK: :[[@LINE]]:18: error: optional integer offset must be 0
 
 # First and second operands (rd and rs2) of amocas.d must be even for RV32.
-amocas.d a1, a2, (a1) # CHECK: :[[@LINE]]:10: error: The destination register must be even.
-amocas.d a0, a1, (a1) # CHECK: :[[@LINE]]:14: error: The source register must be even.
-amocas.d.aq a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: The destination register must be even.
-amocas.d.aq a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: The source register must be even.
-amocas.d.rl a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: The destination register must be even.
-amocas.d.rl a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: The source register must be even.
-amocas.d.aqrl a1, a2, (a1) # CHECK: :[[@LINE]]:15: error: The destination register must be even.
-amocas.d.aqrl a0, a1, (a1) # CHECK: :[[@LINE]]:19: error: The source register must be even.
+amocas.d a1, a2, (a1) # CHECK: :[[@LINE]]:10: error: register must be even
+amocas.d a0, a1, (a1) # CHECK: :[[@LINE]]:14: error: register must be even
+amocas.d.aq a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: register must be even
+amocas.d.aq a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: register must be even
+amocas.d.rl a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: register must be even
+amocas.d.rl a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: register must be even
+amocas.d.aqrl a1, a2, (a1) # CHECK: :[[@LINE]]:15: error: register must be even
+amocas.d.aqrl a0, a1, (a1) # CHECK: :[[@LINE]]:19: error: register must be even
 
 # amocas.q is not supported for RV32.
-amocas.q a1, a1, (a1) # CHECK: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
+amocas.q a0, a0, (a1) # CHECK: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
diff --git a/llvm/test/MC/RISCV/rv64zacas-invalid.s b/llvm/test/MC/RISCV/rv64zacas-invalid.s
index feb570a2952784..e6a4e4007e9787 100644
--- a/llvm/test/MC/RISCV/rv64zacas-invalid.s
+++ b/llvm/test/MC/RISCV/rv64zacas-invalid.s
@@ -3,14 +3,14 @@
 # Non-zero offsets not supported for the third operand (rs1).
 amocas.w a1, a3, 1(a5) # CHECK: :[[@LINE]]:18: error: optional integer offset must be 0
 amocas.d a1, a3, 2(a5) # CHECK: :[[@LINE]]:18: error: optional integer offset must be 0
-amocas.q a1, a3, 3(a5) # CHECK: :[[@LINE]]:18: error: optional integer offset must be 0
+amocas.q a0, a2, 3(a5) # CHECK: :[[@LINE]]:18: error: optional integer offset must be 0
 
 # First and second operands (rd and rs2) of amocas.q must be even.
-amocas.q a1, a2, (a1) # CHECK: :[[@LINE]]:10: error: The destination register must be even.
-amocas.q a0, a1, (a1) # CHECK: :[[@LINE]]:14: error: The source register must be even.
-amocas.q.aq a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: The destination register must be even.
-amocas.q.aq a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: The source register must be even.
-amocas.q.rl a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: The destination register must be even.
-amocas.q.rl a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: The source register must be even.
-amocas.q.aqrl a1, a2, (a1) # CHECK: :[[@LINE]]:15: error: The destination register must be even.
-amocas.q.aqrl a0, a1, (a1) # CHECK: :[[@LINE]]:19: error: The source register must be even.
+amocas.q a1, a2, (a1) # CHECK: :[[@LINE]]:10: error: register must be even
+amocas.q a0, a1, (a1) # CHECK: :[[@LINE]]:14: error: register must be even
+amocas.q.aq a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: register must be even
+amocas.q.aq a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: register must be even
+amocas.q.rl a1, a2, (a1) # CHECK: :[[@LINE]]:13: error: register must be even
+amocas.q.rl a0, a1, (a1) # CHECK: :[[@LINE]]:17: error: register must be even
+amocas.q.aqrl a1, a2, (a1) # CHECK: :[[@LINE]]:15: error: register must be even
+amocas.q.aqrl a0, a1, (a1) # CHECK: :[[@LINE]]:19: error: register must be even

>From ec24434f66ca0668e19cc48b53aba5b588f5fd46 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Mon, 8 Jan 2024 23:16:26 -0800
Subject: [PATCH 2/2] fixup! clang-format

---
 llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 3bc7638db172c7..7d42481db57fa9 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -199,8 +199,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
   ParseStatus parseInsnDirectiveOpcode(OperandVector &Operands);
   ParseStatus parseInsnCDirectiveOpcode(OperandVector &Operands);
   ParseStatus parseGPRAsFPR(OperandVector &Operands);
-  template <bool IsRV64Inst>
-  ParseStatus parseGPRPair(OperandVector &Operands);
+  template <bool IsRV64Inst> ParseStatus parseGPRPair(OperandVector &Operands);
   ParseStatus parseGPRPair(OperandVector &Operands, bool IsRV64Inst);
   ParseStatus parseFRMArg(OperandVector &Operands);
   ParseStatus parseFenceArg(OperandVector &Operands);
@@ -471,7 +470,8 @@ struct RISCVOperand final : public MCParsedAsmOperand {
 
   bool isGPRPair() const {
     return Kind == KindTy::Register &&
-           RISCVMCRegisterClasses[RISCV::GPRPairRegClassID].contains(Reg.RegNum);
+           RISCVMCRegisterClasses[RISCV::GPRPairRegClassID].contains(
+               Reg.RegNum);
   }
 
   static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm,
@@ -2269,8 +2269,9 @@ ParseStatus RISCVAsmParser::parseGPRPair(OperandVector &Operands,
   getLexer().Lex();
 
   const MCRegisterInfo *RI = getContext().getRegisterInfo();
-  unsigned Pair = RI->getMatchingSuperReg(RegNo, RISCV::sub_gpr_even,
-                                          &RISCVMCRegisterClasses[RISCV::GPRPairRegClassID]);
+  unsigned Pair = RI->getMatchingSuperReg(
+      RegNo, RISCV::sub_gpr_even,
+      &RISCVMCRegisterClasses[RISCV::GPRPairRegClassID]);
   Operands.push_back(RISCVOperand::createReg(Pair, S, E));
   return ParseStatus::Success;
 }



More information about the llvm-commits mailing list