Skip to content

Commit 2b9078f

Browse files
topperccuviper
authored andcommitted
[RISCV] Initial support .insn directive for the assembler.
This allows for a custom encoding to be emitted. It can also be used with inline assembly to allow the custom instruction to be register allocated like other instructions. I initially started from SystemZ's implementation, but some of the formats allow operands to be specified in multiple ways so I had to add support for matching different operand class lists for the same format. That implementation is a simplified version of what is emitted by tablegen for regular instructions. I've left out the compressed formats. And I haven't supported the named opcodes like LUI or OP_IMM_32. Those can be added in future patches. Documentation can be found here https://sourceware.org/binutils/docs-2.37/as/RISC_002dV_002dFormats.html Reviewed By: jrtc27, MaskRay Differential Revision: https://reviews.llvm.org/D108602
1 parent 01c8b65 commit 2b9078f

File tree

8 files changed

+407
-3
lines changed

8 files changed

+407
-3
lines changed

llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
169169

170170
bool parseDirectiveOption();
171171
bool parseDirectiveAttribute();
172+
bool parseDirectiveInsn(SMLoc L);
172173

173174
void setFeatureBits(uint64_t Feature, StringRef FeatureString) {
174175
if (!(getSTI().getFeatureBits()[Feature])) {
@@ -504,6 +505,24 @@ struct RISCVOperand : public MCParsedAsmOperand {
504505
return (isRV64() && isUInt<5>(Imm)) || isUInt<4>(Imm);
505506
}
506507

508+
bool isUImm2() const {
509+
int64_t Imm;
510+
RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None;
511+
if (!isImm())
512+
return false;
513+
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
514+
return IsConstantImm && isUInt<2>(Imm) && VK == RISCVMCExpr::VK_RISCV_None;
515+
}
516+
517+
bool isUImm3() const {
518+
int64_t Imm;
519+
RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None;
520+
if (!isImm())
521+
return false;
522+
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
523+
return IsConstantImm && isUInt<3>(Imm) && VK == RISCVMCExpr::VK_RISCV_None;
524+
}
525+
507526
bool isUImm5() const {
508527
int64_t Imm;
509528
RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None;
@@ -513,6 +532,15 @@ struct RISCVOperand : public MCParsedAsmOperand {
513532
return IsConstantImm && isUInt<5>(Imm) && VK == RISCVMCExpr::VK_RISCV_None;
514533
}
515534

535+
bool isUImm7() const {
536+
int64_t Imm;
537+
RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None;
538+
if (!isImm())
539+
return false;
540+
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
541+
return IsConstantImm && isUInt<7>(Imm) && VK == RISCVMCExpr::VK_RISCV_None;
542+
}
543+
516544
bool isSImm5() const {
517545
if (!isImm())
518546
return false;
@@ -1835,8 +1863,10 @@ bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) {
18351863

18361864
if (IDVal == ".option")
18371865
return parseDirectiveOption();
1838-
else if (IDVal == ".attribute")
1866+
if (IDVal == ".attribute")
18391867
return parseDirectiveAttribute();
1868+
if (IDVal == ".insn")
1869+
return parseDirectiveInsn(DirectiveID.getLoc());
18401870

18411871
return true;
18421872
}
@@ -2200,6 +2230,37 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
22002230
return false;
22012231
}
22022232

2233+
/// parseDirectiveInsn
2234+
/// ::= .insn [ format encoding, (operands (, operands)*) ]
2235+
bool RISCVAsmParser::parseDirectiveInsn(SMLoc L) {
2236+
MCAsmParser &Parser = getParser();
2237+
2238+
// Expect instruction format as identifier.
2239+
StringRef Format;
2240+
SMLoc ErrorLoc = Parser.getTok().getLoc();
2241+
if (Parser.parseIdentifier(Format))
2242+
return Error(ErrorLoc, "expected instruction format");
2243+
2244+
if (Format != "r" && Format != "r4" && Format != "i" && Format != "b" &&
2245+
Format != "sb" && Format != "u" && Format != "j" && Format != "uj" &&
2246+
Format != "s")
2247+
return Error(ErrorLoc, "invalid instruction format");
2248+
2249+
std::string FormatName = (".insn_" + Format).str();
2250+
2251+
ParseInstructionInfo Info;
2252+
SmallVector<std::unique_ptr<MCParsedAsmOperand>, 8> Operands;
2253+
2254+
if (ParseInstruction(Info, FormatName, L, Operands))
2255+
return true;
2256+
2257+
unsigned Opcode;
2258+
uint64_t ErrorInfo;
2259+
return MatchAndEmitInstruction(L, Opcode, Operands, Parser.getStreamer(),
2260+
ErrorInfo,
2261+
/*MatchingInlineAsm=*/false);
2262+
}
2263+
22032264
void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) {
22042265
MCInst CInst;
22052266
bool Res = compressInst(CInst, Inst, getSTI(), S.getContext());

llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,11 @@ enum {
158158
namespace RISCVOp {
159159
enum OperandType : unsigned {
160160
OPERAND_FIRST_RISCV_IMM = MCOI::OPERAND_FIRST_TARGET,
161-
OPERAND_UIMM4 = OPERAND_FIRST_RISCV_IMM,
161+
OPERAND_UIMM2 = OPERAND_FIRST_RISCV_IMM,
162+
OPERAND_UIMM3,
163+
OPERAND_UIMM4,
162164
OPERAND_UIMM5,
165+
OPERAND_UIMM7,
163166
OPERAND_UIMM12,
164167
OPERAND_SIMM12,
165168
OPERAND_UIMM20,

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
358358
}
359359
} else if (Kind == MCExpr::SymbolRef &&
360360
cast<MCSymbolRefExpr>(Expr)->getKind() == MCSymbolRefExpr::VK_None) {
361-
if (Desc.getOpcode() == RISCV::JAL) {
361+
if (MIFrm == RISCVII::InstFormatJ) {
362362
FixupKind = RISCV::fixup_riscv_jal;
363363
} else if (MIFrm == RISCVII::InstFormatB) {
364364
FixupKind = RISCV::fixup_riscv_branch;

llvm/lib/Target/RISCV/RISCVInstrFormats.td

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,135 @@ class RVInstJ<RISCVOpcode opcode, dag outs, dag ins, string opcodestr,
406406
let Inst{11-7} = rd;
407407
let Opcode = opcode.Value;
408408
}
409+
410+
//===----------------------------------------------------------------------===//
411+
// Instruction classes for .insn directives
412+
//===----------------------------------------------------------------------===//
413+
414+
class DirectiveInsnR<dag outs, dag ins, string argstr>
415+
: RVInst<outs, ins, "", "", [], InstFormatR> {
416+
bits<7> opcode;
417+
bits<7> funct7;
418+
bits<3> funct3;
419+
420+
bits<5> rs2;
421+
bits<5> rs1;
422+
bits<5> rd;
423+
424+
let Inst{31-25} = funct7;
425+
let Inst{24-20} = rs2;
426+
let Inst{19-15} = rs1;
427+
let Inst{14-12} = funct3;
428+
let Inst{11-7} = rd;
429+
let Opcode = opcode;
430+
431+
let AsmString = ".insn r " # argstr;
432+
}
433+
434+
class DirectiveInsnR4<dag outs, dag ins, string argstr>
435+
: RVInst<outs, ins, "", "", [], InstFormatR4> {
436+
bits<7> opcode;
437+
bits<2> funct2;
438+
bits<3> funct3;
439+
440+
bits<5> rs3;
441+
bits<5> rs2;
442+
bits<5> rs1;
443+
bits<5> rd;
444+
445+
let Inst{31-27} = rs3;
446+
let Inst{26-25} = funct2;
447+
let Inst{24-20} = rs2;
448+
let Inst{19-15} = rs1;
449+
let Inst{14-12} = funct3;
450+
let Inst{11-7} = rd;
451+
let Opcode = opcode;
452+
453+
let AsmString = ".insn r4 " # argstr;
454+
}
455+
456+
class DirectiveInsnI<dag outs, dag ins, string argstr>
457+
: RVInst<outs, ins, "", "", [], InstFormatI> {
458+
bits<7> opcode;
459+
bits<3> funct3;
460+
461+
bits<12> imm12;
462+
bits<5> rs1;
463+
bits<5> rd;
464+
465+
let Inst{31-20} = imm12;
466+
let Inst{19-15} = rs1;
467+
let Inst{14-12} = funct3;
468+
let Inst{11-7} = rd;
469+
let Opcode = opcode;
470+
471+
let AsmString = ".insn i " # argstr;
472+
}
473+
474+
class DirectiveInsnS<dag outs, dag ins, string argstr>
475+
: RVInst<outs, ins, "", "", [], InstFormatS> {
476+
bits<7> opcode;
477+
bits<3> funct3;
478+
479+
bits<12> imm12;
480+
bits<5> rs2;
481+
bits<5> rs1;
482+
483+
let Inst{31-25} = imm12{11-5};
484+
let Inst{24-20} = rs2;
485+
let Inst{19-15} = rs1;
486+
let Inst{14-12} = funct3;
487+
let Inst{11-7} = imm12{4-0};
488+
let Opcode = opcode;
489+
490+
let AsmString = ".insn s " # argstr;
491+
}
492+
493+
class DirectiveInsnB<dag outs, dag ins, string argstr>
494+
: RVInst<outs, ins, "", "", [], InstFormatB> {
495+
bits<7> opcode;
496+
bits<3> funct3;
497+
498+
bits<12> imm12;
499+
bits<5> rs2;
500+
bits<5> rs1;
501+
502+
let Inst{31} = imm12{11};
503+
let Inst{30-25} = imm12{9-4};
504+
let Inst{24-20} = rs2;
505+
let Inst{19-15} = rs1;
506+
let Inst{14-12} = funct3;
507+
let Inst{11-8} = imm12{3-0};
508+
let Inst{7} = imm12{10};
509+
let Opcode = opcode;
510+
511+
let AsmString = ".insn b " # argstr;
512+
}
513+
514+
class DirectiveInsnU<dag outs, dag ins, string argstr>
515+
: RVInst<outs, ins, "", "", [], InstFormatU> {
516+
bits<7> opcode;
517+
518+
bits<20> imm20;
519+
bits<5> rd;
520+
521+
let Inst{31-12} = imm20;
522+
let Inst{11-7} = rd;
523+
let Opcode = opcode;
524+
525+
let AsmString = ".insn u " # argstr;
526+
}
527+
528+
class DirectiveInsnJ<dag outs, dag ins, string argstr>
529+
: RVInst<outs, ins, "", "", [], InstFormatJ> {
530+
bits<7> opcode;
531+
532+
bits<20> imm20;
533+
bits<5> rd;
534+
535+
let Inst{31-12} = imm20;
536+
let Inst{11-7} = rd;
537+
let Opcode = opcode;
538+
539+
let AsmString = ".insn j " # argstr;
540+
}

llvm/lib/Target/RISCV/RISCVInstrInfo.td

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,34 @@ def uimmlog2xlen : Operand<XLenVT>, ImmLeaf<XLenVT, [{
152152
let OperandNamespace = "RISCVOp";
153153
}
154154

155+
def uimm2 : Operand<XLenVT> {
156+
let ParserMatchClass = UImmAsmOperand<2>;
157+
let DecoderMethod = "decodeUImmOperand<2>";
158+
let OperandType = "OPERAND_UIMM2";
159+
let OperandNamespace = "RISCVOp";
160+
}
161+
162+
def uimm3 : Operand<XLenVT> {
163+
let ParserMatchClass = UImmAsmOperand<3>;
164+
let DecoderMethod = "decodeUImmOperand<3>";
165+
let OperandType = "OPERAND_UIMM3";
166+
let OperandNamespace = "RISCVOp";
167+
}
168+
155169
def uimm5 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isUInt<5>(Imm);}]> {
156170
let ParserMatchClass = UImmAsmOperand<5>;
157171
let DecoderMethod = "decodeUImmOperand<5>";
158172
let OperandType = "OPERAND_UIMM5";
159173
let OperandNamespace = "RISCVOp";
160174
}
161175

176+
def uimm7 : Operand<XLenVT> {
177+
let ParserMatchClass = UImmAsmOperand<7>;
178+
let DecoderMethod = "decodeUImmOperand<7>";
179+
let OperandType = "OPERAND_UIMM7";
180+
let OperandNamespace = "RISCVOp";
181+
}
182+
162183
def simm12 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<12>(Imm);}]> {
163184
let ParserMatchClass = SImmAsmOperand<12>;
164185
let EncoderMethod = "getImmOpValue";
@@ -848,6 +869,87 @@ def : MnemonicAlias<"sbreak", "ebreak">;
848869
// that don't support this alias.
849870
def : InstAlias<"zext.b $rd, $rs", (ANDI GPR:$rd, GPR:$rs, 0xFF), 0>;
850871

872+
//===----------------------------------------------------------------------===//
873+
// .insn directive instructions
874+
//===----------------------------------------------------------------------===//
875+
876+
// isCodeGenOnly = 1 to hide them from the tablegened assembly parser.
877+
let isCodeGenOnly = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1,
878+
hasNoSchedulingInfo = 1 in {
879+
def InsnR : DirectiveInsnR<(outs AnyReg:$rd), (ins uimm7:$opcode, uimm3:$funct3,
880+
uimm7:$funct7, AnyReg:$rs1,
881+
AnyReg:$rs2),
882+
"$opcode, $funct3, $funct7, $rd, $rs1, $rs2">;
883+
def InsnR4 : DirectiveInsnR4<(outs AnyReg:$rd), (ins uimm7:$opcode,
884+
uimm3:$funct3,
885+
uimm2:$funct2,
886+
AnyReg:$rs1, AnyReg:$rs2,
887+
AnyReg:$rs3),
888+
"$opcode, $funct3, $funct2, $rd, $rs1, $rs2, $rs3">;
889+
def InsnI : DirectiveInsnI<(outs AnyReg:$rd), (ins uimm7:$opcode, uimm3:$funct3,
890+
AnyReg:$rs1, simm12:$imm12),
891+
"$opcode, $funct3, $rd, $rs1, $imm12">;
892+
def InsnI_Mem : DirectiveInsnI<(outs AnyReg:$rd), (ins uimm7:$opcode,
893+
uimm3:$funct3,
894+
AnyReg:$rs1,
895+
simm12:$imm12),
896+
"$opcode, $funct3, $rd, ${imm12}(${rs1})">;
897+
def InsnB : DirectiveInsnB<(outs), (ins uimm7:$opcode, uimm3:$funct3,
898+
AnyReg:$rs1, AnyReg:$rs2,
899+
simm13_lsb0:$imm12),
900+
"$opcode, $funct3, $rs1, $rs2, $imm12">;
901+
def InsnU : DirectiveInsnU<(outs AnyReg:$rd), (ins uimm7:$opcode,
902+
uimm20_lui:$imm20),
903+
"$opcode, $rd, $imm20">;
904+
def InsnJ : DirectiveInsnJ<(outs AnyReg:$rd), (ins uimm7:$opcode,
905+
simm21_lsb0_jal:$imm20),
906+
"$opcode, $rd, $imm20">;
907+
def InsnS : DirectiveInsnS<(outs), (ins uimm7:$opcode, uimm3:$funct3,
908+
AnyReg:$rs2, AnyReg:$rs1,
909+
simm12:$imm12),
910+
"$opcode, $funct3, $rs2, ${imm12}(${rs1})">;
911+
}
912+
913+
// Use InstAliases to match these so that we can combine the insn and format
914+
// into a mnemonic to use as the key for the tablegened asm matcher table. The
915+
// parser will take care of creating these fake mnemonics and will only do it
916+
// for known formats.
917+
let EmitPriority = 0 in {
918+
def : InstAlias<".insn_r $opcode, $funct3, $funct7, $rd, $rs1, $rs2",
919+
(InsnR AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, uimm7:$funct7,
920+
AnyReg:$rs1, AnyReg:$rs2)>;
921+
// Accept 4 register form of ".insn r" as alias for ".insn r4".
922+
def : InstAlias<".insn_r $opcode, $funct3, $funct7, $rd, $rs1, $rs2, $rs3",
923+
(InsnR4 AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, uimm7:$funct7,
924+
AnyReg:$rs1, AnyReg:$rs2, AnyReg:$rs3)>;
925+
def : InstAlias<".insn_r4 $opcode, $funct3, $funct7, $rd, $rs1, $rs2, $rs3",
926+
(InsnR4 AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, uimm7:$funct7,
927+
AnyReg:$rs1, AnyReg:$rs2, AnyReg:$rs3)>;
928+
def : InstAlias<".insn_i $opcode, $funct3, $rd, $rs1, $imm12",
929+
(InsnI AnyReg:$rd, uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1,
930+
simm12:$imm12)>;
931+
def : InstAlias<".insn_i $opcode, $funct3, $rd, ${imm12}(${rs1})",
932+
(InsnI_Mem AnyReg:$rd, uimm7:$opcode, uimm3:$funct3,
933+
AnyReg:$rs1, simm12:$imm12)>;
934+
def : InstAlias<".insn_b $opcode, $funct3, $rs1, $rs2, $imm12",
935+
(InsnB uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1,
936+
AnyReg:$rs2, simm13_lsb0:$imm12)>;
937+
// Accept sb as an alias for b.
938+
def : InstAlias<".insn_sb $opcode, $funct3, $rs1, $rs2, $imm12",
939+
(InsnB uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1,
940+
AnyReg:$rs2, simm13_lsb0:$imm12)>;
941+
def : InstAlias<".insn_u $opcode, $rd, $imm20",
942+
(InsnU AnyReg:$rd, uimm7:$opcode, uimm20_lui:$imm20)>;
943+
def : InstAlias<".insn_j $opcode, $rd, $imm20",
944+
(InsnJ AnyReg:$rd, uimm7:$opcode, simm21_lsb0_jal:$imm20)>;
945+
// Accept uj as an alias for j.
946+
def : InstAlias<".insn_uj $opcode, $rd, $imm20",
947+
(InsnJ AnyReg:$rd, uimm7:$opcode, simm21_lsb0_jal:$imm20)>;
948+
def : InstAlias<".insn_s $opcode, $funct3, $rs2, ${imm12}(${rs1})",
949+
(InsnS uimm7:$opcode, uimm3:$funct3, AnyReg:$rs2,
950+
AnyReg:$rs1, simm12:$imm12)>;
951+
}
952+
851953
//===----------------------------------------------------------------------===//
852954
// Pseudo-instructions and codegen patterns
853955
//

llvm/lib/Target/RISCV/RISCVRegisterInfo.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,3 +557,15 @@ foreach m = LMULList.m in {
557557
def FFLAGS : RISCVReg<0, "fflags">;
558558
def FRM : RISCVReg<0, "frm">;
559559
def FCSR : RISCVReg<0, "fcsr">;
560+
561+
// Any type register. Used for .insn directives when we don't know what the
562+
// register types could be.
563+
// NOTE: The alignment and size are bogus values. The Size needs to be non-zero
564+
// or tablegen will use "untyped" to determine the size which will assert.
565+
let isAllocatable = 0 in
566+
def AnyReg : RegisterClass<"RISCV", [untyped], 32,
567+
(add (sequence "X%u", 0, 31),
568+
(sequence "F%u_D", 0, 31),
569+
(sequence "V%u", 0, 31))> {
570+
let Size = 32;
571+
}

0 commit comments

Comments
 (0)