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

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.
This commit is contained in:
Craig Topper 2024-01-10 09:18:40 -08:00 committed by GitHub
parent 14e7dac92a
commit c053e9f0f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 43 deletions

View File

@ -199,6 +199,8 @@ 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 +468,12 @@ public:
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,48 @@ 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 +3389,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;

View File

@ -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,

View File

@ -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]
//===----------------------------------------------------------------------===//

View File

@ -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{{$}}

View File

@ -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