Skip to content

[RISCV] Add Xqci Insn Formats #132986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,16 @@ struct RISCVOperand final : public MCParsedAsmOperand {
VK == RISCVMCExpr::VK_None;
}

bool isSImm16() const {
if (!isImm())
return false;
RISCVMCExpr::Specifier VK = RISCVMCExpr::VK_None;
int64_t Imm;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
return IsConstantImm && isInt<16>(fixImmediateForRV32(Imm, isRV64Imm())) &&
VK == RISCVMCExpr::VK_None;
}

bool isSImm16NonZero() const {
if (!isImm())
return false;
Expand Down Expand Up @@ -1760,6 +1770,9 @@ bool RISCVAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
return generateImmOutOfRangeError(
Operands, ErrorInfo, -(1 << 12), (1 << 12) - 2,
"immediate must be a multiple of 2 bytes in the range");
case Match_InvalidSImm16:
return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 15),
(1 << 15) - 1);
case Match_InvalidSImm16NonZero:
return generateImmOutOfRangeError(
Operands, ErrorInfo, -(1 << 15), (1 << 15) - 1,
Expand Down Expand Up @@ -3368,10 +3381,13 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
return false;
}

bool isValidInsnFormat(StringRef Format, bool AllowC) {
bool isValidInsnFormat(StringRef Format, const MCSubtargetInfo &STI) {
return StringSwitch<bool>(Format)
.Cases("r", "r4", "i", "b", "sb", "u", "j", "uj", "s", true)
.Cases("cr", "ci", "ciw", "css", "cl", "cs", "ca", "cb", "cj", AllowC)
.Cases("cr", "ci", "ciw", "css", "cl", "cs", "ca", "cb", "cj",
STI.hasFeature(RISCV::FeatureStdExtZca))
.Cases("qc.eai", "qc.ei", "qc.eb", "qc.ej", "qc.es",
!STI.hasFeature(RISCV::Feature64Bit))
.Default(false);
}

Expand Down Expand Up @@ -3461,7 +3477,7 @@ bool RISCVAsmParser::parseDirectiveInsn(SMLoc L) {
return false;
}

if (!isValidInsnFormat(Format, AllowC))
if (!isValidInsnFormat(Format, getSTI()))
return Error(ErrorLoc, "invalid instruction format");

std::string FormatName = (".insn_" + Format).str();
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ enum {
InstFormatCLH = 19,
InstFormatCSB = 20,
InstFormatCSH = 21,
InstFormatOther = 22,
InstFormatQC_EAI = 22,
InstFormatQC_EI = 23,
InstFormatQC_EB = 24,
InstFormatQC_EJ = 25,
InstFormatQC_ES = 26,
InstFormatOther = 31,

InstFormatMask = 31,
InstFormatShift = 0,
Expand Down Expand Up @@ -332,6 +337,7 @@ enum OperandType : unsigned {
OPERAND_SIMM11,
OPERAND_SIMM12,
OPERAND_SIMM12_LSB00000,
OPERAND_SIMM16,
OPERAND_SIMM16_NONZERO,
OPERAND_SIMM20,
OPERAND_SIMM26,
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/Target/RISCV/RISCVInstrFormats.td
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,13 @@ def InstFormatCLB : InstFormat<18>;
def InstFormatCLH : InstFormat<19>;
def InstFormatCSB : InstFormat<20>;
def InstFormatCSH : InstFormat<21>;
def InstFormatOther : InstFormat<22>;
def InstFormatQC_EAI : InstFormat<22>;
def InstFormatQC_EI : InstFormat<23>;
def InstFormatQC_EB : InstFormat<24>;
def InstFormatQC_EJ : InstFormat<25>;
def InstFormatQC_ES : InstFormat<26>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a mismatch between the naming here and in RISCVBaseInfo above.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

def InstFormatOther : InstFormat<31>;


class RISCVVConstraint<bits<3> val> {
bits<3> Value = val;
Expand Down
45 changes: 28 additions & 17 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,33 @@ def AnyReg : Operand<XLenVT> {
let ParserMatchClass = AnyRegOperand;
}

// isCodeGenOnly = 1 to hide them from the tablegened assembly parser.
let isCodeGenOnly = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1,
hasNoSchedulingInfo = 1 in {
def Insn16 : RVInst16<(outs), (ins uimm16:$value), "", "", [], InstFormatOther> {
bits<16> value;

let Inst{15-0} = value;
let AsmString = ".insn 0x2, $value";
}
def Insn32 : RVInst<(outs), (ins uimm32:$value), "", "", [], InstFormatOther> {
bits<32> value;

let Inst{31-0} = value;
let AsmString = ".insn 0x4, $value";
}
def Insn48 : RVInst48<(outs), (ins uimm48:$value), "", "", [], InstFormatOther> {
bits<48> value;
let Inst{47-0} = value;
let AsmString = ".insn 0x6, $value";
}
def Insn64 : RVInst64<(outs), (ins uimm64:$value), "", "", [], InstFormatOther> {
bits<64> value;
let Inst{63-0} = value;
let AsmString = ".insn 0x8, $value";
}
} // isCodeGenOnly, hasSideEffects, mayLoad, mayStore, hasNoSchedulingInfo

// isCodeGenOnly = 1 to hide them from the tablegened assembly parser.
let isCodeGenOnly = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1,
hasNoSchedulingInfo = 1 in {
Expand Down Expand Up @@ -1177,23 +1204,7 @@ def InsnS : DirectiveInsnS<(outs), (ins uimm7_opcode:$opcode, uimm3:$funct3,
AnyReg:$rs2, AnyReg:$rs1,
simm12:$imm12),
"$opcode, $funct3, $rs2, ${imm12}(${rs1})">;
def Insn32 : RVInst<(outs), (ins uimm32:$value), "", "", [], InstFormatOther> {
bits<32> value;

let Inst{31-0} = value;
let AsmString = ".insn 0x4, $value";
}
def Insn48 : RVInst48<(outs), (ins uimm48:$value), "", "", [], InstFormatOther> {
bits<48> value;
let Inst{47-0} = value;
let AsmString = ".insn 0x6, $value";
}
def Insn64 : RVInst64<(outs), (ins uimm64:$value), "", "", [], InstFormatOther> {
bits<64> value;
let Inst{63-0} = value;
let AsmString = ".insn 0x8, $value";
}
}
} // isCodeGenOnly, hasSideEffects, mayLoad, mayStore, hasNoSchedulingInfo

// Use InstAliases to match these so that we can combine the insn and format
// into a mnemonic to use as the key for the tablegened asm matcher table. The
Expand Down
6 changes: 0 additions & 6 deletions llvm/lib/Target/RISCV/RISCVInstrInfoC.td
Original file line number Diff line number Diff line change
Expand Up @@ -799,12 +799,6 @@ def InsnCJ : DirectiveInsnCJ<(outs), (ins uimm2_opcode:$opcode,
uimm3:$funct3,
simm12_lsb0:$imm11),
"$opcode, $funct3, $imm11">;
def Insn16 : RVInst16<(outs), (ins uimm16:$value), "", "", [], InstFormatOther> {
bits<16> value;

let Inst{15-0} = value;
let AsmString = ".insn 0x2, $value";
}
Comment on lines -802 to -807
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was under a predicate, which I wasn't sure was right, so I moved it, with the others, into their own unpredicated block, separate from the .insn_<format> instructions/aliases.

}

// Use InstAliases to match these so that we can combine the insn and format
Expand Down
215 changes: 215 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ def simm5nonzero : RISCVOp<XLenVT>,

def simm11 : RISCVSImmLeafOp<11>;

def simm16 : RISCVSImmOp<16>;

def simm16nonzero : RISCVOp<XLenVT>,
ImmLeaf<XLenVT, [{return (Imm != 0) && isInt<16>(Imm);}]> {
let ParserMatchClass = SImmAsmOperand<16, "NonZero">;
Expand Down Expand Up @@ -130,6 +132,219 @@ def simm32_lsb0 : Operand<OtherVT> {
// Instruction Formats
//===----------------------------------------------------------------------===//


class DirectiveInsnQC_EAI<dag outs, dag ins, string argstr>
: RVInst48<outs, ins, "", "", [], InstFormatQC_EAI> {
bits<7> opcode;
bits<3> func3;
bits<1> func1;

bits<5> rd;
bits<32> imm32;

let Inst{47-16} = imm32;
let Inst{15} = func1;
let Inst{14-12} = func3;
let Inst{11-7} = rd;
let Inst{6-0} = opcode;

let AsmString = ".insn qc.eai " # argstr;
}

class DirectiveInsnQC_EI<dag outs, dag ins, string argstr>
: RVInst48<outs, ins, "", "", [], InstFormatQC_EI> {
bits<7> opcode;
bits<3> func3;
bits<2> func2;

bits<5> rd;
bits<5> rs1;
bits<26> imm26;

let Inst{47-32} = imm26{25-10};
let Inst{31-30} = func2;
let Inst{29-20} = imm26{9-0};
let Inst{19-15} = rs1;
let Inst{14-12} = func3;
let Inst{11-7} = rd;
let Inst{6-0} = opcode;

let AsmString = ".insn qc.ei " # argstr;
}

class DirectiveInsnQC_EB<dag outs, dag ins, string argstr>
: RVInst48<outs, ins, "", "", [], InstFormatQC_EB> {
bits<7> opcode;
bits<3> func3;
bits<5> func5;

bits<5> rs1;
bits<12> imm12; // This one is the PC-relative offset
bits<16> imm16;

let Inst{47-32} = imm16;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The qc.e. instructions all take a non zero imm16. While some take a simm16 there are two that take a uimm16 (qc.e.bgeui/qc.e.bltui). We will not be able to accommodate all valid values for them in simm16.

Also would the .insn for these instructions error out if the comparison is against 0? I took a look and could not find a test for it. Could you point me to it or add one please?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intention not to honour the "nonzero" bit of the 16-bit immediate is on purpose - the formats don't put these restrictions in, only individual instructions do.

As for the uimm16/simm16 difference, I'm not sure how to deal with this, but I want to punt on it, as users should be able to convert any unsigned immediates to the equivalent signed 16-bit immediate in their own assembly code, which would be accepted, and would give the expected bit pattern.

This is broadly a limitation of the instruction format idea in practice, especially when architects think of the fields as "just N bits" and push the sign/zero extension into the operation of different instructions.

let Inst{31} = imm12{11};
let Inst{30-25} = imm12{9-4};
let Inst{24-20} = func5;
let Inst{19-15} = rs1;
let Inst{14-12} = func3;
let Inst{11-8} = imm12{3-0};
let Inst{7} = imm12{10};
let Inst{6-0} = opcode;

let AsmString = ".insn qc.eb " # argstr;
}

class DirectiveInsnQC_EJ<dag outs, dag ins, string argstr>
: RVInst48<outs, ins, "", "", [], InstFormatQC_EJ> {
bits<7> opcode;
bits<3> func3;
bits<2> func2;
bits<5> func5;

bits<31> imm31;

let Inst{47-32} = imm31{30-15};
let Inst{31} = imm31{11};
let Inst{30-25} = imm31{9-4};
let Inst{24-20} = func5;
let Inst{19-17} = imm31{14-12};
let Inst{16-15} = func2;
let Inst{14-12} = func3;
let Inst{11-8} = imm31{3-0};
let Inst{7} = imm31{10};
let Inst{6-0} = opcode;

let AsmString = ".insn qc.ej " # argstr;
}

class DirectiveInsnQC_ES<dag outs, dag ins, string argstr>
: RVInst48<outs, ins, "", "", [], InstFormatQC_ES> {
bits<7> opcode;
bits<3> func3;
bits<2> func2;

bits<5> rs1;
bits<5> rs2;
bits<26> imm26;

let Inst{47-32} = imm26{25-10};
let Inst{31-30} = func2;
let Inst{29-25} = imm26{9-5};
let Inst{24-20} = rs2;
let Inst{19-15} = rs1;
let Inst{14-12} = func3;
let Inst{11-7} = imm26{4-0};
let Inst{6-0} = opcode;

let AsmString = ".insn qc.es " # argstr;
}


let isCodeGenOnly = true, hasSideEffects = true, mayLoad = true,
mayStore = true, hasNoSchedulingInfo = true, Predicates=[IsRV32] in {
def InsnQC_EAI : DirectiveInsnQC_EAI<(outs AnyReg:$rd),
(ins uimm7_opcode:$opcode,
uimm3:$func3,
uimm1:$func1,
simm32:$imm32),
"$opcode, $func3, $func1, $rd, $imm32">;
def InsnQC_EI : DirectiveInsnQC_EI<(outs AnyReg:$rd),
(ins uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs1,
simm26:$imm26),
"$opcode, $func3, $func2, $rd, $rs1, $imm26">;
def InsnQC_EI_Mem : DirectiveInsnQC_EI<(outs AnyReg:$rd),
(ins uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs1,
simm26:$imm26),
"$opcode, $func3, $func2, $rd, ${imm26}(${rs1})">;
def InsnQC_EB : DirectiveInsnQC_EB<(outs),
(ins uimm7_opcode:$opcode,
uimm3:$func3,
uimm5:$func5,
AnyReg:$rs1,
simm16:$imm16,
simm13_lsb0:$imm12),
"$opcode, $func3, $func5, $rs1, $imm16, $imm12">;
def InsnQC_EJ : DirectiveInsnQC_EJ<(outs),
(ins uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
uimm5:$func5,
simm32_lsb0:$imm31),
"$opcode, $func3, $func2, $func5, $imm31">;
def InsnQC_ES : DirectiveInsnQC_ES<(outs),
(ins uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs2,
AnyReg:$rs1,
simm26:$imm26),
"$opcode, $func3, $func2, $rs2, ${imm26}(${rs1})">;
} // isCodeGenOnly, hasSideEffects, mayLoad, mayStore, hasNoSchedulingInfo, Predicates

let EmitPriority = 0, Predicates = [IsRV32] in {
def : InstAlias<".insn_qc.eai $opcode, $func3, $func1, $rd, $imm32",
(InsnQC_EAI AnyReg:$rd,
uimm7_opcode:$opcode,
uimm3:$func3,
uimm1:$func1,
simm32:$imm32)>;
def : InstAlias<".insn_qc.ei $opcode, $func3, $func2, $rd, $rs1, $imm26",
(InsnQC_EI AnyReg:$rd,
uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs1,
simm26:$imm26)>;
def : InstAlias<".insn_qc.ei $opcode, $func3, $func2, $rd, ${imm26}(${rs1})",
(InsnQC_EI_Mem AnyReg:$rd,
uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs1,
simm26:$imm26)>;
def : InstAlias<".insn_qc.ei $opcode, $func3, $func2, $rd, (${rs1})",
(InsnQC_EI_Mem AnyReg:$rd,
uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs1,
0)>;
def : InstAlias<".insn_qc.eb $opcode, $func3, $func5, $rs1, $imm16, $imm12",
(InsnQC_EB uimm7_opcode:$opcode,
uimm3:$func3,
uimm5:$func5,
AnyReg:$rs1,
simm16:$imm16,
simm13_lsb0:$imm12)>;
def : InstAlias<".insn_qc.ej $opcode, $func3, $func2, $func5, $imm31",
(InsnQC_EJ uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
uimm5:$func5,
simm32_lsb0:$imm31)>;
def : InstAlias<".insn_qc.es $opcode, $func3, $func2, $rs2, ${imm26}(${rs1})",
(InsnQC_ES uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs2,
AnyReg:$rs1,
simm26:$imm26)>;
def : InstAlias<".insn_qc.es $opcode, $func3, $func2, $rs2, (${rs1})",
(InsnQC_ES uimm7_opcode:$opcode,
uimm3:$func3,
uimm2:$func2,
AnyReg:$rs2,
AnyReg:$rs1,
0)>;
} // EmitPriority = 0, Predicates = [IsRV32]

//===----------------------------------------------------------------------===//
// Instruction Class Templates
//===----------------------------------------------------------------------===//
Expand Down
Loading