Skip to content

Commit

Permalink
[RISCV] Support named opcodes in .insn directive.
Browse files Browse the repository at this point in the history
This patch is one of the TODO of commit, 2838797

We build the GenericTable for these opcodes, and also extend class RISCVOpcode, to store the names of opcodes.  Then we call the parseInsnDirectiveOpcode to parse the opcode filed in .insn directive.  We only allow users to write the recognized opcode names, or just write the immediate values in the 7 bits range.

Documentation: https://sourceware.org/binutils/docs-2.37/as/RISC_002dV_002dFormats.html

Reviewed By: craig.topper

Differential Revision: https://reviews.llvm.org/D115224
  • Loading branch information
Nelson Chu authored and topperc committed Dec 14, 2021
1 parent e7a95b0 commit 10a7198
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 42 deletions.
62 changes: 62 additions & 0 deletions llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Expand Up @@ -169,6 +169,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
OperandMatchResultTy parseJALOffset(OperandVector &Operands);
OperandMatchResultTy parseVTypeI(OperandVector &Operands);
OperandMatchResultTy parseMaskReg(OperandVector &Operands);
OperandMatchResultTy parseInsnDirectiveOpcode(OperandVector &Operands);

bool parseOperand(OperandVector &Operands, StringRef Mnemonic);

Expand Down Expand Up @@ -1306,6 +1307,67 @@ OperandMatchResultTy RISCVAsmParser::parseRegister(OperandVector &Operands,
return MatchOperand_Success;
}

OperandMatchResultTy
RISCVAsmParser::parseInsnDirectiveOpcode(OperandVector &Operands) {
SMLoc S = getLoc();
SMLoc E;
const MCExpr *Res;

switch (getLexer().getKind()) {
default:
return MatchOperand_NoMatch;
case AsmToken::LParen:
case AsmToken::Minus:
case AsmToken::Plus:
case AsmToken::Exclaim:
case AsmToken::Tilde:
case AsmToken::Integer:
case AsmToken::String: {
if (getParser().parseExpression(Res, E))
return MatchOperand_ParseFail;

auto *CE = dyn_cast<MCConstantExpr>(Res);
if (CE) {
int64_t Imm = CE->getValue();
if (isUInt<7>(Imm)) {
Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64()));
return MatchOperand_Success;
}
}

Twine Msg = "immediate must be an integer in the range";
Error(S, Msg + " [" + Twine(0) + ", " + Twine((1 << 7) - 1) + "]");
return MatchOperand_ParseFail;
}
case AsmToken::Identifier: {
StringRef Identifier;
if (getParser().parseIdentifier(Identifier))
return MatchOperand_ParseFail;

auto Opcode = RISCVInsnOpcode::lookupRISCVOpcodeByName(Identifier);
if (Opcode) {
Res = MCConstantExpr::create(Opcode->Value, getContext());
E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64()));
return MatchOperand_Success;
}

Twine Msg = "operand must be a valid opcode name or an "
"integer in the range";
Error(S, Msg + " [" + Twine(0) + ", " + Twine((1 << 7) - 1) + "]");
return MatchOperand_ParseFail;
}
case AsmToken::Percent: {
// Discard operand with modifier.
Twine Msg = "immediate must be an integer in the range";
Error(S, Msg + " [" + Twine(0) + ", " + Twine((1 << 7) - 1) + "]");
return MatchOperand_ParseFail;
}
}

return MatchOperand_NoMatch;
}

OperandMatchResultTy
RISCVAsmParser::parseCSRSystemRegister(OperandVector &Operands) {
SMLoc S = getLoc();
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
Expand Up @@ -27,6 +27,11 @@ namespace RISCVSysReg {
#include "RISCVGenSearchableTables.inc"
} // namespace RISCVSysReg

namespace RISCVInsnOpcode {
#define GET_RISCVOpcodesList_IMPL
#include "RISCVGenSearchableTables.inc"
} // namespace RISCVInsnOpcode

namespace RISCVABI {
ABI computeTargetABI(const Triple &TT, FeatureBitset FeatureBits,
StringRef ABIName) {
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
Expand Up @@ -299,6 +299,16 @@ struct SysReg {
#include "RISCVGenSearchableTables.inc"
} // end namespace RISCVSysReg

namespace RISCVInsnOpcode {
struct RISCVOpcode {
const char *Name;
unsigned Value;
};

#define GET_RISCVOpcodesList_DECL
#include "RISCVGenSearchableTables.inc"
} // end namespace RISCVInsnOpcode

namespace RISCVABI {

enum ABI {
Expand Down
59 changes: 36 additions & 23 deletions llvm/lib/Target/RISCV/RISCVInstrFormats.td
Expand Up @@ -107,31 +107,44 @@ def Vcompress : RISCVVConstraint<!or(VS2Constraint.Value,

// The following opcode names match those given in Table 19.1 in the
// RISC-V User-level ISA specification ("RISC-V base opcode map").
class RISCVOpcode<bits<7> val> {
class RISCVOpcode<string name, bits<7> val> {
string Name = name;
bits<7> Value = val;
}
def OPC_LOAD : RISCVOpcode<0b0000011>;
def OPC_LOAD_FP : RISCVOpcode<0b0000111>;
def OPC_MISC_MEM : RISCVOpcode<0b0001111>;
def OPC_OP_IMM : RISCVOpcode<0b0010011>;
def OPC_AUIPC : RISCVOpcode<0b0010111>;
def OPC_OP_IMM_32 : RISCVOpcode<0b0011011>;
def OPC_STORE : RISCVOpcode<0b0100011>;
def OPC_STORE_FP : RISCVOpcode<0b0100111>;
def OPC_AMO : RISCVOpcode<0b0101111>;
def OPC_OP : RISCVOpcode<0b0110011>;
def OPC_LUI : RISCVOpcode<0b0110111>;
def OPC_OP_32 : RISCVOpcode<0b0111011>;
def OPC_MADD : RISCVOpcode<0b1000011>;
def OPC_MSUB : RISCVOpcode<0b1000111>;
def OPC_NMSUB : RISCVOpcode<0b1001011>;
def OPC_NMADD : RISCVOpcode<0b1001111>;
def OPC_OP_FP : RISCVOpcode<0b1010011>;
def OPC_OP_V : RISCVOpcode<0b1010111>;
def OPC_BRANCH : RISCVOpcode<0b1100011>;
def OPC_JALR : RISCVOpcode<0b1100111>;
def OPC_JAL : RISCVOpcode<0b1101111>;
def OPC_SYSTEM : RISCVOpcode<0b1110011>;
def RISCVOpcodesList : GenericTable {
let FilterClass = "RISCVOpcode";
let Fields = [
"Name", "Value"
];
let PrimaryKey = [ "Value" ];
let PrimaryKeyName = "lookupRISCVOpcodeByValue";
}
def lookupRISCVOpcodeByName : SearchIndex {
let Table = RISCVOpcodesList;
let Key = [ "Name" ];
}
def OPC_LOAD : RISCVOpcode<"LOAD", 0b0000011>;
def OPC_LOAD_FP : RISCVOpcode<"LOAD_FP", 0b0000111>;
def OPC_MISC_MEM : RISCVOpcode<"MISC_MEM", 0b0001111>;
def OPC_OP_IMM : RISCVOpcode<"OP_IMM", 0b0010011>;
def OPC_AUIPC : RISCVOpcode<"AUIPC", 0b0010111>;
def OPC_OP_IMM_32 : RISCVOpcode<"OP_IMM_32", 0b0011011>;
def OPC_STORE : RISCVOpcode<"STORE", 0b0100011>;
def OPC_STORE_FP : RISCVOpcode<"STORE_FP", 0b0100111>;
def OPC_AMO : RISCVOpcode<"AMO", 0b0101111>;
def OPC_OP : RISCVOpcode<"OP", 0b0110011>;
def OPC_LUI : RISCVOpcode<"LUI", 0b0110111>;
def OPC_OP_32 : RISCVOpcode<"OP_32", 0b0111011>;
def OPC_MADD : RISCVOpcode<"MADD", 0b1000011>;
def OPC_MSUB : RISCVOpcode<"MSUB", 0b1000111>;
def OPC_NMSUB : RISCVOpcode<"NMSUB", 0b1001011>;
def OPC_NMADD : RISCVOpcode<"NMADD", 0b1001111>;
def OPC_OP_FP : RISCVOpcode<"OP_FP", 0b1010011>;
def OPC_OP_V : RISCVOpcode<"OP_V", 0b1010111>;
def OPC_BRANCH : RISCVOpcode<"BRANCH", 0b1100011>;
def OPC_JALR : RISCVOpcode<"JALR", 0b1100111>;
def OPC_JAL : RISCVOpcode<"JAL", 0b1101111>;
def OPC_SYSTEM : RISCVOpcode<"SYSTEM", 0b1110011>;

class RVInst<dag outs, dag ins, string opcodestr, string argstr,
list<dag> pattern, InstFormat format>
Expand Down
52 changes: 33 additions & 19 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.td
Expand Up @@ -174,6 +174,20 @@ def uimm5 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isUInt<5>(Imm);}]> {
let OperandNamespace = "RISCVOp";
}

def InsnDirectiveOpcode : AsmOperandClass {
let Name = "InsnDirectiveOpcode";
let ParserMethod = "parseInsnDirectiveOpcode";
let RenderMethod = "addImmOperands";
let PredicateMethod = "isImm";
}

def uimm7_opcode : Operand<XLenVT> {
let ParserMatchClass = InsnDirectiveOpcode;
let DecoderMethod = "decodeUImmOperand<7>";
let OperandType = "OPERAND_UIMM7";
let OperandNamespace = "RISCVOp";
}

def uimm7 : Operand<XLenVT> {
let ParserMatchClass = UImmAsmOperand<7>;
let DecoderMethod = "decodeUImmOperand<7>";
Expand Down Expand Up @@ -878,35 +892,35 @@ def : InstAlias<"zext.b $rd, $rs", (ANDI GPR:$rd, GPR:$rs, 0xFF), 0>;
// isCodeGenOnly = 1 to hide them from the tablegened assembly parser.
let isCodeGenOnly = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1,
hasNoSchedulingInfo = 1 in {
def InsnR : DirectiveInsnR<(outs AnyReg:$rd), (ins uimm7:$opcode, uimm3:$funct3,
def InsnR : DirectiveInsnR<(outs AnyReg:$rd), (ins uimm7_opcode:$opcode, uimm3:$funct3,
uimm7:$funct7, AnyReg:$rs1,
AnyReg:$rs2),
"$opcode, $funct3, $funct7, $rd, $rs1, $rs2">;
def InsnR4 : DirectiveInsnR4<(outs AnyReg:$rd), (ins uimm7:$opcode,
def InsnR4 : DirectiveInsnR4<(outs AnyReg:$rd), (ins uimm7_opcode:$opcode,
uimm3:$funct3,
uimm2:$funct2,
AnyReg:$rs1, AnyReg:$rs2,
AnyReg:$rs3),
"$opcode, $funct3, $funct2, $rd, $rs1, $rs2, $rs3">;
def InsnI : DirectiveInsnI<(outs AnyReg:$rd), (ins uimm7:$opcode, uimm3:$funct3,
def InsnI : DirectiveInsnI<(outs AnyReg:$rd), (ins uimm7_opcode:$opcode, uimm3:$funct3,
AnyReg:$rs1, simm12:$imm12),
"$opcode, $funct3, $rd, $rs1, $imm12">;
def InsnI_Mem : DirectiveInsnI<(outs AnyReg:$rd), (ins uimm7:$opcode,
def InsnI_Mem : DirectiveInsnI<(outs AnyReg:$rd), (ins uimm7_opcode:$opcode,
uimm3:$funct3,
AnyReg:$rs1,
simm12:$imm12),
"$opcode, $funct3, $rd, ${imm12}(${rs1})">;
def InsnB : DirectiveInsnB<(outs), (ins uimm7:$opcode, uimm3:$funct3,
def InsnB : DirectiveInsnB<(outs), (ins uimm7_opcode:$opcode, uimm3:$funct3,
AnyReg:$rs1, AnyReg:$rs2,
simm13_lsb0:$imm12),
"$opcode, $funct3, $rs1, $rs2, $imm12">;
def InsnU : DirectiveInsnU<(outs AnyReg:$rd), (ins uimm7:$opcode,
def InsnU : DirectiveInsnU<(outs AnyReg:$rd), (ins uimm7_opcode:$opcode,
uimm20_lui:$imm20),
"$opcode, $rd, $imm20">;
def InsnJ : DirectiveInsnJ<(outs AnyReg:$rd), (ins uimm7:$opcode,
def InsnJ : DirectiveInsnJ<(outs AnyReg:$rd), (ins uimm7_opcode:$opcode,
simm21_lsb0_jal:$imm20),
"$opcode, $rd, $imm20">;
def InsnS : DirectiveInsnS<(outs), (ins uimm7:$opcode, uimm3:$funct3,
def InsnS : DirectiveInsnS<(outs), (ins uimm7_opcode:$opcode, uimm3:$funct3,
AnyReg:$rs2, AnyReg:$rs1,
simm12:$imm12),
"$opcode, $funct3, $rs2, ${imm12}(${rs1})">;
Expand All @@ -918,37 +932,37 @@ def InsnS : DirectiveInsnS<(outs), (ins uimm7:$opcode, uimm3:$funct3,
// for known formats.
let EmitPriority = 0 in {
def : InstAlias<".insn_r $opcode, $funct3, $funct7, $rd, $rs1, $rs2",
(InsnR AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, uimm7:$funct7,
(InsnR AnyReg:$rd, uimm7_opcode:$opcode, uimm3:$funct3, uimm7:$funct7,
AnyReg:$rs1, AnyReg:$rs2)>;
// Accept 4 register form of ".insn r" as alias for ".insn r4".
def : InstAlias<".insn_r $opcode, $funct3, $funct2, $rd, $rs1, $rs2, $rs3",
(InsnR4 AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, uimm2:$funct2,
(InsnR4 AnyReg:$rd, uimm7_opcode:$opcode, uimm3:$funct3, uimm2:$funct2,
AnyReg:$rs1, AnyReg:$rs2, AnyReg:$rs3)>;
def : InstAlias<".insn_r4 $opcode, $funct3, $funct2, $rd, $rs1, $rs2, $rs3",
(InsnR4 AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, uimm2:$funct2,
(InsnR4 AnyReg:$rd, uimm7_opcode:$opcode, uimm3:$funct3, uimm2:$funct2,
AnyReg:$rs1, AnyReg:$rs2, AnyReg:$rs3)>;
def : InstAlias<".insn_i $opcode, $funct3, $rd, $rs1, $imm12",
(InsnI AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1,
(InsnI AnyReg:$rd, uimm7_opcode:$opcode, uimm3:$funct3, AnyReg:$rs1,
simm12:$imm12)>;
def : InstAlias<".insn_i $opcode, $funct3, $rd, ${imm12}(${rs1})",
(InsnI_Mem AnyReg:$rd, uimm7:$opcode, uimm3:$funct3,
(InsnI_Mem AnyReg:$rd, uimm7_opcode:$opcode, uimm3:$funct3,
AnyReg:$rs1, simm12:$imm12)>;
def : InstAlias<".insn_b $opcode, $funct3, $rs1, $rs2, $imm12",
(InsnB uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1,
(InsnB uimm7_opcode:$opcode, uimm3:$funct3, AnyReg:$rs1,
AnyReg:$rs2, simm13_lsb0:$imm12)>;
// Accept sb as an alias for b.
def : InstAlias<".insn_sb $opcode, $funct3, $rs1, $rs2, $imm12",
(InsnB uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1,
(InsnB uimm7_opcode:$opcode, uimm3:$funct3, AnyReg:$rs1,
AnyReg:$rs2, simm13_lsb0:$imm12)>;
def : InstAlias<".insn_u $opcode, $rd, $imm20",
(InsnU AnyReg:$rd, uimm7:$opcode, uimm20_lui:$imm20)>;
(InsnU AnyReg:$rd, uimm7_opcode:$opcode, uimm20_lui:$imm20)>;
def : InstAlias<".insn_j $opcode, $rd, $imm20",
(InsnJ AnyReg:$rd, uimm7:$opcode, simm21_lsb0_jal:$imm20)>;
(InsnJ AnyReg:$rd, uimm7_opcode:$opcode, simm21_lsb0_jal:$imm20)>;
// Accept uj as an alias for j.
def : InstAlias<".insn_uj $opcode, $rd, $imm20",
(InsnJ AnyReg:$rd, uimm7:$opcode, simm21_lsb0_jal:$imm20)>;
(InsnJ AnyReg:$rd, uimm7_opcode:$opcode, simm21_lsb0_jal:$imm20)>;
def : InstAlias<".insn_s $opcode, $funct3, $rs2, ${imm12}(${rs1})",
(InsnS uimm7:$opcode, uimm3:$funct3, AnyReg:$rs2,
(InsnS uimm7_opcode:$opcode, uimm3:$funct3, AnyReg:$rs2,
AnyReg:$rs1, simm12:$imm12)>;
}

Expand Down
3 changes: 3 additions & 0 deletions llvm/test/MC/RISCV/insn-invalid.s
Expand Up @@ -18,5 +18,8 @@
.insn r 0x33, 8, 0, a0, a1, a2 # CHECK: :[[@LINE]]:17: error: immediate must be an integer in the range [0, 7]
.insn r4 0x43, 0, 4, fa0, fa1, fa2, fa3 # CHECK: :[[@LINE]]:21: error: immediate must be an integer in the range [0, 3]

# Unrecognized opcode name
.insn r UNKNOWN, 0, a1, a2, a3 #CHECK: :[[@LINE]]:9: error: operand must be a valid opcode name or an integer in the range [0, 127]

# Make fake mnemonics we use to match these in the tablegened asm match table isn't exposed.
.insn_i 0x13, 0, a0, a1, 13, 14 # CHECK: :[[@LINE]]:1: error: unknown directive

0 comments on commit 10a7198

Please sign in to comment.