diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -169,6 +169,7 @@ bool parseDirectiveOption(); bool parseDirectiveAttribute(); + bool parseDirectiveInsn(SMLoc L); void setFeatureBits(uint64_t Feature, StringRef FeatureString) { if (!(getSTI().getFeatureBits()[Feature])) { @@ -504,6 +505,24 @@ return (isRV64() && isUInt<5>(Imm)) || isUInt<4>(Imm); } + bool isUImm2() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isUInt<2>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + + bool isUImm3() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isUInt<3>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isUImm5() const { int64_t Imm; RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; @@ -513,6 +532,15 @@ return IsConstantImm && isUInt<5>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; } + bool isUImm7() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK = RISCVMCExpr::VK_RISCV_None; + if (!isImm()) + return false; + bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); + return IsConstantImm && isUInt<7>(Imm) && VK == RISCVMCExpr::VK_RISCV_None; + } + bool isSImm5() const { if (!isImm()) return false; @@ -1835,8 +1863,10 @@ if (IDVal == ".option") return parseDirectiveOption(); - else if (IDVal == ".attribute") + if (IDVal == ".attribute") return parseDirectiveAttribute(); + if (IDVal == ".insn") + return parseDirectiveInsn(DirectiveID.getLoc()); return true; } @@ -2200,6 +2230,36 @@ return false; } +/// parseDirectiveInsn +/// ::= .insn [ format encoding, (operands (, operands)*) ] +bool RISCVAsmParser::parseDirectiveInsn(SMLoc L) { + MCAsmParser &Parser = getParser(); + + // Expect instruction format as identifier. + StringRef Format; + SMLoc ErrorLoc = Parser.getTok().getLoc(); + if (Parser.parseIdentifier(Format)) + return Error(ErrorLoc, "expected instruction format"); + + if (Format != "r" && Format != "r4" && Format != "i" && Format != "b" && + Format != "sb" && Format != "u" && Format != "j" && Format != "uj" && + Format != "s") + return Error(ErrorLoc, "invalid instruction format"); + + std::string FormatName = (".insn_" + Format).str(); + + ParseInstructionInfo Info; + SmallVector, 8> Operands; + + if (ParseInstruction(Info, FormatName, L, Operands)) + return true; + + unsigned Opcode; + uint64_t ErrorInfo; + return MatchAndEmitInstruction(L, Opcode, Operands, Parser.getStreamer(), ErrorInfo, + /* MatchingInlineAsm */ false); +} + void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { MCInst CInst; bool Res = compressInst(CInst, Inst, getSTI(), S.getContext()); diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -168,8 +168,11 @@ namespace RISCVOp { enum OperandType : unsigned { OPERAND_FIRST_RISCV_IMM = MCOI::OPERAND_FIRST_TARGET, - OPERAND_UIMM4 = OPERAND_FIRST_RISCV_IMM, + OPERAND_UIMM2 = OPERAND_FIRST_RISCV_IMM, + OPERAND_UIMM3, + OPERAND_UIMM4, OPERAND_UIMM5, + OPERAND_UIMM7, OPERAND_UIMM12, OPERAND_SIMM12, OPERAND_UIMM20, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -358,7 +358,7 @@ } } else if (Kind == MCExpr::SymbolRef && cast(Expr)->getKind() == MCSymbolRefExpr::VK_None) { - if (Desc.getOpcode() == RISCV::JAL) { + if (MIFrm == RISCVII::InstFormatJ) { FixupKind = RISCV::fixup_riscv_jal; } else if (MIFrm == RISCVII::InstFormatB) { FixupKind = RISCV::fixup_riscv_branch; diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td --- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td +++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td @@ -409,3 +409,135 @@ let Inst{11-7} = rd; let Opcode = opcode.Value; } + +//===----------------------------------------------------------------------===// +// Instruction classes for .insn directives +//===----------------------------------------------------------------------===// + +class DirectiveInsnR + : RVInst { + bits<7> opcode; + bits<7> funct7; + bits<3> funct3; + + bits<5> rs2; + bits<5> rs1; + bits<5> rd; + + let Inst{31-25} = funct7; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = ".insn r " # argstr; +} + +class DirectiveInsnR4 + : RVInst { + bits<7> opcode; + bits<2> funct2; + bits<3> funct3; + + bits<5> rs3; + bits<5> rs2; + bits<5> rs1; + bits<5> rd; + + let Inst{31-27} = rs3; + let Inst{26-25} = funct2; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = ".insn r4 " # argstr; +} + +class DirectiveInsnI + : RVInst { + bits<7> opcode; + bits<3> funct3; + + bits<12> imm12; + bits<5> rs1; + bits<5> rd; + + let Inst{31-20} = imm12; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = ".insn i " # argstr; +} + +class DirectiveInsnS + : RVInst { + bits<7> opcode; + bits<3> funct3; + + bits<12> imm12; + bits<5> rs2; + bits<5> rs1; + + let Inst{31-25} = imm12{11-5}; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-7} = imm12{4-0}; + let Opcode = opcode; + + let AsmString = ".insn s " # argstr; +} + +class DirectiveInsnB + : RVInst { + bits<7> opcode; + bits<3> funct3; + + bits<12> imm12; + bits<5> rs2; + bits<5> rs1; + + let Inst{31} = imm12{11}; + let Inst{30-25} = imm12{9-4}; + let Inst{24-20} = rs2; + let Inst{19-15} = rs1; + let Inst{14-12} = funct3; + let Inst{11-8} = imm12{3-0}; + let Inst{7} = imm12{10}; + let Opcode = opcode; + + let AsmString = ".insn b " # argstr; +} + +class DirectiveInsnU + : RVInst { + bits<7> opcode; + + bits<20> imm20; + bits<5> rd; + + let Inst{31-12} = imm20; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = ".insn u " # argstr; +} + +class DirectiveInsnJ + : RVInst { + bits<7> opcode; + + bits<20> imm20; + bits<5> rd; + + let Inst{31-12} = imm20; + let Inst{11-7} = rd; + let Opcode = opcode; + + let AsmString = ".insn j " # argstr; +} diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -153,6 +153,20 @@ let OperandNamespace = "RISCVOp"; } +def uimm2 : Operand { + let ParserMatchClass = UImmAsmOperand<2>; + let DecoderMethod = "decodeUImmOperand<2>"; + let OperandType = "OPERAND_UIMM2"; + let OperandNamespace = "RISCVOp"; +} + +def uimm3 : Operand { + let ParserMatchClass = UImmAsmOperand<3>; + let DecoderMethod = "decodeUImmOperand<3>"; + let OperandType = "OPERAND_UIMM3"; + let OperandNamespace = "RISCVOp"; +} + def uimm5 : Operand, ImmLeaf(Imm);}]> { let ParserMatchClass = UImmAsmOperand<5>; let DecoderMethod = "decodeUImmOperand<5>"; @@ -160,6 +174,13 @@ let OperandNamespace = "RISCVOp"; } +def uimm7 : Operand { + let ParserMatchClass = UImmAsmOperand<7>; + let DecoderMethod = "decodeUImmOperand<7>"; + let OperandType = "OPERAND_UIMM7"; + let OperandNamespace = "RISCVOp"; +} + def simm12 : Operand, ImmLeaf(Imm);}]> { let ParserMatchClass = SImmAsmOperand<12>; let EncoderMethod = "getImmOpValue"; @@ -850,6 +871,84 @@ def : InstAlias<"zext.b $rd, $rs", (ANDI GPR:$rd, GPR:$rs, 0xFF), 0>; //===----------------------------------------------------------------------===// +// .insn directive instructions +//===----------------------------------------------------------------------===// + +// 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), (ins uimm7:$opcode, uimm3:$funct3, + uimm7:$funct7, AnyReg:$rd, + AnyReg:$rs1, AnyReg:$rs2), + "$opcode, $funct3, $funct7, $rd, $rs1, $rs2">; + def InsnR4 : DirectiveInsnR4<(outs), (ins uimm7:$opcode, uimm3:$funct3, + uimm2:$funct2, AnyReg:$rd, + AnyReg:$rs1, AnyReg:$rs2, + AnyReg:$rs3), + "$opcode, $funct3, $funct2, $rd, $rs1, $rs2, $rs3">; + def InsnI : DirectiveInsnI<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rd, AnyReg:$rs1, + simm12:$imm12), + "$opcode, $funct3, $rd, $rs1, $imm12">; + def InsnI_Mem : DirectiveInsnI<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rd, AnyReg:$rs1, + simm12:$imm12), + "$opcode, $funct3, $rd, ${imm12}(${rs1})">; + def InsnB : DirectiveInsnB<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rs1, AnyReg:$rs2, + simm13_lsb0:$imm12), + "$opcode, $funct3, $rs1, $rs2, $imm12">; + def InsnU : DirectiveInsnU<(outs), (ins uimm7:$opcode, AnyReg:$rd, + uimm20_lui:$imm20), + "$opcode, $rd, $imm20">; + def InsnJ : DirectiveInsnJ<(outs), (ins uimm7:$opcode, AnyReg:$rd, + simm21_lsb0_jal:$imm20), + "$opcode, $rd, $imm20">; + def InsnS : DirectiveInsnS<(outs), (ins uimm7:$opcode, uimm3:$funct3, + AnyReg:$rs2, AnyReg:$rs1, + simm12:$imm12), + "$opcode, $funct3, $rs2, ${imm12}(${rs1})">; +} + +// 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 +// parser will take care of creating these fake mnemonics and will only do it +// for known formats. +def : InstAlias<".insn_r $opcode, $funct3, $funct7, $rd, $rs1, $rs2", + (InsnR uimm7:$opcode, uimm3:$funct3, uimm7:$funct7, AnyReg:$rd, + AnyReg:$rs1, AnyReg:$rs2), 0>; +// Accept 4 register form of ".insn r" as alias for ".insn r4". +def : InstAlias<".insn_r $opcode, $funct3, $funct7, $rd, $rs1, $rs2, $rs3", + (InsnR4 uimm7:$opcode, uimm3:$funct3, uimm7:$funct7, AnyReg:$rd, + AnyReg:$rs1, AnyReg:$rs2, AnyReg:$rs3), 0>; +def : InstAlias<".insn_r4 $opcode, $funct3, $funct7, $rd, $rs1, $rs2, $rs3", + (InsnR4 uimm7:$opcode, uimm3:$funct3, uimm7:$funct7, AnyReg:$rd, + AnyReg:$rs1, AnyReg:$rs2, AnyReg:$rs3), 0>; +def : InstAlias<".insn_i $opcode, $funct3, $rd, $rs1, $imm12", + (InsnI uimm7:$opcode, uimm3:$funct3, AnyReg:$rd, + AnyReg:$rs1, simm12:$imm12), 0>; +def : InstAlias<".insn_i $opcode, $funct3, $rd, ${imm12}(${rs1})", + (InsnI_Mem uimm7:$opcode, uimm3:$funct3, AnyReg:$rd, + AnyReg:$rs1, simm12:$imm12), 0>; +def : InstAlias<".insn_b $opcode, $funct3, $rs1, $rs2, $imm12", + (InsnB uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1, + AnyReg:$rs2, simm13_lsb0:$imm12), 0>; +// Accept sb as an alias for b. +def : InstAlias<".insn_sb $opcode, $funct3, $rs1, $rs2, $imm12", + (InsnB uimm7:$opcode, uimm3:$funct3, AnyReg:$rs1, + AnyReg:$rs2, simm13_lsb0:$imm12), 0>; +def : InstAlias<".insn_u $opcode, $rd, $imm20", + (InsnU uimm7:$opcode, AnyReg:$rd, uimm20_lui:$imm20), 0>; +def : InstAlias<".insn_j $opcode, $rd, $imm20", + (InsnJ uimm7:$opcode, AnyReg:$rd, simm21_lsb0_jal:$imm20), 0>; +// Accept uj as an alias for j. +def : InstAlias<".insn_uj $opcode, $rd, $imm20", + (InsnJ uimm7:$opcode, AnyReg:$rd, simm21_lsb0_jal:$imm20), 0>; +def : InstAlias<".insn_s $opcode, $funct3, $rs2, ${imm12}(${rs1})", + (InsnS uimm7:$opcode, uimm3:$funct3, AnyReg:$rs2, + AnyReg:$rs1, simm12:$imm12)>; + +//===----------------------------------------------------------------------===// // Pseudo-instructions and codegen patterns // // Naming convention: For 'generic' pattern classes, we use the naming diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td @@ -557,3 +557,13 @@ def FFLAGS : RISCVReg<0, "fflags">; def FRM : RISCVReg<0, "frm">; def FCSR : RISCVReg<0, "fcsr">; + +// Any type register. Used for .insn directives when we don't know what the +// register types could be. +let isAllocatable = 0 in +def AnyReg : RegisterClass<"RISCV", [XLenVT], 32, + (add (sequence "X%u", 0, 31), + (sequence "F%u_D", 0, 31), + (sequence "V%u", 0, 31))> { + let RegInfos = XLenRI; +} diff --git a/llvm/test/MC/RISCV/insn-invalid.s b/llvm/test/MC/RISCV/insn-invalid.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/insn-invalid.s @@ -0,0 +1,14 @@ +# RUN: not llvm-mc -triple riscv32 < %s 2>&1 | FileCheck %s + +# Too many operands +.insn i 0x13, 0, a0, a1, 13, 14 # CHECK: :[[@LINE]]:33: error: invalid operand for instruction +.insn r 0x43, 0, 0, fa0, fa1, fa2, fa3, fa4 # CHECK: :[[@LINE]]:44: error: invalid operand for instruction + +# Too few operands +.insn r 0x33, 0, 0, a0, a1 # CHECK: :[[@LINE]]:1: error: too few operands for instruction +.insn i 0x13, 0, a0, a1 # CHECK: :[[@LINE]]:1: error: too few operands for instruction + +.insn r 0x33, 0, 0, a0, 13 # CHECK: :[[@LINE]]:28: error: invalid operand for instruction +.insn i 0x13, 0, a0, a1, a2 # CHECK: :[[@LINE]]:28: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo modifier or an integer in the range [-2048, 2047] + +.insn q 0x13, 0, a0, a1, 13, 14 # CHECK: :[[@LINE]]:7: error: invalid instruction format diff --git a/llvm/test/MC/RISCV/insn.s b/llvm/test/MC/RISCV/insn.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/RISCV/insn.s @@ -0,0 +1,77 @@ +# RUN: llvm-mc %s -triple=riscv32 -mattr=+f -riscv-no-aliases -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK-ASM %s +# RUN: llvm-mc %s -triple riscv64 -mattr=+f -riscv-no-aliases -show-encoding \ +# RUN: | FileCheck -check-prefixes=CHECK-ASM %s +# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+f < %s \ +# RUN: | llvm-objdump --mattr=+f -M no-aliases -d -r - \ +# RUN: | FileCheck -check-prefixes=CHECK-OBJ %s +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+f < %s \ +# RUN: | llvm-objdump --mattr=+f -M no-aliases -d -r - \ +# RUN: | FileCheck -check-prefixes=CHECK-OBJ %s + +target: + +# CHECK-ASM: .insn r 51, 0, 0, a0, a1, a2 +# CHECK-ASM: encoding: [0x33,0x85,0xc5,0x00] +# CHECK-OBJ: add a0, a1, a2 +.insn r 0x33, 0, 0, a0, a1, a2 + +# CHECK-ASM: .insn i 19, 0, a0, a1, 13 +# CHECK-ASM: encoding: [0x13,0x85,0xd5,0x00] +# CHECK-OBJ: addi a0, a1, 13 +.insn i 0x13, 0, a0, a1, 13 + +# CHECK-ASM: .insn i 103, 0, a0, 10(a1) +# CHECK-ASM: encoding: [0x67,0x85,0xa5,0x00] +# CHECK-OBJ: jalr a0, 10(a1) +.insn i 0x67, 0, a0, 10(a1) + +# CHECK-ASM: .insn i 3, 0, a0, 4(a1) +# CHECK-ASM: encoding: [0x03,0x85,0x45,0x00] +# CHECK-OBJ: lb a0, 4(a1) +.insn i 0x3, 0, a0, 4(a1) + +# CHECK-ASM: .insn b 99, 0, a0, a1, target +# CHECK-ASM: [0x63'A',A,0xb5'A',A] +# CHECK-OBJ: beq a0, a1, 0x0 +.insn sb 0x63, 0, a0, a1, target + +# CHECK-ASM: .insn b 99, 0, a0, a1, target +# CHECK-ASM: [0x63'A',A,0xb5'A',A] +# CHECK-OBJ: beq a0, a1, 0x0 +.insn b 0x63, 0, a0, a1, target + +# CHECK-ASM: .insn s 35, 0, a0, 4(a1) +# CHECK-ASM: encoding: [0x23,0x82,0xa5,0x00] +# CHECK-OBJ: sb a0, 4(a1) +.insn s 0x23, 0, a0, 4(a1) + +# CHECK-ASM: .insn u 55, a0, 4095 +# CHECK-ASM: encoding: [0x37,0xf5,0xff,0x00] +# CHECK-OBJ: lui a0, 4095 +.insn u 0x37, a0, 0xfff + +# CHECK-ASM: .insn j 111, a0, target +# CHECK-ASM: encoding: [0x6f,0bAAAA0101,A,A] +# CHECK-OBJ: jal a0, 0x0 +.insn uj 0x6f, a0, target + +# CHECK-ASM: .insn j 111, a0, target +# CHECK-ASM: encoding: [0x6f,0bAAAA0101,A,A] +# CHECK-OBJ: jal a0, 0x0 +.insn j 0x6f, a0, target + +# CHECK-ASM: .insn r4 67, 0, 0, fa0, fa1, fa2, fa3 +# CHECK-ASM: encoding: [0x43,0x85,0xc5,0x68] +# CHECK-OBJ: fmadd.s fa0, fa1, fa2, fa3, rne +.insn r 0x43, 0, 0, fa0, fa1, fa2, fa3 + +# CHECK-ASM: .insn r4 67, 0, 0, fa0, fa1, fa2, fa3 +# CHECK-ASM: encoding: [0x43,0x85,0xc5,0x68] +# CHECK-OBJ: fmadd.s fa0, fa1, fa2, fa3, rne +.insn r4 0x43, 0, 0, fa0, fa1, fa2, fa3 + +# CHECK-ASM: .insn i 3, 5, t1, -2048(t2) +# CHECK-ASM: encoding: [0x03,0xd3,0x03,0x80] +# CHECK-OBJ: lhu t1, -2048(t2) +.insn i 0x3, 0x5, x6, %lo(2048)(x7)