[llvm] 278b9af - [RISCV][MC] Emit Better Token Diagnostics (#209700)

via llvm-commits llvm-commits at lists.llvm.org
Sun Aug 2 23:02:24 PDT 2026


Author: Sam Elliott
Date: 2026-08-02T23:02:19-07:00
New Revision: 278b9af90c6fd32776ad3f4f4b5eab87f52587e2

URL: https://github.com/llvm/llvm-project/commit/278b9af90c6fd32776ad3f4f4b5eab87f52587e2
DIFF: https://github.com/llvm/llvm-project/commit/278b9af90c6fd32776ad3f4f4b5eab87f52587e2.diff

LOG: [RISCV][MC] Emit Better Token Diagnostics (#209700)

This implements an old FIXME in the AsmMatcherEmitter, which can now
emit a token-specific match error diagnostic id, and potentially a
token-specific error message to go along with the diagnostic id.

For RISC-V, the overall effect is to have fewer "invalid operand for
instruction" diagnostics and have more "expected '<TOKEN>'" diagnostics,
which, with multiple near miss support, gives the user the location that
token was expected (but not found).

The rejig to the order of checks in `validateOperandClass` do not
prevent backends from having custom operand kinds which can accept
tokens, as was available before.

The TableGen parts have been implemented in an opt-in way.

---

This was implemented with the assistance of AI.

Added: 
    llvm/test/TableGen/AsmMatcherTokens.td

Modified: 
    llvm/include/llvm/Target/Target.td
    llvm/lib/Target/RISCV/RISCV.td
    llvm/test/MC/RISCV/corev/XCVmem-invalid.s
    llvm/test/MC/RISCV/rv32c-invalid.s
    llvm/test/MC/RISCV/rv32zclsd-invalid.s
    llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s
    llvm/test/MC/RISCV/rvzicbop-invalid.s
    llvm/test/MC/RISCV/tlsdesc.s
    llvm/test/MC/RISCV/xqciio-invalid.s
    llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s
    llvm/test/MC/RISCV/xqcilsm-invalid.s
    llvm/test/TableGen/RegClassByHwMode.td
    llvm/utils/TableGen/AsmMatcherEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td
index c652af023c720..7d1e7a28b7b6e 100644
--- a/llvm/include/llvm/Target/Target.td
+++ b/llvm/include/llvm/Target/Target.td
@@ -1897,6 +1897,12 @@ class AsmParser {
   //    (Optional) Instruction id (see AsmMatcherEmitter.cpp for details),
   //    Number of required features (least first)
   bit PreferSmallerInstructions = false;
+
+  // Set to true to emit specific Match_Invalid<token> diagnostic types for
+  // literal text tokens (i.e. not $ operand references). When enabled, a
+  // Match_Invalid<name> entry is generated for each such token; targets can
+  // map these codes to more accurate error messages than Match_InvalidOperand.
+  bit EmitTokenDiagnosticTypes = false;
 }
 def DefaultAsmParser : AsmParser;
 

diff  --git a/llvm/lib/Target/RISCV/RISCV.td b/llvm/lib/Target/RISCV/RISCV.td
index 74b5f39570648..94de554ddfdce 100644
--- a/llvm/lib/Target/RISCV/RISCV.td
+++ b/llvm/lib/Target/RISCV/RISCV.td
@@ -92,6 +92,7 @@ def RISCVAsmParser : AsmParser {
   let ShouldEmitMatchRegisterAltName = true;
   let AllowDuplicateRegisterNames = true;
   let ReportMultipleNearMisses = true;
+  let EmitTokenDiagnosticTypes = true;
 }
 
 def RISCVAsmWriter : AsmWriter {

diff  --git a/llvm/test/MC/RISCV/corev/XCVmem-invalid.s b/llvm/test/MC/RISCV/corev/XCVmem-invalid.s
index 68e66ef3ce98a..0941d97a3c4fb 100644
--- a/llvm/test/MC/RISCV/corev/XCVmem-invalid.s
+++ b/llvm/test/MC/RISCV/corev/XCVmem-invalid.s
@@ -2,7 +2,9 @@
 # RUN:        | FileCheck %s --check-prefixes=CHECK-ERROR
 
 cv.lb t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.lb 0, (t1), 0
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
@@ -21,7 +23,9 @@ cv.lb t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:17: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.lb t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.lb 0, (t1), t1
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
@@ -36,7 +40,9 @@ cv.lb t0, (t1), t2, t3
 # CHECK-ERROR: :[[@LINE-1]]:21: error: unexpected extra operand for instruction
 
 cv.lbu t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:12: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:12: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:12: note: expected '('
 
 cv.lbu 0, (t1), 0
 # CHECK-ERROR: :[[@LINE-1]]:8: error: register must be a GPR
@@ -55,7 +61,9 @@ cv.lbu t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:18: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.lbu t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:12: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:12: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:12: note: expected '('
 
 cv.lbu 0, (t1), t1
 # CHECK-ERROR: :[[@LINE-1]]:8: error: register must be a GPR
@@ -70,7 +78,9 @@ cv.lbu t0, (t1), t2, t3
 # CHECK-ERROR: :[[@LINE-1]]:22: error: unexpected extra operand for instruction
 
 cv.lh t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.lh 0, (t1), 0
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
@@ -89,7 +99,9 @@ cv.lh t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:17: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.lh t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.lh t0, t1(0)
 # CHECK-ERROR: :[[@LINE-1]]:14: error: expected GPR register
@@ -107,7 +119,9 @@ cv.lh t0, (t1), t2, t3
 # CHECK-ERROR: :[[@LINE-1]]:21: error: unexpected extra operand for instruction
 
 cv.lhu t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:12: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:12: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:12: note: expected '('
 
 cv.lhu 0, (t1), 0
 # CHECK-ERROR: :[[@LINE-1]]:8: error: register must be a GPR
@@ -126,7 +140,9 @@ cv.lhu t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:18: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.lhu t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:12: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:12: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:12: note: expected '('
 
 cv.lhu t0, t1(0)
 # CHECK-ERROR: :[[@LINE-1]]:15: error: expected GPR register
@@ -144,7 +160,9 @@ cv.lhu t0, (t1), t2, t3
 # CHECK-ERROR: :[[@LINE-1]]:22: error: unexpected extra operand for instruction
 
 cv.lw t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.lw 0, (t1), 0
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
@@ -163,7 +181,9 @@ cv.lw t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:17: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.lw t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.lw t0, t1(0)
 # CHECK-ERROR: :[[@LINE-1]]:14: error: expected GPR register
@@ -181,13 +201,17 @@ cv.lw t0, (t1), t2, t3
 # CHECK-ERROR: :[[@LINE-1]]:21: error: unexpected extra operand for instruction
 
 cv.sb t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sb 0, (t0), 0
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
 
 cv.sb t0, 0(t1)
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sb t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
@@ -195,7 +219,9 @@ cv.sb t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:17: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.sb t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sb 0, (t1), t1
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
@@ -204,13 +230,17 @@ cv.sb t0
 # CHECK-ERROR: :[[@LINE-1]]:9: error: too few operands for instruction
 
 cv.sh t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sh 0, (t1), 0
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
 
 cv.sh t0, 0(t1)
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sh t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
@@ -218,7 +248,9 @@ cv.sh t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:17: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.sh t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sh 0, (t1), t1
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
@@ -227,13 +259,17 @@ cv.sh t0
 # CHECK-ERROR: :[[@LINE-1]]:9: error: too few operands for instruction
 
 cv.sw t0, (0), 0
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sw 0, (t1), 0
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR
 
 cv.sw t0, 0(t1)
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sw t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
@@ -241,7 +277,9 @@ cv.sw t0, (t1), 2048
 # CHECK-ERROR: :[[@LINE-3]]:17: note: immediate must be an integer in the range [-2048, 2047]
 
 cv.sw t0, (0), t1
-# CHECK-ERROR: :[[@LINE-1]]:11: error: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-ERROR: :[[@LINE-2]]:11: note: invalid operand for instruction
+# CHECK-ERROR: :[[@LINE-3]]:11: note: expected '('
 
 cv.sw 0, (t1), t1
 # CHECK-ERROR: :[[@LINE-1]]:7: error: register must be a GPR

diff  --git a/llvm/test/MC/RISCV/rv32c-invalid.s b/llvm/test/MC/RISCV/rv32c-invalid.s
index 5b660fa2ed4b3..d63c24e386064 100644
--- a/llvm/test/MC/RISCV/rv32c-invalid.s
+++ b/llvm/test/MC/RISCV/rv32c-invalid.s
@@ -116,12 +116,12 @@ c.swsp  ra, -4(sp)
 ## uimm7_lsb00
 c.lw  s0, -4(sp)
 # CHECK: :[[#@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[#@LINE-2]]:11: note: invalid operand for instruction
+# CHECK: :[[#@LINE-2]]:11: note: expected '('
 # CHECK: :[[#@LINE-3]]:11: note: immediate must be a multiple of 4 bytes in the range [0, 124]
 
 c.sw  s0, 128(sp)
 # CHECK: :[[#@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK: :[[#@LINE-2]]:11: note: invalid operand for instruction
+# CHECK: :[[#@LINE-2]]:11: note: expected '('
 # CHECK: :[[#@LINE-3]]:11: note: immediate must be a multiple of 4 bytes in the range [0, 124]
 
 ## simm9_lsb0

diff  --git a/llvm/test/MC/RISCV/rv32zclsd-invalid.s b/llvm/test/MC/RISCV/rv32zclsd-invalid.s
index db0ce4c4136fc..6456580590019 100644
--- a/llvm/test/MC/RISCV/rv32zclsd-invalid.s
+++ b/llvm/test/MC/RISCV/rv32zclsd-invalid.s
@@ -30,13 +30,13 @@ c.sdsp t1, -8(sp) # CHECK: :[[@LINE]]:12: error: immediate must be a multiple of
 c.ld  s0, -8(sp)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK: :[[@LINE-2]]:7: note: register must be a GPR from x8 to x15
-# CHECK: :[[@LINE-3]]:11: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:11: note: expected '('
 # CHECK: :[[@LINE-4]]:11: note: immediate must be a multiple of 8 bytes in the range [0, 248]
 
 c.sd  s0, 256(sp)
 # CHECK: :[[@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK: :[[@LINE-2]]:7: note: register must be a GPR from x8 to x15
-# CHECK: :[[@LINE-3]]:11: note: invalid operand for instruction
+# CHECK: :[[@LINE-3]]:11: note: expected '('
 # CHECK: :[[@LINE-4]]:11: note: immediate must be a multiple of 8 bytes in the range [0, 248]
 
 # Invalid register names

diff  --git a/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s b/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s
index 06e57494af8a0..0b3d8a7040e78 100644
--- a/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s
+++ b/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s
@@ -2,7 +2,7 @@
 # RUN: not llvm-mc -triple riscv64 -mattr=+xtheadmemidx < %s 2>&1 | FileCheck %s
 
 th.ldia		0(a0), (a1), 0, 0  # CHECK: :[[@LINE]]:10: error: register must be a GPR
-th.ldib		a0, 2(a1), 15, 1   # CHECK: :[[@LINE]]:14: error: invalid operand for instruction
+th.ldib		a0, 2(a1), 15, 1   # CHECK: :[[@LINE]]:14: error: expected '('
 th.lwia		a0, (a1), 30, 2    # CHECK: :[[@LINE]]:20: error: immediate must be an integer in the range [-16, 15]
 th.lwib		a0, (a1), -16, 43  # CHECK: :[[@LINE]]:25: error: immediate must be an integer in the range [0, 3]
 th.lhib		a0, (a1), -17, 3   # CHECK: :[[@LINE]]:20: error: immediate must be an integer in the range [-16, 15]

diff  --git a/llvm/test/MC/RISCV/rvzicbop-invalid.s b/llvm/test/MC/RISCV/rvzicbop-invalid.s
index a158764c489aa..0354a6741df23 100644
--- a/llvm/test/MC/RISCV/rvzicbop-invalid.s
+++ b/llvm/test/MC/RISCV/rvzicbop-invalid.s
@@ -3,7 +3,7 @@
 
 # Memory operand not formatted correctly.
 prefetch.i a0, 32 # CHECK: :[[@LINE]]:12: error: immediate must be a multiple of 32 bytes in the range [-2048, 2016]
-prefetch.r 32, a0 # CHECK: :[[@LINE]]:16: error: invalid operand for instruction
+prefetch.r 32, a0 # CHECK: :[[@LINE]]:16: error: expected '('
 prefetch.w a0(32) # CHECK: :[[@LINE]]:14: error: unexpected token
 
 # Out of range offset.

diff  --git a/llvm/test/MC/RISCV/tlsdesc.s b/llvm/test/MC/RISCV/tlsdesc.s
index db126d2fcc2da..b75934f3ff4d7 100644
--- a/llvm/test/MC/RISCV/tlsdesc.s
+++ b/llvm/test/MC/RISCV/tlsdesc.s
@@ -48,7 +48,7 @@ start:                                  # @start
 	lw   a0, t0, %tlsdesc_load_lo(a_symbol)(a4)
 # ERR: :[[#@LINE-1]]:2: error: invalid instruction, any one of the following would fix this:
 # ERR: :[[#@LINE-2]]:15: note: unexpected extra operand for instruction
-# ERR: :[[#@LINE-3]]:11: note: invalid operand for instruction
+# ERR: :[[#@LINE-3]]:11: note: expected '('
 # ERR: :[[#@LINE-4]]:11: note: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo specifier or an integer in the range [-2048, 2047]
 # ERR: :[[#@LINE-5]]:11: note: immediate must be an integer in the range [-2048, 2047]
 

diff  --git a/llvm/test/MC/RISCV/xqciio-invalid.s b/llvm/test/MC/RISCV/xqciio-invalid.s
index 25a2b8f992366..0627f00ac5df5 100644
--- a/llvm/test/MC/RISCV/xqciio-invalid.s
+++ b/llvm/test/MC/RISCV/xqciio-invalid.s
@@ -8,12 +8,12 @@
 qc.outw x5, 2048(10)
 
 # CHECK-PLUS: :[[@LINE+3]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK-PLUS: :[[@LINE+2]]:13: note: invalid operand for instruction
+# CHECK-PLUS: :[[@LINE+2]]:13: note: expected '('
 # CHECK-PLUS: :[[@LINE+1]]:13: note: immediate must be a multiple of 4 bytes in the range [0, 16380]
 qc.outw x5, x10
 
 # CHECK-MINUS: :[[@LINE+3]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK-MINUS: :[[@LINE+2]]:13: note: invalid operand for instruction
+# CHECK-MINUS: :[[@LINE+2]]:13: note: expected '('
 # CHECK-MINUS: :[[@LINE+1]]:13: note: immediate must be a multiple of 4 bytes in the range [0, 16380]
 qc.outw x5, x10
 
@@ -28,12 +28,12 @@ qc.outw x5, 2048(x10)
 qc.inw x23, 16380(17)
 
 # CHECK-PLUS: :[[@LINE+3]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK-PLUS: :[[@LINE+2]]:13: note: invalid operand for instruction
+# CHECK-PLUS: :[[@LINE+2]]:13: note: expected '('
 # CHECK-PLUS: :[[@LINE+1]]:13: note: immediate must be a multiple of 4 bytes in the range [0, 16380]
 qc.inw x23, x17
 
 # CHECK-MINUS: :[[@LINE+3]]:1: error: invalid instruction, any one of the following would fix this:
-# CHECK-MINUS: :[[@LINE+2]]:13: note: invalid operand for instruction
+# CHECK-MINUS: :[[@LINE+2]]:13: note: expected '('
 # CHECK-MINUS: :[[@LINE+1]]:13: note: immediate must be a multiple of 4 bytes in the range [0, 16380]
 qc.inw x23, x17
 

diff  --git a/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s b/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s
index 792f18761960f..6b087979c8ccc 100644
--- a/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s
+++ b/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s
@@ -7,37 +7,37 @@
 # CHECK-ENABLED: [[@LINE+4]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK-ENABLED: [[@LINE+3]]:13: note: operand must be a bare symbol name
 # CHECK-ENABLED: [[@LINE+2]]:19: note: too few operands for instruction
-# CHECK-DISABLED: :[[@LINE+1]]:13: error: invalid operand for instruction
+# CHECK-DISABLED: :[[@LINE+1]]:13: error: expected '('
 qc.e.lb a0, 0xf000
 
 # CHECK-ENABLED: [[@LINE+4]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK-ENABLED: [[@LINE+3]]:13: note: operand must be a bare symbol name
 # CHECK-ENABLED: [[@LINE+2]]:19: note: too few operands for instruction
-# CHECK-DISABLED: :[[@LINE+1]]:13: error: invalid operand for instruction
+# CHECK-DISABLED: :[[@LINE+1]]:13: error: expected '('
 qc.e.lb a0, 0xf000
 
 # CHECK-ENABLED: [[@LINE+4]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK-ENABLED: [[@LINE+3]]:14: note: operand must be a bare symbol name
 # CHECK-ENABLED: [[@LINE+2]]:20: note: too few operands for instruction
-# CHECK-DISABLED: :[[@LINE+1]]:14: error: invalid operand for instruction
+# CHECK-DISABLED: :[[@LINE+1]]:14: error: expected '('
 qc.e.lbu a0, 0xf000
 
 # CHECK-ENABLED: [[@LINE+4]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK-ENABLED: [[@LINE+3]]:13: note: operand must be a bare symbol name
 # CHECK-ENABLED: [[@LINE+2]]:19: note: too few operands for instruction
-# CHECK-DISABLED: :[[@LINE+1]]:13: error: invalid operand for instruction
+# CHECK-DISABLED: :[[@LINE+1]]:13: error: expected '('
 qc.e.lh a0, 0xf000
 
 # CHECK-ENABLED: [[@LINE+4]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK-ENABLED: [[@LINE+3]]:14: note: operand must be a bare symbol name
 # CHECK-ENABLED: [[@LINE+2]]:20: note: too few operands for instruction
-# CHECK-DISABLED: :[[@LINE+1]]:14: error: invalid operand for instruction
+# CHECK-DISABLED: :[[@LINE+1]]:14: error: expected '('
 qc.e.lhu a0, 0xf000
 
 # CHECK-ENABLED: [[@LINE+4]]:1: error: invalid instruction, any one of the following would fix this:
 # CHECK-ENABLED: [[@LINE+3]]:13: note: operand must be a bare symbol name
 # CHECK-ENABLED: [[@LINE+2]]:19: note: too few operands for instruction
-# CHECK-DISABLED: :[[@LINE+1]]:13: error: invalid operand for instruction
+# CHECK-DISABLED: :[[@LINE+1]]:13: error: expected '('
 qc.e.lw a0, 0xf000
 
 # CHECK-ENABLED: [[@LINE+2]]:13: error: operand must be a bare symbol name

diff  --git a/llvm/test/MC/RISCV/xqcilsm-invalid.s b/llvm/test/MC/RISCV/xqcilsm-invalid.s
index 24e4fda322ee5..38e5282969970 100644
--- a/llvm/test/MC/RISCV/xqcilsm-invalid.s
+++ b/llvm/test/MC/RISCV/xqcilsm-invalid.s
@@ -34,7 +34,7 @@ qc.swmi x10, 4, 20(4)
 qc.swmi x0, 4, 20(x4)
 
 # CHECK-PLUS: :[[@LINE+2]]:19: error: too few operands for instruction
-# CHECK-MINUS: :[[@LINE+1]]:17: error: invalid operand for instruction
+# CHECK-MINUS: :[[@LINE+1]]:17: error: expected '('
 qc.swmi x10, 4, 20
 
 # CHECK-PLUS: :[[@LINE+1]]:14: error: immediate must be an integer in the range [1, 31]
@@ -58,7 +58,7 @@ qc.setwm x4, x30, 124(2)
 qc.setwm x4, x0, 124(x2)
 
 # CHECK-PLUS: :[[@LINE+2]]:22: error: too few operands for instruction
-# CHECK-MINUS: :[[@LINE+1]]:19: error: invalid operand for instruction
+# CHECK-MINUS: :[[@LINE+1]]:19: error: expected '('
 qc.setwm x4, x30, 124
 
 # CHECK-PLUS: :[[@LINE+1]]:19: error: immediate must be a multiple of 4 bytes in the range [0, 124]
@@ -72,7 +72,7 @@ qc.setwm x4, x30, 124(x2)
 qc.setwmi x5, 31, 12(12)
 
 # CHECK-PLUS: :[[@LINE+2]]:21: error: too few operands for instruction
-# CHECK-MINUS: :[[@LINE+1]]:19: error: invalid operand for instruction
+# CHECK-MINUS: :[[@LINE+1]]:19: error: expected '('
 qc.setwmi x5, 31, 12
 
 # CHECK-PLUS: :[[@LINE+1]]:15: error: immediate must be an integer in the range [1, 31]
@@ -92,7 +92,7 @@ qc.setwmi x5, 31, 12(x12)
 qc.lwm x7, x1, 24(20)
 
 # CHECK-PLUS: :[[@LINE+2]]:18: error: too few operands for instruction
-# CHECK-MINUS: :[[@LINE+1]]:16: error: invalid operand for instruction
+# CHECK-MINUS: :[[@LINE+1]]:16: error: expected '('
 qc.lwm x7, x1, 24
 
 # CHECK-PLUS: :[[@LINE+2]]:8: error: register must be a GPR excluding zero (x0)
@@ -110,7 +110,7 @@ qc.lwm x7, x1, 24(x20)
 qc.lwmi x13, 9, 4(23)
 
 # CHECK-PLUS: :[[@LINE+2]]:18: error: too few operands for instruction
-# CHECK-MINUS: :[[@LINE+1]]:17: error: invalid operand for instruction
+# CHECK-MINUS: :[[@LINE+1]]:17: error: expected '('
 qc.lwmi x13, 9, 4
 
 # CHECK-PLUS: :[[@LINE+2]]:9: error: register must be a GPR excluding zero (x0)

diff  --git a/llvm/test/TableGen/AsmMatcherTokens.td b/llvm/test/TableGen/AsmMatcherTokens.td
new file mode 100644
index 0000000000000..e7565fe43b3c3
--- /dev/null
+++ b/llvm/test/TableGen/AsmMatcherTokens.td
@@ -0,0 +1,76 @@
+// RUN: llvm-tblgen -gen-asm-matcher -I %p/../../include %s | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def ArchInstrInfo : InstrInfo { }
+def ArchAsmParser : AsmParser {
+  let EmitTokenDiagnosticTypes = true;
+}
+
+def Arch : Target {
+  let InstructionSet = ArchInstrInfo;
+  let AssemblyParsers = [ArchAsmParser];
+}
+
+def Reg : Register<"reg">;
+def RegClass : RegisterClass<"foo", [i32], 0, (add Reg)>;
+
+class ArchInstruction<string AsmStr, dag OutOperands, dag InOperands> : Instruction {
+    let OutOperandList = OutOperands;
+    let InOperandList = InOperands;
+    let AsmString = AsmStr;
+}
+
+def Instr1 : ArchInstruction<"Instr1 [$foo]", (outs RegClass:$foo), (ins)> {
+  bits<1> foo;
+}
+def Instr2 : ArchInstruction<"Instr2 shift $foo", (outs RegClass:$foo), (ins)> {
+  bits<1> foo;
+}
+// Ensure there are match class kinds for the tokens `[`, `]`, and `shift`
+// CHECK-LABEL: enum MatchClassKind
+// CHECK:       OptionalMatchClass = 1,
+// CHECK-NEXT:  MCK__91_, // '['
+// CHECK-NEXT:  MCK__93_, // ']'
+// CHECK-NEXT:  MCK_shift, // 'shift'
+// CHECK-NEXT:  MCK_LAST_TOKEN = MCK_shift,
+
+// Ensure there are diagnostic strings for Match_InvalidToken* failures
+// CHECK-LABEL: getMatchKindDiag
+// CHECK:      (ArchAsmParser::ArchMatchResultTy MatchResult) {
+// CHECK-NEXT:   switch (MatchResult) {
+// CHECK-NEXT:   case ArchAsmParser::Match_InvalidToken_91_:
+// CHECK-NEXT:     return "expected '['";
+// CHECK-NEXT:   case ArchAsmParser::Match_InvalidToken_93_:
+// CHECK-NEXT:     return "expected ']'";
+// CHECK-NEXT:   case ArchAsmParser::Match_InvalidTokenshift:
+// CHECK-NEXT:     return "expected 'shift'";
+
+// Ensure there are Match_InvalidToken* instances for each token kind
+// CHECK-LABEL: getDiagKindFromTokenClass
+// CHECK:      (MatchClassKind Kind) {
+// CHECK-NEXT:   switch (Kind) {
+// CHECK-NEXT:   default:
+// CHECK-NEXT:     return MCTargetAsmParser::Match_InvalidOperand;
+// CHECK-NEXT:   case MCK__91_:
+// CHECK-NEXT:     return ArchAsmParser::Match_InvalidToken_91_;
+// CHECK-NEXT:   case MCK__93_:
+// CHECK-NEXT:     return ArchAsmParser::Match_InvalidToken_93_;
+// CHECK-NEXT:   case MCK_shift:
+// CHECK-NEXT:     return ArchAsmParser::Match_InvalidTokenshift;
+// CHECK-NEXT:   }
+// CHECK-NEXT: }
+
+// Ensure that we call `getDiagKindFromTokenClass` when a token is expected but not found
+// CHECK-LABEL: validateOperandClass
+// CHECK:      (MCParsedAsmOperand &GOp, MatchClassKind Kind, const MCSubtargetInfo &STI) {
+// CHECK-NEXT:   ArchOperand &Operand = (ArchOperand &)GOp;
+// CHECK-NEXT:   if (Kind == InvalidMatchClass)
+// CHECK-NEXT:     return MCTargetAsmParser::Match_InvalidOperand;
+// CHECK-EMPTY:
+// CHECK-NEXT:   if (Kind <= MCK_LAST_TOKEN) {
+// CHECK-NEXT:     if (Operand.isToken() &&
+// CHECK-NEXT:         isSubclass(matchTokenString(Operand.getToken()), Kind))
+// CHECK-NEXT:       return MCTargetAsmParser::Match_Success;
+// CHECK-NEXT:     return getDiagKindFromTokenClass(Kind);
+// CHECK-NEXT:   }

diff  --git a/llvm/test/TableGen/RegClassByHwMode.td b/llvm/test/TableGen/RegClassByHwMode.td
index 46aef05ebc9c2..ee1e8a32ed711 100644
--- a/llvm/test/TableGen/RegClassByHwMode.td
+++ b/llvm/test/TableGen/RegClassByHwMode.td
@@ -98,7 +98,7 @@ include "Common/RegClassByHwModeCommon.td"
 // ASMMATCHER-NEXT:   MCK_Imm, // user defined class 'ImmAsmOperand'
 
 // ASMMATCHER: static unsigned validateOperandClass(MCParsedAsmOperand &GOp, MatchClassKind Kind, const MCSubtargetInfo &STI) {
-// ASMMATCHER: if (Operand.isToken() && Kind <= MCK_LAST_TOKEN)
+// ASMMATCHER: if (Kind <= MCK_LAST_TOKEN) {
 
 // ASMMATCHER: switch (Kind) {
 

diff  --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
index 4af5c8510bffb..e32111ed46b8e 100644
--- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp
+++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp
@@ -799,7 +799,7 @@ class AsmMatcherInfo {
 
 private:
   /// getTokenClass - Lookup or create the class for the given token.
-  ClassInfo *getTokenClass(StringRef Token);
+  ClassInfo *getTokenClass(StringRef Token, bool WantDiagnostic = false);
 
   /// getOperandClass - Lookup or create the class for the given operand.
   ClassInfo *getOperandClass(const CGIOperandList::OperandInfo &OI,
@@ -1176,7 +1176,7 @@ static std::string getEnumNameForToken(StringRef Str) {
   return Res;
 }
 
-ClassInfo *AsmMatcherInfo::getTokenClass(StringRef Token) {
+ClassInfo *AsmMatcherInfo::getTokenClass(StringRef Token, bool WantDiagnostic) {
   ClassInfo *&Entry = TokenClasses[Token.str()];
 
   if (!Entry) {
@@ -1194,6 +1194,14 @@ ClassInfo *AsmMatcherInfo::getTokenClass(StringRef Token) {
     Entry->DefaultMethod = "<invalid>";
   }
 
+  // Outside the creation block so a later WantDiagnostic=true call can
+  // update an entry first created with WantDiagnostic=false.
+  if (WantDiagnostic && Entry->DiagnosticType.empty() &&
+      AsmParser->getValueAsBit("EmitTokenDiagnosticTypes")) {
+    Entry->DiagnosticType = "InvalidToken" + getEnumNameForToken(Token);
+    Entry->DiagnosticString = "expected '" + Token.str() + "'";
+  }
+
   return Entry;
 }
 
@@ -1659,7 +1667,7 @@ void AsmMatcherInfo::buildInfo() {
 
       // Check for simple tokens.
       if (Token[0] != '$') {
-        Op.Class = getTokenClass(Token);
+        Op.Class = getTokenClass(Token, /*WantDiagnostic=*/true);
         continue;
       }
 
@@ -2520,6 +2528,24 @@ static void emitRegisterMatchErrorFunc(AsmMatcherInfo &Info, raw_ostream &OS) {
   OS << "}\n\n";
 }
 
+/// emitTokenDiagFunction - Emit a function mapping token class kinds to
+/// diagnostics.
+static void emitTokenDiagFunction(AsmMatcherInfo &Info, raw_ostream &OS) {
+  OS << "static unsigned getDiagKindFromTokenClass(MatchClassKind Kind) {\n";
+  OS << "  switch (Kind) {\n";
+  OS << "  default:\n";
+  OS << "    return MCTargetAsmParser::Match_InvalidOperand;\n";
+  for (const auto &CI : Info.Classes) {
+    if (CI.Kind == ClassInfo::Token && !CI.DiagnosticType.empty()) {
+      OS << "  case " << CI.Name << ":\n";
+      OS << "    return " << Info.Target.getName() << "AsmParser::Match_"
+         << CI.DiagnosticType << ";\n";
+    }
+  }
+  OS << "  }\n";
+  OS << "}\n\n";
+}
+
 /// emitValidateOperandClass - Emit the function to validate an operand class.
 static void emitValidateOperandClass(const CodeGenTarget &Target,
                                      AsmMatcherInfo &Info, raw_ostream &OS) {
@@ -2533,11 +2559,15 @@ static void emitValidateOperandClass(const CodeGenTarget &Target,
   OS << "    return MCTargetAsmParser::Match_InvalidOperand;\n\n";
 
   // Check for Token operands first.
-  // FIXME: Use a more specific diagnostic type.
-  OS << "  if (Operand.isToken() && Kind <= MCK_LAST_TOKEN)\n";
-  OS << "    return isSubclass(matchTokenString(Operand.getToken()), Kind) ?\n"
-     << "             MCTargetAsmParser::Match_Success :\n"
-     << "             MCTargetAsmParser::Match_InvalidOperand;\n\n";
+  OS << "  if (Kind <= MCK_LAST_TOKEN) {\n";
+  OS << "    if (Operand.isToken() &&\n"
+     << "        isSubclass(matchTokenString(Operand.getToken()), Kind))\n";
+  OS << "      return MCTargetAsmParser::Match_Success;\n";
+  if (Info.AsmParser->getValueAsBit("EmitTokenDiagnosticTypes"))
+    OS << "    return getDiagKindFromTokenClass(Kind);\n";
+  else
+    OS << "    return MCTargetAsmParser::Match_InvalidOperand;\n";
+  OS << "  }\n\n";
 
   // Check the user classes. We don't care what order since we're only
   // actually matching against one of them.
@@ -2802,13 +2832,9 @@ static void emitMatchRegisterAltName(const CodeGenTarget &Target,
 static void emitOperandDiagnosticTypes(AsmMatcherInfo &Info, raw_ostream &OS) {
   // Get the set of diagnostic types from all of the operand classes.
   std::set<StringRef> Types;
-  for (const auto &OpClassEntry : Info.AsmOperandClasses) {
-    if (!OpClassEntry.second->DiagnosticType.empty())
-      Types.insert(OpClassEntry.second->DiagnosticType);
-  }
-  for (const auto &OpClassEntry : Info.RegisterClassClasses) {
-    if (!OpClassEntry.second->DiagnosticType.empty())
-      Types.insert(OpClassEntry.second->DiagnosticType);
+  for (const auto &CI : Info.Classes) {
+    if (!CI.DiagnosticType.empty())
+      Types.insert(CI.DiagnosticType);
   }
 
   if (Types.empty())
@@ -3534,6 +3560,10 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
   // Emit the subclass predicate routine.
   emitIsSubclass(Target, Info.Classes, OS);
 
+  // Emit the function mapping token class kinds to diagnostic codes.
+  if (AsmParser->getValueAsBit("EmitTokenDiagnosticTypes"))
+    emitTokenDiagFunction(Info, OS);
+
   // Emit the routine to validate an operand against a match class.
   emitValidateOperandClass(Target, Info, OS);
 


        


More information about the llvm-commits mailing list