[clang] [llvm] [RISCV] Add assembler support for the Qualcomm Xqccmi vendor extension (PR #225119)

Sudharsan Veeravalli via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 08:48:52 PDT 2026


https://github.com/svs-quic created https://github.com/llvm/llvm-project/pull/225119

This patch adds support for assembling and disassembling the Qualcomm Xqccmi 16-bit Instruction Lookup Table vendor extension. The extension has a single `qc.cm.ilut` instruction which executes one or two instructions from an instruction lookup table. The encoding overlaps with `c.fld`, making `Xqccmi` and `Zcd` mutually exclusive. 

The extension also adds two CSR's `qc.itba` and `qc.itdec` that contain the lookup table base address and the leading double entries count respectively.

Spec: https://github.com/qualcomm/riscv-unified-db/releases#release-Xqccmi-0.2.0

Assisted by gpt-5.6-luna.

>From be3d928597767c8a1abd6937fddf43e5ce1526b8 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <svs at qti.qualcomm.com>
Date: Tue, 1 Sep 2026 20:37:02 +0530
Subject: [PATCH] [RISCV] Add assembler support for the Qualcomm Xqccmi vendor
 extension

---
 .../Driver/print-supported-extensions-riscv.c |  1 +
 llvm/docs/RISCVUsage.md                       |  4 +++
 llvm/docs/ReleaseNotes.md                     |  1 +
 .../RISCV/Disassembler/RISCVDisassembler.cpp  |  3 ++
 llvm/lib/Target/RISCV/RISCVFeatures.td        |  9 ++++++
 llvm/lib/Target/RISCV/RISCVInstrInfo.td       |  1 +
 llvm/lib/Target/RISCV/RISCVInstrInfoXqccmi.td | 30 +++++++++++++++++++
 llvm/lib/Target/RISCV/RISCVSystemOperands.td  |  6 ++++
 llvm/lib/TargetParser/RISCVISAInfo.cpp        | 13 ++++----
 llvm/test/CodeGen/RISCV/features-info.ll      |  1 +
 llvm/test/MC/RISCV/attribute-arch.s           |  3 ++
 llvm/test/MC/RISCV/xqccmi-invalid.s           |  8 +++++
 llvm/test/MC/RISCV/xqccmi-valid.s             | 23 ++++++++++++++
 .../TargetParser/RISCVISAInfoTest.cpp         |  6 ++++
 14 files changed, 103 insertions(+), 6 deletions(-)
 create mode 100644 llvm/lib/Target/RISCV/RISCVInstrInfoXqccmi.td
 create mode 100644 llvm/test/MC/RISCV/xqccmi-invalid.s
 create mode 100644 llvm/test/MC/RISCV/xqccmi-valid.s

diff --git a/clang/test/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c
index 545cc3673fa32..b08b93545ebb3 100644
--- a/clang/test/Driver/print-supported-extensions-riscv.c
+++ b/clang/test/Driver/print-supported-extensions-riscv.c
@@ -280,6 +280,7 @@
 // CHECK-NEXT:     ssijt                0.20      'Ssijt' (Interrupt Jump Table at Supervisor Level)
 // CHECK-NEXT:     ssip                 0.20      'Ssip' (Support for Interrupt Handler Push/Pop at Supervisor Level)
 // CHECK-NEXT:     svukte               1.0       'Svukte' (Address-Independent Latency of User-Mode Faults to Supervisor Addresses)
+// CHECK-NEXT:     xqccmi               0.2       'Xqccmi' (Qualcomm 16-bit Instruction Lookup Table)
 // CHECK-NEXT:     xqccmt               0.1       'Xqccmt' (Qualcomm 16-bit Table Jump)
 // CHECK-NEXT:     xsfmclic             0.1       'XSfmclic' (SiFive CLIC Machine-mode CSRs)
 // CHECK-NEXT:     xsfsclic             0.1       'XSfsclic' (SiFive CLIC Supervisor-mode CSRs)
diff --git a/llvm/docs/RISCVUsage.md b/llvm/docs/RISCVUsage.md
index 1e89c8d7d82bd..94214fa2e3c51 100644
--- a/llvm/docs/RISCVUsage.md
+++ b/llvm/docs/RISCVUsage.md
@@ -538,6 +538,10 @@ The current vendor extensions supported are:
 
 : LLVM implements `the custom compressed opcodes present in some QingKe cores` by WCH / Nanjing Qinheng Microelectronics. The vendor refers to these opcodes by the name "XW".
 
+`experimental-Xqccmi`
+
+: LLVM implements [version 0.2 of the Qualcomm 16-bit Instruction Lookup Table extension specification](https://github.com/qualcomm/riscv-unified-db/releases#release-Xqccmi-0.2.0) by Qualcomm. All instructions are prefixed with `qc.` as described in the specification.
+
 `Xqccmp`
 
 : LLVM implements [version 0.3 of the 16-bit Push/Pop instructions and double-moves extension specification](https://github.com/quic/riscv-unified-db/releases/tag/Xqccmp_extension-0.3.0) by Qualcomm. All instructions are prefixed with `qc.` as described in the specification.
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index e616c0581b87b..32331608e9afe 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -210,6 +210,7 @@ Makes programs 10x faster by doing Special New Thing.
   push/pop extensions.
 * Bump Svukte extension to 1.0.
 * Remove experimental from Zicfiss.
+* Adds experimental assembler support for the `Xqccmi` (Qualcomm 16-bit Instruction Lookup Table) vendor extension
 
 ### Changes to the WebAssembly Backend
 
diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index 99066346f0411..057117200df4d 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -646,6 +646,9 @@ static constexpr DecoderListEntry DecoderList16[]{
     {DecoderTableXqccmp16,
      {RISCV::FeatureVendorXqccmp},
      "Xqccmp (Qualcomm 16-bit Push/Pop & Double Move Instructions)"},
+    {DecoderTableXqccmi16,
+     {RISCV::FeatureVendorXqccmi},
+     "Xqccmi (Qualcomm 16-bit Instruction Lookup Table)"},
     {DecoderTableXqccmt16,
      {RISCV::FeatureVendorXqccmt},
      "Xqccmt (Qualcomm 16-bit Table Jump Instructions)"},
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index d388e0ea81c19..19599b62ffbd5 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1682,6 +1682,15 @@ def HasVendorXqccmp
       AssemblerPredicate<(all_of FeatureVendorXqccmp),
                          "'Xqccmp' (Qualcomm 16-bit Push/Pop and Double Moves)">;
 
+def FeatureVendorXqccmi
+    : RISCVExperimentalExtension<0, 2,
+                     "Qualcomm 16-bit Instruction Lookup Table",
+                     [FeatureStdExtZca, FeatureStdExtZicsr]>;
+def HasVendorXqccmi
+    : Predicate<"Subtarget->hasVendorXqccmi()">,
+      AssemblerPredicate<(all_of FeatureVendorXqccmi),
+                         "'Xqccmi' (Qualcomm 16-bit Instruction Lookup Table)">;
+
 def FeatureVendorXqccmt
     : RISCVExperimentalExtension<0, 1, "Qualcomm 16-bit Table Jump",
                      [FeatureStdExtZca, FeatureStdExtZicsr]>;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 1a0f85271a9b1..ae897710af1c2 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -2478,6 +2478,7 @@ include "RISCVInstrInfoXSfmm.td"
 include "RISCVInstrInfoXCV.td"
 include "RISCVInstrInfoXwch.td"
 include "RISCVInstrInfoXqci.td"
+include "RISCVInstrInfoXqccmi.td"
 include "RISCVInstrInfoXqccmp.td"
 include "RISCVInstrInfoXqccmt.td"
 include "RISCVInstrInfoXMips.td"
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmi.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmi.td
new file mode 100644
index 0000000000000..74ae261e2cedd
--- /dev/null
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmi.td
@@ -0,0 +1,30 @@
+//===-- RISCVInstrInfoXqccmi.td ---------------------------*- tablegen -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes Qualcomm's Xqccmi extension.
+//
+// Xqccmi provides qc.cm.ilut, a compressed instruction which executes one or two
+// instructions from an instruction lookup table. The encoding overlaps with
+// c.fld, so Xqccmi and Zcd are mutually exclusive.
+//
+//===----------------------------------------------------------------------===//
+
+let DecoderNamespace = "Xqccmi", Predicates = [HasVendorXqccmi, IsRV32],
+    hasSideEffects = 1, mayLoad = 1, mayStore = 0 in {
+
+def QC_CM_ILUT : RVInst16<(outs), (ins uimm11:$ilut_index),
+                          "qc.cm.ilut", "$ilut_index", [],
+                          InstFormatOther> {
+  bits<11> ilut_index;
+
+  let Inst{15-13} = 0b001;
+  let Inst{12-2} = ilut_index;
+  let Inst{1-0} = 0b00;
+}
+
+} // DecoderNamespace = "Xqccmi", Predicates = [HasVendorXqccmi, IsRV32]
diff --git a/llvm/lib/Target/RISCV/RISCVSystemOperands.td b/llvm/lib/Target/RISCV/RISCVSystemOperands.td
index 421a3ae37338d..7dd8bdfb950a5 100644
--- a/llvm/lib/Target/RISCV/RISCVSystemOperands.td
+++ b/llvm/lib/Target/RISCV/RISCVSystemOperands.td
@@ -564,6 +564,12 @@ foreach i = 0 - 3 in {
 }
 } // FeatureVendorXqciint
 
+// Xqccmi
+let FeaturesRequired = [{ {RISCV::FeatureVendorXqccmi} }] in {
+def : SysReg<"qc.itba", 0x800, RV32Only=true>;
+def : SysReg<"qc.itdec", 0x801, RV32Only=true>;
+}
+
 // XAIFET
 let FeaturesRequired = [{ {RISCV::FeatureVendorXAIFET} }] in {
   def : SysReg<"aif.cache_invalidate", 0x7d0>;
diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index 9d4ce85efe187..ea304a7631e6c 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -769,14 +769,15 @@ Error RISCVISAInfo::checkDependency() {
   bool HasXqccmt = Exts.count("xqccmt") != 0;
 
   static constexpr StringLiteral ZcdOverlaps[] = {
-      {"zcmt"}, {"zcmp"}, {"xqccmp"}, {"xqccmt"}, {"xqciac"}, {"xqcicm"},
+      {"zcmt"},   {"zcmp"},   {"xqccmi"}, {"xqccmp"},
+      {"xqccmt"}, {"xqciac"}, {"xqcicm"},
   };
   static constexpr StringLiteral RV32Only[] = {
-      {"zcf"},     {"zclsd"},   {"zilsd"},    {"xwchc"},   {"xqci"},
-      {"xqcia"},   {"xqciac"},  {"xqcibi"},   {"xqcibm"},  {"xqcicli"},
-      {"xqcicm"},  {"xqcics"},  {"xqcicsr"},  {"xqciint"}, {"xqciio"},
-      {"xqcilb"},  {"xqcili"},  {"xqcilia"},  {"xqcilo"},  {"xqcilsm"},
-      {"xqcisim"}, {"xqcisls"}, {"xqcisync"},
+      {"zcf"},     {"zclsd"},   {"zilsd"},   {"xwchc"},    {"xqccmi"},
+      {"xqci"},    {"xqcia"},   {"xqciac"},  {"xqcibi"},   {"xqcibm"},
+      {"xqcicli"}, {"xqcicm"},  {"xqcics"},  {"xqcicsr"},  {"xqciint"},
+      {"xqciio"},  {"xqcilb"},  {"xqcili"},  {"xqcilia"},  {"xqcilo"},
+      {"xqcilsm"}, {"xqcisim"}, {"xqcisls"}, {"xqcisync"},
   };
 
   if (HasI && HasE)
diff --git a/llvm/test/CodeGen/RISCV/features-info.ll b/llvm/test/CodeGen/RISCV/features-info.ll
index b756a2f862b7d..b39adc7ecf316 100644
--- a/llvm/test/CodeGen/RISCV/features-info.ll
+++ b/llvm/test/CodeGen/RISCV/features-info.ll
@@ -34,6 +34,7 @@
 ; CHECK-NEXT:   experimental-ssijt               - 'Ssijt' (Interrupt Jump Table at Supervisor Level).
 ; CHECK-NEXT:   experimental-ssip                - 'Ssip' (Support for Interrupt Handler Push/Pop at Supervisor Level).
 ; CHECK-NEXT:   experimental-svukte              - 'Svukte' (Address-Independent Latency of User-Mode Faults to Supervisor Addresses).
+; CHECK-NEXT:   experimental-xqccmi              - 'Xqccmi' (Qualcomm 16-bit Instruction Lookup Table).
 ; CHECK-NEXT:   experimental-xqccmt              - 'Xqccmt' (Qualcomm 16-bit Table Jump).
 ; CHECK-NEXT:   experimental-xsfmclic            - 'XSfmclic' (SiFive CLIC Machine-mode CSRs).
 ; CHECK-NEXT:   experimental-xsfsclic            - 'XSfsclic' (SiFive CLIC Supervisor-mode CSRs).
diff --git a/llvm/test/MC/RISCV/attribute-arch.s b/llvm/test/MC/RISCV/attribute-arch.s
index f802e91b312b4..71fa80037645a 100644
--- a/llvm/test/MC/RISCV/attribute-arch.s
+++ b/llvm/test/MC/RISCV/attribute-arch.s
@@ -276,6 +276,9 @@
 .attribute arch, "rv32izcmt1p0"
 # CHECK: attribute      5, "rv32i2p1_c2p0_zicsr2p0_zca1p0_zcmt1p0"
 
+.attribute arch, "rv32i_xqccmi0p2"
+# CHECK: attribute      5, "rv32i2p1_c2p0_zicsr2p0_zca1p0_xqccmi0p2"
+
 .attribute arch, "rv32i_xqccmt0p1"
 # CHECK: attribute      5, "rv32i2p1_c2p0_zicsr2p0_zca1p0_xqccmt0p1"
 
diff --git a/llvm/test/MC/RISCV/xqccmi-invalid.s b/llvm/test/MC/RISCV/xqccmi-invalid.s
new file mode 100644
index 0000000000000..2249bbbf2d4b4
--- /dev/null
+++ b/llvm/test/MC/RISCV/xqccmi-invalid.s
@@ -0,0 +1,8 @@
+# Xqccmi - Qualcomm 16-bit Instruction Lookup Table
+# RUN: not llvm-mc -triple=riscv32 -mattr=+experimental-xqccmi -M no-aliases -show-encoding < %s 2>&1 | FileCheck -check-prefix=CHECK-ERROR %s
+
+# CHECK-ERROR: error: immediate must be an integer in the range [0, 2047]
+qc.cm.ilut 2048
+
+# CHECK-ERROR: error: immediate must be an integer in the range [0, 2047]
+qc.cm.ilut -1
diff --git a/llvm/test/MC/RISCV/xqccmi-valid.s b/llvm/test/MC/RISCV/xqccmi-valid.s
new file mode 100644
index 0000000000000..c47e7737d3926
--- /dev/null
+++ b/llvm/test/MC/RISCV/xqccmi-valid.s
@@ -0,0 +1,23 @@
+# Xqccmi - Qualcomm 16-bit Instruction Lookup Table
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-xqccmi -M no-aliases -show-encoding | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+experimental-xqccmi < %s | llvm-objdump --mattr=-c,+experimental-xqccmi --no-print-imm-hex -M no-aliases -d -r - | FileCheck --check-prefixes=CHECK-ASM-AND-OBJ %s
+
+# CHECK-ASM-AND-OBJ: qc.cm.ilut 0
+# CHECK-ASM: encoding: [0x00,0x20]
+qc.cm.ilut 0
+
+# CHECK-ASM-AND-OBJ: qc.cm.ilut 1
+# CHECK-ASM: encoding: [0x04,0x20]
+qc.cm.ilut 1
+
+# CHECK-ASM-AND-OBJ: qc.cm.ilut 2047
+# CHECK-ASM: encoding: [0xfc,0x3f]
+qc.cm.ilut 2047
+
+# CHECK-ASM-AND-OBJ: csrrs t2, qc.itba, zero
+# CHECK-ASM: encoding: [0xf3,0x23,0x00,0x80]
+csrrs t2, qc.itba, zero
+
+# CHECK-ASM-AND-OBJ: csrrs t2, qc.itdec, zero
+# CHECK-ASM: encoding: [0xf3,0x23,0x10,0x80]
+csrrs t2, qc.itdec, zero
diff --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
index 60a464d1bce2f..ea9af84be5244 100644
--- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
+++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp
@@ -815,6 +815,11 @@ TEST(ParseArchString, RejectsConflictingExtensions) {
     EXPECT_THAT(Error, ::testing::HasSubstr(ConflictingExt));
   }
 
+  for (StringRef Input : {"rv32idc_xqccmi0p2", "rv32i_zcd_xqccmi0p2"}) {
+    EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
+              "'xqccmi' and 'zcd' extensions are incompatible");
+  }
+
   for (StringRef Input : {"rv32i_zcmp_xqccmp0p3", "rv64i_zcmp_xqccmp0p3"}) {
     EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()),
               "'zcmp' and 'xqccmp' extensions are incompatible");
@@ -1670,6 +1675,7 @@ Experimental extensions
     ssijt                0.20
     ssip                 0.20
     svukte               1.0
+    xqccmi               0.2
     xqccmt               0.1
     xsfmclic             0.1
     xsfsclic             0.1



More information about the llvm-commits mailing list