Recommit "[RISCV] Add .insn support for compressed formats."
authorCraig Topper <craig.topper@sifive.com>
Mon, 27 Mar 2023 19:30:37 +0000 (12:30 -0700)
committerCraig Topper <craig.topper@sifive.com>
Mon, 27 Mar 2023 19:30:37 +0000 (12:30 -0700)
I think the failure was caused by a mistake in an earlier patch.

Original commit message:

We've supported .insn for non-compressed for a while. This finishes the compressed supported.

Differential Revision: https://reviews.llvm.org/D146663

llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
llvm/lib/Target/RISCV/RISCVInstrFormatsC.td
llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
llvm/lib/Target/RISCV/RISCVInstrInfo.td
llvm/lib/Target/RISCV/RISCVInstrInfoC.td
llvm/test/MC/RISCV/insn_c-invalid.s [new file with mode: 0644]
llvm/test/MC/RISCV/insn_c.s [new file with mode: 0644]

index 108ecf8..7b6da03 100644 (file)
@@ -172,6 +172,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
   OperandMatchResultTy parseVTypeI(OperandVector &Operands);
   OperandMatchResultTy parseMaskReg(OperandVector &Operands);
   OperandMatchResultTy parseInsnDirectiveOpcode(OperandVector &Operands);
+  OperandMatchResultTy parseInsnCDirectiveOpcode(OperandVector &Operands);
   OperandMatchResultTy parseGPRAsFPR(OperandVector &Operands);
   OperandMatchResultTy parseFRMArg(OperandVector &Operands);
   OperandMatchResultTy parseFenceArg(OperandVector &Operands);
@@ -374,6 +375,13 @@ public:
             RISCVMCRegisterClasses[RISCV::FPR64RegClassID].contains(Reg.RegNum) ||
             RISCVMCRegisterClasses[RISCV::VRRegClassID].contains(Reg.RegNum));
   }
+  bool isAnyRegC() const {
+    return Kind == KindTy::Register &&
+           (RISCVMCRegisterClasses[RISCV::GPRCRegClassID].contains(
+                Reg.RegNum) ||
+            RISCVMCRegisterClasses[RISCV::FPR64CRegClassID].contains(
+                Reg.RegNum));
+  }
   bool isImm() const override { return Kind == KindTy::Immediate; }
   bool isMem() const override { return false; }
   bool isSystemRegister() const { return Kind == KindTy::SystemRegister; }
@@ -569,9 +577,11 @@ public:
 
   bool isUImm2() const { return IsUImm<2>(); }
   bool isUImm3() const { return IsUImm<3>(); }
+  bool isUImm4() const { return IsUImm<4>(); }
   bool isUImm5() const { return IsUImm<5>(); }
   bool isUImm6() const { return IsUImm<6>(); }
   bool isUImm7() const { return IsUImm<7>(); }
+  bool isUImm8() const { return IsUImm<8>(); }
 
   bool isRnumArg() const {
     int64_t Imm;
@@ -1250,10 +1260,16 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                                       "immediate must be one of");
   case Match_InvalidUImm3:
     return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 3) - 1);
+  case Match_InvalidUImm4:
+    return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 4) - 1);
   case Match_InvalidUImm5:
     return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 5) - 1);
+  case Match_InvalidUImm6:
+    return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 6) - 1);
   case Match_InvalidUImm7:
     return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 7) - 1);
+  case Match_InvalidUImm8:
+    return generateImmOutOfRangeError(Operands, ErrorInfo, 0, (1 << 8) - 1);
   case Match_InvalidSImm5:
     return generateImmOutOfRangeError(Operands, ErrorInfo, -(1 << 4),
                                       (1 << 4) - 1);
@@ -1540,6 +1556,67 @@ RISCVAsmParser::parseInsnDirectiveOpcode(OperandVector &Operands) {
 }
 
 OperandMatchResultTy
+RISCVAsmParser::parseInsnCDirectiveOpcode(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 (Imm >= 0 && Imm <= 2) {
+        Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64()));
+        return MatchOperand_Success;
+      }
+    }
+
+    break;
+  }
+  case AsmToken::Identifier: {
+    StringRef Identifier;
+    if (getParser().parseIdentifier(Identifier))
+      return MatchOperand_ParseFail;
+
+    unsigned Opcode;
+    if (Identifier == "C0")
+      Opcode = 0;
+    else if (Identifier == "C1")
+      Opcode = 1;
+    else if (Identifier == "C2")
+      Opcode = 2;
+    else
+      break;
+
+    Res = MCConstantExpr::create(Opcode, getContext());
+    E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
+    Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64()));
+    return MatchOperand_Success;
+  }
+  case AsmToken::Percent: {
+    // Discard operand with modifier.
+    break;
+  }
+  }
+
+  Error(S, "opcode must be a valid opcode name or an immediate in the range "
+           "[0, 2]");
+  return MatchOperand_ParseFail;
+}
+
+OperandMatchResultTy
 RISCVAsmParser::parseCSRSystemRegister(OperandVector &Operands) {
   SMLoc S = getLoc();
   const MCExpr *Res;
@@ -2472,6 +2549,13 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
   return false;
 }
 
+bool isValidInsnFormat(StringRef Format, bool AllowC) {
+  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)
+      .Default(false);
+}
+
 /// parseDirectiveInsn
 /// ::= .insn [ format encoding, (operands (, operands)*) ]
 bool RISCVAsmParser::parseDirectiveInsn(SMLoc L) {
@@ -2483,9 +2567,9 @@ bool RISCVAsmParser::parseDirectiveInsn(SMLoc L) {
   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")
+  bool AllowC = getSTI().hasFeature(RISCV::FeatureStdExtC) ||
+                getSTI().hasFeature(RISCV::FeatureExtZca);
+  if (!isValidInsnFormat(Format, AllowC))
     return Error(ErrorLoc, "invalid instruction format");
 
   std::string FormatName = (".insn_" + Format).str();
index 06c3b66..0e3a854 100644 (file)
@@ -249,6 +249,7 @@ enum OperandType : unsigned {
   OPERAND_UIMM7,
   OPERAND_UIMM7_LSB00,
   OPERAND_UIMM8_LSB00,
+  OPERAND_UIMM8,
   OPERAND_UIMM8_LSB000,
   OPERAND_UIMM9_LSB000,
   OPERAND_UIMM10_LSB00_NONZERO,
index 83b4b55..70fe672 100644 (file)
@@ -229,3 +229,169 @@ class RVInst16CSH<bits<6> funct6, bit funct1, bits<2> opcode, dag outs,
   let Inst{4-2} = rs2;
   let Inst{1-0} = opcode;
 }
+
+//===----------------------------------------------------------------------===//
+// Instruction classes for .insn directives
+//===----------------------------------------------------------------------===//
+
+class DirectiveInsnCR<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCR> {
+  bits<2> opcode;
+  bits<4> funct4;
+
+  bits<5> rs2;
+  bits<5> rd;
+
+  let Inst{15-12} = funct4;
+  let Inst{11-7} = rd;
+  let Inst{6-2} = rs2;
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn cr " # argstr;
+}
+
+class DirectiveInsnCI<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCI> {
+  bits<2> opcode;
+  bits<3> funct3;
+
+  bits<6> imm6;
+  bits<5> rd;
+
+  let Inst{15-13} = funct3;
+  let Inst{12} = imm6{5};
+  let Inst{11-7} = rd;
+  let Inst{6-2} = imm6{4-0};
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn ci " # argstr;
+}
+
+class DirectiveInsnCIW<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCIW> {
+  bits<2> opcode;
+  bits<3> funct3;
+
+  bits<8> imm8;
+  bits<3> rd;
+
+  let Inst{15-13} = funct3;
+  let Inst{12-5} = imm8;
+  let Inst{4-2} = rd;
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn ciw " # argstr;
+}
+
+class DirectiveInsnCSS<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCSS> {
+  bits<2> opcode;
+  bits<3> funct3;
+
+  bits<6> imm6;
+  bits<5> rs2;
+
+  let Inst{15-13} = funct3;
+  let Inst{12-7} = imm6;
+  let Inst{6-2} = rs2;
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn css " # argstr;
+}
+
+class DirectiveInsnCL<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCL> {
+  bits<2> opcode;
+  bits<3> funct3;
+
+  bits<5> imm5;
+  bits<3> rd;
+  bits<3> rs1;
+
+  let Inst{15-13} = funct3;
+  let Inst{12-10} = imm5{4-2};
+  let Inst{9-7} = rs1;
+  let Inst{6-5} = imm5{1-0};
+  let Inst{4-2} = rd;
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn cl " # argstr;
+}
+
+class DirectiveInsnCS<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCS> {
+  bits<2> opcode;
+  bits<3> funct3;
+
+  bits<5> imm5;
+  bits<3> rs2;
+  bits<3> rs1;
+
+  let Inst{15-13} = funct3;
+  let Inst{12-10} = imm5{4-2};
+  let Inst{9-7} = rs1;
+  let Inst{6-5} = imm5{1-0};
+  let Inst{4-2} = rs2;
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn cs " # argstr;
+}
+
+class DirectiveInsnCA<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCA> {
+  bits<2> opcode;
+  bits<6> funct6;
+  bits<2> funct2;
+
+  bits<3> rd;
+  bits<3> rs2;
+
+  let Inst{15-10} = funct6;
+  let Inst{9-7} = rd;
+  let Inst{6-5} = funct2;
+  let Inst{4-2} = rs2;
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn ca " # argstr;
+}
+
+class DirectiveInsnCB<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCB> {
+  bits<2> opcode;
+  bits<3> funct3;
+
+  bits<8> imm8;
+  bits<3> rs1;
+
+  let Inst{15-13} = funct3;
+  let Inst{12} = imm8{7};
+  let Inst{11-10} = imm8{3-2};
+  let Inst{9-7} = rs1;
+  let Inst{6-5} = imm8{6-5};
+  let Inst{4-3} = imm8{1-0};
+  let Inst{2} = imm8{4};
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn cb " # argstr;
+}
+
+class DirectiveInsnCJ<dag outs, dag ins, string argstr>
+  : RVInst16<outs, ins, "", "", [], InstFormatCJ> {
+  bits<2> opcode;
+  bits<3> funct3;
+
+  bits<11> imm11;
+
+  let Inst{15-13} = funct3;
+  let Inst{12} = imm11{10};
+  let Inst{11} = imm11{3};
+  let Inst{10-9} = imm11{8-7};
+  let Inst{8} = imm11{9};
+  let Inst{7} = imm11{5};
+  let Inst{6} = imm11{6};
+  let Inst{5-3} = imm11{2-0};
+  let Inst{2} = imm11{4};
+  let Inst{1-0} = opcode;
+
+  let AsmString = ".insn cj " # argstr;
+}
index 77f63ee..523895c 100644 (file)
@@ -1672,7 +1672,9 @@ bool RISCVInstrInfo::verifyInstruction(const MachineInstr &MI,
         CASE_OPERAND_UIMM(3)
         CASE_OPERAND_UIMM(4)
         CASE_OPERAND_UIMM(5)
+        CASE_OPERAND_UIMM(6)
         CASE_OPERAND_UIMM(7)
+        CASE_OPERAND_UIMM(8)
         CASE_OPERAND_UIMM(12)
         CASE_OPERAND_UIMM(20)
           // clang-format on
index 9f3e762..bbca4ea 100644 (file)
@@ -211,6 +211,13 @@ def uimm3 : Operand<XLenVT> {
   let OperandNamespace = "RISCVOp";
 }
 
+def uimm4 : Operand<XLenVT> {
+  let ParserMatchClass = UImmAsmOperand<4>;
+  let DecoderMethod = "decodeUImmOperand<4>";
+  let OperandType = "OPERAND_UIMM4";
+  let OperandNamespace = "RISCVOp";
+}
+
 def uimm5 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isUInt<5>(Imm);}]> {
   let ParserMatchClass = UImmAsmOperand<5>;
   let DecoderMethod = "decodeUImmOperand<5>";
@@ -246,6 +253,13 @@ def uimm7 : Operand<XLenVT> {
   let OperandNamespace = "RISCVOp";
 }
 
+def uimm8 : Operand<XLenVT> {
+  let ParserMatchClass = UImmAsmOperand<8>;
+  let DecoderMethod = "decodeUImmOperand<8>";
+  let OperandType = "OPERAND_UIMM8";
+  let OperandNamespace = "RISCVOp";
+}
+
 def simm12 : Operand<XLenVT>, ImmLeaf<XLenVT, [{return isInt<12>(Imm);}]> {
   let ParserMatchClass = SImmAsmOperand<12>;
   let EncoderMethod = "getImmOpValue";
index 7618f6c..91aaa43 100644 (file)
@@ -236,6 +236,20 @@ def simm12_lsb0 : Operand<XLenVT>,
   let OperandType = "OPERAND_PCREL";
 }
 
+def InsnCDirectiveOpcode : AsmOperandClass {
+  let Name = "InsnCDirectiveOpcode";
+  let ParserMethod = "parseInsnCDirectiveOpcode";
+  let RenderMethod = "addImmOperands";
+  let PredicateMethod = "isImm";
+}
+
+def uimm2_opcode : Operand<XLenVT> {
+  let ParserMatchClass = InsnCDirectiveOpcode;
+  let DecoderMethod = "decodeUImmOperand<2>";
+  let OperandType = "OPERAND_UIMM2";
+  let OperandNamespace = "RISCVOp";
+}
+
 //===----------------------------------------------------------------------===//
 // Instruction Class Templates
 //===----------------------------------------------------------------------===//
@@ -749,6 +763,100 @@ def : InstAlias<"c.fsdsp $rs2, (${rs1})", (C_FSDSP FPR64C:$rs2, SPMem:$rs1, 0)>;
 }
 } // EmitPriority = 0
 
+//===----------------------------------------------------------------------===//
+// .insn directive instructions
+//===----------------------------------------------------------------------===//
+
+def AnyRegCOperand : AsmOperandClass {
+  let Name = "AnyRegCOperand";
+  let RenderMethod = "addRegOperands";
+  let PredicateMethod = "isAnyRegC";
+}
+
+def AnyRegC : Operand<XLenVT> {
+  let OperandType = "OPERAND_REGISTER";
+  let ParserMatchClass = AnyRegCOperand;
+}
+
+// isCodeGenOnly = 1 to hide them from the tablegened assembly parser.
+let isCodeGenOnly = 1, hasSideEffects = 1, mayLoad = 1, mayStore = 1,
+    hasNoSchedulingInfo = 1, Predicates = [HasStdExtCOrZca] in {
+def InsnCR : DirectiveInsnCR<(outs AnyReg:$rd), (ins uimm2_opcode:$opcode,
+                                                     uimm4:$funct4,
+                                                     AnyReg:$rs2),
+                             "$opcode, $funct4, $rd, $rs2">;
+def InsnCI : DirectiveInsnCI<(outs AnyRegC:$rd), (ins uimm2_opcode:$opcode,
+                                                      uimm3:$funct3,
+                                                      simm6:$imm6),
+                             "$opcode, $funct3, $rd, $imm6">;
+def InsnCIW : DirectiveInsnCIW<(outs AnyRegC:$rd), (ins uimm2_opcode:$opcode,
+                                                        uimm3:$funct3,
+                                                        uimm8:$imm8),
+                               "$opcode, $funct3, $rd, $imm8">;
+def InsnCSS : DirectiveInsnCSS<(outs), (ins uimm2_opcode:$opcode,
+                                            uimm3:$funct3,
+                                            AnyReg:$rs2,
+                                            uimm6:$imm6),
+                               "$opcode, $funct3, $rs2, $imm6">;
+def InsnCL : DirectiveInsnCL<(outs AnyRegC:$rd), (ins uimm2_opcode:$opcode,
+                                                      uimm3:$funct3,
+                                                      AnyRegC:$rs1,
+                                                      uimm5:$imm5),
+                             "$opcode, $funct3, $rd, ${imm5}(${rs1})">;
+def InsnCS : DirectiveInsnCS<(outs), (ins uimm2_opcode:$opcode,
+                                          uimm3:$funct3,
+                                          AnyRegC:$rs2,
+                                          AnyRegC:$rs1,
+                                          uimm5:$imm5),
+                             "$opcode, $funct3, $rs2, ${imm5}(${rs1})">;
+def InsnCA : DirectiveInsnCA<(outs AnyRegC:$rd), (ins uimm2_opcode:$opcode,
+                                                      uimm6:$funct6,
+                                                      uimm2:$funct2,
+                                                      AnyRegC:$rs2),
+                             "$opcode, $funct6, $funct2, $rd, $rs2">;
+def InsnCB : DirectiveInsnCB<(outs), (ins uimm2_opcode:$opcode, uimm3:$funct3,
+                                          AnyRegC:$rs1,
+                                          simm9_lsb0:$imm8),
+                             "$opcode, $funct3, $rs1, $imm8">;
+def InsnCJ : DirectiveInsnCJ<(outs), (ins uimm2_opcode:$opcode,
+                                          uimm3:$funct3,
+                                          simm12_lsb0:$imm11),
+                             "$opcode, $funct3, $imm11">;
+}
+
+// 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.
+let EmitPriority = 0, Predicates = [HasStdExtCOrZca] in {
+def : InstAlias<".insn_cr $opcode, $funct4, $rd, $rs2",
+                (InsnCR AnyReg:$rd, uimm2_opcode:$opcode, uimm4:$funct4,
+                        AnyReg:$rs2)>;
+def : InstAlias<".insn_ci $opcode, $funct3, $rd, $imm6",
+                (InsnCI AnyRegC:$rd, uimm2_opcode:$opcode, uimm3:$funct3,
+                        simm6:$imm6)>;
+def : InstAlias<".insn_ciw $opcode, $funct3, $rd, $imm8",
+                (InsnCIW AnyRegC:$rd, uimm2_opcode:$opcode, uimm3:$funct3,
+                         uimm8:$imm8)>;
+def : InstAlias<".insn_css $opcode, $funct3, $rs2, $imm6",
+                (InsnCSS uimm2_opcode:$opcode, uimm3:$funct3, AnyReg:$rs2,
+                         uimm6:$imm6)>;
+def : InstAlias<".insn_cl $opcode, $funct3, $rd, ${imm5}(${rs1})",
+                (InsnCL AnyRegC:$rd, uimm2_opcode:$opcode, uimm3:$funct3,
+                        AnyRegC:$rs1, uimm5:$imm5)>;
+def : InstAlias<".insn_cs $opcode, $funct3, $rs2, ${imm5}(${rs1})",
+                (InsnCS uimm2_opcode:$opcode, uimm3:$funct3, AnyRegC:$rs2,
+                        AnyRegC:$rs1, uimm5:$imm5)>;
+def : InstAlias<".insn_ca $opcode, $funct6, $funct2, $rd, $rs2",
+                (InsnCA AnyRegC:$rd, uimm2_opcode:$opcode, uimm6:$funct6,
+                        uimm2:$funct2, AnyRegC:$rs2)>;
+def : InstAlias<".insn_cb $opcode, $funct3, $rs1, $imm8",
+                (InsnCB uimm2_opcode:$opcode, uimm3:$funct3, AnyRegC:$rs1,
+                        simm9_lsb0:$imm8)>;
+def : InstAlias<".insn_cj $opcode, $funct3, $imm11",
+                (InsnCJ uimm2_opcode:$opcode, uimm3:$funct3, simm12_lsb0:$imm11)>;
+}
+
 //===----------------------------------------------------------------------===/i
 // Compress Instruction tablegen backend.
 //===----------------------------------------------------------------------===//
diff --git a/llvm/test/MC/RISCV/insn_c-invalid.s b/llvm/test/MC/RISCV/insn_c-invalid.s
new file mode 100644 (file)
index 0000000..c983d32
--- /dev/null
@@ -0,0 +1,26 @@
+# RUN: not llvm-mc -triple riscv32 -mattr=+c < %s 2>&1 | FileCheck %s
+
+# Too many operands
+.insn ci  1, 0, a0, 13, 14 # CHECK: :[[#@LINE]]:25: error: invalid operand for instruction
+.insn cr  2, 9, a0, a1, a2 # CHECK: :[[#@LINE]]:25: error: invalid operand for instruction
+
+## Too few operands
+.insn ci  1, 0, a0 # CHECK: :[[#@LINE]]:1: error: too few operands for instruction
+.insn cr  2, 9, a0 # CHECK: :[[#@LINE]]:1: error: too few operands for instruction
+
+.insn cr  2, 9, a0, 13 # CHECK: :[[#@LINE]]:21: error: invalid operand for instruction
+.insn ci  1, 0, a0, a1 # CHECK: :[[#@LINE]]:21: error: immediate must be an integer in the range [-32, 31]
+
+.insn cq  0x13,  0,  a0, a1, 13, 14 # CHECK: :[[#@LINE]]:7: error: invalid instruction format
+
+# Invalid immediate
+.insn ci  3, 0, a0, 13 # CHECK: :[[#@LINE]]:11: error: opcode must be a valid opcode name or an immediate in the range [0, 2]
+.insn cr  2, 16, a0, a1 # CHECK: :[[#@LINE]]:14: error: immediate must be an integer in the range [0, 15]
+.insn ciw 0, 0, a0, 256 # CHECK: :[[#@LINE]]:21: error: immediate must be an integer in the range [0, 255]
+
+## Unrecognized opcode name
+.insn cr C3, 9, a0, a1 # CHECK: :[[#@LINE]]:10: error: opcode must be a valid opcode name or an immediate in the range [0, 2]
+
+## Make fake mnemonics we use to match these in the tablegened asm match table isn't exposed.
+.insn_cr  2, 9, a0, a1 # CHECK: :[[#@LINE]]:1: error: unknown directive
+
diff --git a/llvm/test/MC/RISCV/insn_c.s b/llvm/test/MC/RISCV/insn_c.s
new file mode 100644 (file)
index 0000000..959bd8a
--- /dev/null
@@ -0,0 +1,72 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+f,+c -riscv-no-aliases -show-encoding \
+# RUN:     | FileCheck -check-prefix=CHECK-ASM %s
+# RUN: llvm-mc %s -triple riscv64 -mattr=+f,+c -riscv-no-aliases -show-encoding \
+# RUN:     | FileCheck -check-prefix=CHECK-ASM %s
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+f,+c < %s \
+# RUN:     | llvm-objdump --mattr=+f,+c -M no-aliases -d -r - \
+# RUN:     | FileCheck -check-prefix=CHECK-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+f,+c < %s \
+# RUN:     | llvm-objdump --mattr=+f,+c -M no-aliases -d -r - \
+# RUN:     | FileCheck -check-prefix=CHECK-OBJ %s
+
+target:
+
+# CHECK-ASM: .insn cr  2, 9, a0, a1
+# CHECK-ASM: encoding: [0x2e,0x95]
+# CHECK-OBJ: c.add a0, a1
+.insn cr  2, 9, a0, a1
+
+# CHECK-ASM: .insn cr  2, 9, a0, a1
+# CHECK-ASM: encoding: [0x2e,0x95]
+# CHECK-OBJ: c.add a0, a1
+.insn cr C2, 9, a0, a1
+
+# CHECK-ASM: .insn ci  1, 0, a0, 13
+# CHECK-ASM: encoding: [0x35,0x05]
+# CHECK-OBJ: c.addi a0, 13
+.insn ci  1, 0, a0, 13
+
+# CHECK-ASM: .insn ci  1, 0, a0, 13
+# CHECK-ASM: encoding: [0x35,0x05]
+# CHECK-OBJ: c.addi a0, 13
+.insn ci C1, 0, a0, 13
+
+# CHECK-ASM: .insn ciw  0, 0, a0, 13
+# CHECK-ASM: encoding: [0xa8,0x01]
+# CHECK-OBJ: c.addi4spn a0, sp, 200
+.insn ciw  0, 0, a0, 13
+
+# CHECK-ASM: .insn ciw  0, 0, a0, 13
+# CHECK-ASM: encoding: [0xa8,0x01]
+# CHECK-OBJ: c.addi4spn a0, sp, 200
+.insn ciw C0, 0, a0, 13
+
+# CHECK-ASM: .insn css  2, 6, a0, 13
+# CHECK-ASM: encoding: [0xaa,0xc6]
+# CHECK-OBJ: c.swsp a0, 76(sp)
+.insn css  2, 6, a0, 13
+
+# CHECK-ASM: .insn cl  0, 2, a0, 13
+# CHECK-ASM: encoding: [0xa8,0x4d]
+# CHECK-OBJ: c.lw a0, 88(a1)
+.insn cl  0, 2, a0, 13(a1)
+
+# CHECK-ASM: .insn cs  0, 6, a0, 13
+# CHECK-ASM: encoding: [0xa8,0xcd]
+# CHECK-OBJ: c.sw a0, 88(a1)
+.insn cs  0, 6, a0, 13(a1)
+
+# CHECK-ASM: .insn ca  1, 35, 0, a0, a1
+# CHECK-ASM: encoding: [0x0d,0x8d]
+# CHECK-OBJ: c.sub a0, a1
+.insn ca  1, 35, 0, a0, a1
+
+# CHECK-ASM: .insn cb 1, 6, a0, target
+# CHECK-ASM: encoding: [0x01'A',0xc1'A']
+# CHECK-OBJ: c.beqz a0, 0x0 <target>
+.insn cb  1, 6, a0, target
+
+# CHECK-ASM: .insn cj 1, 5, target
+# CHECK-ASM: encoding: [0bAAAAAA01,0b101AAAAA]
+# CHECK-OBJ: c.j 0x0 <target>
+.insn cj  1, 5, target