[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