Skip to content

Commit 2838797

Browse files
committed
[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 66a0b34 commit 2838797

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

llvm/lib/Target/RISCV/RISCVInstrInfo.td

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

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

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

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

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)