Skip to content

[RISCV] Add Xqccmp Assembly Support #128731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Feb 27, 2025
Merged

[RISCV] Add Xqccmp Assembly Support #128731

merged 10 commits into from
Feb 27, 2025

Conversation

lenary
Copy link
Member

@lenary lenary commented Feb 25, 2025

Xqccmp is a new spec by Qualcomm that makes a vendor-specific effort to solve the push/pop + frame pointers issue. Broadly, it takes the Zcmp instructions and reverse the order they push/pop registers in, which ends up matching the frame pointer convention.

This extension adds a new instruction not present in Zcmp, qc.cm.pushfp, which will set fp to the incoming sp value after it has pushed the registers.

This change duplicates the Zcmp implementation, with minor changes to mnemonics (for the qc. prefix), predicates, and the addition of qc.cm.pushfp. There is also new logic to prevent combining Xqccmp and Zcmp. Xqccmp is kept separate to Xqci for decoding/encoding etc, as the specs are separate today.

Specification: https://github.com/quic/riscv-unified-db/releases/tag/Xqccmp_extension-0.1.0

Xqccmp is a new spec by Qualcomm that makes a vendor-specific effort to
solve the push/pop + frame pointers issue. Broadly, it takes the Zcmp
instructions and reverse the order they push/pop registers in, which
ends up matching the frame pointer convention.

This extension adds a new instruction not present in Xqccmp,
`qc.cm.pushfp`, which will set `fp` to the incoming `sp` value after it
has pushed the registers in rlist.

This change duplicates the Zcmp implementation, with minor changes to
mnemonics (for the `qc.` prefix), predicates, and the addition of
`qc.cm.pushfp`. There is also new logic to prevent combining Xqccmp and
Zcmp. Xqccmp is kept separate to Xqci for decoding/encoding etc, as the
specs are separate today.

Specification: https://github.com/quic/riscv-unified-db/releases/tag/Xqccmp_extension-0.1.0
@lenary lenary requested review from svs-quic and hchandel February 25, 2025 15:56
@llvmbot llvmbot added clang Clang issues not falling into any other category backend:RISC-V clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' mc Machine (object) code labels Feb 25, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 25, 2025

@llvm/pr-subscribers-mc
@llvm/pr-subscribers-backend-risc-v
@llvm/pr-subscribers-clang-driver

@llvm/pr-subscribers-clang

Author: Sam Elliott (lenary)

Changes

Xqccmp is a new spec by Qualcomm that makes a vendor-specific effort to solve the push/pop + frame pointers issue. Broadly, it takes the Zcmp instructions and reverse the order they push/pop registers in, which ends up matching the frame pointer convention.

This extension adds a new instruction not present in Xqccmp, qc.cm.pushfp, which will set fp to the incoming sp value after it has pushed the registers.

This change duplicates the Zcmp implementation, with minor changes to mnemonics (for the qc. prefix), predicates, and the addition of qc.cm.pushfp. There is also new logic to prevent combining Xqccmp and Zcmp. Xqccmp is kept separate to Xqci for decoding/encoding etc, as the specs are separate today.

Specification: https://github.com/quic/riscv-unified-db/releases/tag/Xqccmp_extension-0.1.0


Patch is 33.74 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128731.diff

13 Files Affected:

  • (modified) clang/test/Driver/print-supported-extensions-riscv.c (+1)
  • (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+1-1)
  • (modified) llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp (+2)
  • (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (+8)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.td (+1)
  • (added) llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td (+95)
  • (modified) llvm/lib/TargetParser/RISCVISAInfo.cpp (+5)
  • (added) llvm/test/MC/RISCV/rv32xqccmp-invalid.s (+35)
  • (added) llvm/test/MC/RISCV/rv32xqccmp-valid.s (+353)
  • (added) llvm/test/MC/RISCV/rv64e-xqccmp-valid.s (+85)
  • (added) llvm/test/MC/RISCV/rv64xqccmp-invalid.s (+35)
  • (added) llvm/test/MC/RISCV/rv64xqccmp-valid.s (+181)
  • (modified) llvm/unittests/TargetParser/RISCVISAInfoTest.cpp (+6)
diff --git a/clang/test/Driver/print-supported-extensions-riscv.c b/clang/test/Driver/print-supported-extensions-riscv.c
index fcd820464e2d1..1c29ae98c96f0 100644
--- a/clang/test/Driver/print-supported-extensions-riscv.c
+++ b/clang/test/Driver/print-supported-extensions-riscv.c
@@ -193,6 +193,7 @@
 // CHECK-NEXT:     smctr                1.0       'Smctr' (Control Transfer Records Machine Level)
 // CHECK-NEXT:     ssctr                1.0       'Ssctr' (Control Transfer Records Supervisor Level)
 // CHECK-NEXT:     svukte               0.3       'Svukte' (Address-Independent Latency of User-Mode Faults to Supervisor Addresses)
+// CHECK-NEXT:     xqccmp               0.1       'Xqccmp' (Qualcomm 16-bit Push/Pop and Double Moves)
 // CHECK-NEXT:     xqcia                0.2       'Xqcia' (Qualcomm uC Arithmetic Extension)
 // CHECK-NEXT:     xqciac               0.3       'Xqciac' (Qualcomm uC Load-Store Address Calculation Extension)
 // CHECK-NEXT:     xqcicli              0.2       'Xqcicli' (Qualcomm uC Conditional Load Immediate Extension)
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 650ad48e50de0..697dfac48e4a0 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -3640,7 +3640,7 @@ bool RISCVAsmParser::validateInstruction(MCInst &Inst,
     }
   }
 
-  if (Opcode == RISCV::CM_MVSA01) {
+  if (Opcode == RISCV::CM_MVSA01 || Opcode == RISCV::QC_CM_MVSA01) {
     MCRegister Rd1 = Inst.getOperand(0).getReg();
     MCRegister Rd2 = Inst.getOperand(1).getReg();
     if (Rd1 == Rd2) {
diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index 8c07d87680d65..c17e0de111a85 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -745,6 +745,8 @@ DecodeStatus RISCVDisassembler::getInstruction16(MCInst &MI, uint64_t &Size,
                         "Qualcomm uC Conditional Move 16bit");
   TRY_TO_DECODE_FEATURE(RISCV::FeatureVendorXqciint, DecoderTableXqciint16,
                         "Qualcomm uC Interrupts 16bit");
+  TRY_TO_DECODE_FEATURE(RISCV::FeatureVendorXqccmp, DecoderTableXqccmp16,
+                        "Xqccmp (Qualcomm 16-bit Push/Pop & Double Move Instructions)");
   TRY_TO_DECODE_AND_ADD_SP(STI.hasFeature(RISCV::FeatureVendorXwchc),
                            DecoderTableXwchc16, "WCH QingKe XW");
   TRY_TO_DECODE_AND_ADD_SP(true, DecoderTable16,
diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td
index 1a93371a4d92f..2b5b7173f2a68 100644
--- a/llvm/lib/Target/RISCV/RISCVFeatures.td
+++ b/llvm/lib/Target/RISCV/RISCVFeatures.td
@@ -1374,6 +1374,14 @@ def HasVendorXqcilo
       AssemblerPredicate<(all_of FeatureVendorXqcilo),
                          "'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)">;
 
+def FeatureVendorXqccmp
+    : RISCVExperimentalExtension<0, 1,
+                                 "Qualcomm 16-bit Push/Pop and Double Moves",
+                                 [FeatureStdExtZca]>;
+def HasVendorXqccmp : Predicate<"Subtarget->hasVendorXqccmp()">,
+                      AssemblerPredicate<(all_of FeatureVendorXqccmp),
+                        "'Xqccmp' (Qualcomm 16-bit Push/Pop and Double Moves)">;
+
 // Rivos Extension(s)
 
 def FeatureVendorXRivosVizip
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index a962e64581797..c84caa76b1b4d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -2147,6 +2147,7 @@ include "RISCVInstrInfoSFB.td"
 include "RISCVInstrInfoXCV.td"
 include "RISCVInstrInfoXwch.td"
 include "RISCVInstrInfoXqci.td"
+include "RISCVInstrInfoXqccmp.td"
 include "RISCVInstrInfoXMips.td"
 include "RISCVInstrInfoXRivos.td"
 
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td
new file mode 100644
index 0000000000000..043d985e39903
--- /dev/null
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td
@@ -0,0 +1,95 @@
+//===---------------- RISCVInstrInfoXqccmp.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 Xqccmp extension.
+//
+// Xqccmp is broadly equivalent to (and incompatible with) Zcmp except the
+// following changes:
+//
+// - The registers are pushed in the opposite order, so `ra` and `fp` are
+//   closest to the incoming stack pointer (to be compatible with the
+//   frame-pointer convention), and
+//
+// - There is a new `qc.cm.pushfp` instruction which is `qc.cm.push` but it sets
+//   `fp` to the incoming stack pointer value, as expected by the frame-pointer
+//   convention.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Operand and SDNode transformation definitions.
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Instruction Formats
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Instruction Class Templates
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Instructions
+//===----------------------------------------------------------------------===//
+
+// Zcmp
+let DecoderNamespace = "Xqccmp", Predicates = [HasVendorXqccmp],
+    hasSideEffects = 0, mayLoad = 0, mayStore = 0 in {
+let Defs = [X10, X11] in
+def QC_CM_MVA01S : RVInst16CA<0b101011, 0b11, 0b10, (outs),
+                              (ins SR07:$rs1, SR07:$rs2), "qc.cm.mva01s", "$rs1, $rs2">,
+                   Sched<[WriteIALU, WriteIALU, ReadIALU, ReadIALU]>;
+
+let Uses = [X10, X11] in
+def QC_CM_MVSA01 : RVInst16CA<0b101011, 0b01, 0b10, (outs SR07:$rs1, SR07:$rs2),
+                              (ins), "qc.cm.mvsa01", "$rs1, $rs2">,
+                   Sched<[WriteIALU, WriteIALU, ReadIALU, ReadIALU]>;
+} // DecoderNamespace = "Xqccmp", Predicates = [HasVendorXqccmp]...
+
+let DecoderNamespace = "Xqccmp", Predicates = [HasVendorXqccmp] in {
+let hasSideEffects = 0, mayLoad = 0, mayStore = 1, Uses = [X2], Defs = [X2] in
+def QC_CM_PUSH : RVInstZcCPPP<0b11000, "qc.cm.push", negstackadj>,
+                 Sched<[WriteIALU, ReadIALU, ReadStoreData, ReadStoreData,
+                        ReadStoreData, ReadStoreData, ReadStoreData, ReadStoreData,
+                        ReadStoreData, ReadStoreData, ReadStoreData, ReadStoreData,
+                        ReadStoreData, ReadStoreData, ReadStoreData]>;
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 1, Uses = [X2], Defs = [X2, X8] in
+def QC_CM_PUSHFP : RVInstZcCPPP<0b11001, "qc.cm.pushfp", negstackadj>,
+                   Sched<[WriteIALU, WriteIALU, ReadIALU, ReadStoreData, ReadStoreData,
+                          ReadStoreData, ReadStoreData, ReadStoreData, ReadStoreData,
+                          ReadStoreData, ReadStoreData, ReadStoreData, ReadStoreData,
+                          ReadStoreData, ReadStoreData, ReadStoreData]>;
+
+let hasSideEffects = 0, mayLoad = 1, mayStore = 0, isReturn = 1,
+    Uses = [X2], Defs = [X2] in
+def QC_CM_POPRET : RVInstZcCPPP<0b11110, "qc.cm.popret">,
+                   Sched<[WriteIALU, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
+                          WriteLDW, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
+                          WriteLDW, WriteLDW, WriteLDW, WriteLDW, ReadIALU]>;
+
+let hasSideEffects = 0, mayLoad = 1, mayStore = 0, isReturn = 1,
+    Uses = [X2], Defs = [X2, X10] in
+def QC_CM_POPRETZ : RVInstZcCPPP<0b11100, "qc.cm.popretz">,
+                    Sched<[WriteIALU, WriteIALU, WriteLDW, WriteLDW, WriteLDW,
+                           WriteLDW, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
+                           WriteLDW, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
+                           ReadIALU]>;
+
+let hasSideEffects = 0, mayLoad = 1, mayStore = 0,
+    Uses = [X2], Defs = [X2] in
+def QC_CM_POP : RVInstZcCPPP<0b11010, "qc.cm.pop">,
+                Sched<[WriteIALU, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
+                       WriteLDW, WriteLDW, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
+                       WriteLDW, WriteLDW, WriteLDW, ReadIALU]>;
+} // DecoderNamespace = "RVZcmp", Predicates = [HasVendorXqccmp]...
+
+//===----------------------------------------------------------------------===//
+// Aliases
+//===----------------------------------------------------------------------===//
+
diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp
index 132c47ca631b6..932db759cb7ac 100644
--- a/llvm/lib/TargetParser/RISCVISAInfo.cpp
+++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp
@@ -745,6 +745,8 @@ Error RISCVISAInfo::checkDependency() {
       {"xqcia"},  {"xqciac"},  {"xqcicli"}, {"xqcicm"},
       {"xqcics"}, {"xqcicsr"}, {"xqciint"}, {"xqcilia"},
       {"xqcilo"}, {"xqcilsm"}, {"xqcisls"}};
+  bool HasZcmp = Exts.count("zcmp") != 0;
+  bool HasXqccmp = Exts.count("xqccmp") != 0;
 
   if (HasI && HasE)
     return getIncompatibleError("i", "e");
@@ -779,6 +781,9 @@ Error RISCVISAInfo::checkDependency() {
     if (Exts.count(Ext.str()) && (XLen != 32))
       return getError("'" + Twine(Ext) + "'" + " is only supported for 'rv32'");
 
+  if (HasZcmp && HasXqccmp)
+    return getIncompatibleError("zcmp", "xqccmp");
+
   return Error::success();
 }
 
diff --git a/llvm/test/MC/RISCV/rv32xqccmp-invalid.s b/llvm/test/MC/RISCV/rv32xqccmp-invalid.s
new file mode 100644
index 0000000000000..d0be361b786cb
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv32xqccmp-invalid.s
@@ -0,0 +1,35 @@
+# RUN: not llvm-mc -triple=riscv32 -mattr=+experimental-xqccmp -M no-aliases -show-encoding < %s 2>&1 \
+# RUN:     | FileCheck -check-prefixes=CHECK-ERROR %s
+
+# CHECK-ERROR: error: invalid operand for instruction
+qc.cm.mvsa01 a1, a2
+
+# CHECK-ERROR: error: rs1 and rs2 must be different
+qc.cm.mvsa01 s0, s0
+
+# CHECK-ERROR: error: invalid operand for instruction
+qc.cm.mva01s a1, a2
+
+# CHECK-ERROR: error: invalid register list, {ra, s0-s10} or {x1, x8-x9, x18-x26} is not supported
+qc.cm.popretz {ra, s0-s10}, 112
+
+# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
+qc.cm.popretz {ra, s0-s1}, 112
+
+# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
+qc.cm.push {ra}, 16
+
+# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
+qc.cm.pushfp {ra, s0}, 16
+
+# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
+qc.cm.pop {ra, s0-s1}, -32
+
+# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
+qc.cm.push {ra}, -8
+
+# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
+qc.cm.pushfp {ra, s0}, -12
+
+# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
+qc.cm.pop {ra, s0-s1}, -40
diff --git a/llvm/test/MC/RISCV/rv32xqccmp-valid.s b/llvm/test/MC/RISCV/rv32xqccmp-valid.s
new file mode 100644
index 0000000000000..5827777e524ca
--- /dev/null
+++ b/llvm/test/MC/RISCV/rv32xqccmp-valid.s
@@ -0,0 +1,353 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+experimental-xqccmp -M no-aliases -show-encoding \
+# RUN:     | FileCheck -check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ %s
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+experimental-xqccmp < %s \
+# RUN:     | llvm-objdump --mattr=-c,+experimental-xqccmp -M no-aliases -d -r - \
+# RUN:     | FileCheck --check-prefixes=CHECK-ASM-AND-OBJ %s
+
+# CHECK-ASM-AND-OBJ: qc.cm.mvsa01 s1, s0
+# CHECK-ASM: encoding: [0xa2,0xac]
+qc.cm.mvsa01 s1, s0
+
+# CHECK-ASM-AND-OBJ: qc.cm.mva01s s1, s0
+# CHECK-ASM: encoding: [0xe2,0xac]
+qc.cm.mva01s s1, s0
+
+# CHECK-ASM-AND-OBJ: qc.cm.mva01s s0, s0
+# CHECK-ASM: encoding: [0x62,0xac] 
+qc.cm.mva01s s0, s0
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra}, 16
+# CHECK-ASM: encoding: [0x42,0xbe]
+qc.cm.popret {ra}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra}, 16
+# CHECK-ASM: encoding: [0x42,0xbe]
+qc.cm.popret {x1}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra}, 32
+# CHECK-ASM: encoding: [0x46,0xbe]
+qc.cm.popret {ra}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra}, 32
+# CHECK-ASM: encoding: [0x46,0xbe]
+qc.cm.popret {x1}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0}, 64
+# CHECK-ASM: encoding: [0x5e,0xbe]
+qc.cm.popret {ra, s0}, 64
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0}, 64
+# CHECK-ASM: encoding: [0x5e,0xbe]
+qc.cm.popret {x1, x8}, 64
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s1}, 16
+# CHECK-ASM: encoding: [0x62,0xbe]
+qc.cm.popret {ra,s0-s1}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s1}, 16
+# CHECK-ASM: encoding: [0x62,0xbe]
+qc.cm.popret {x1, x8-x9}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s2}, 32
+# CHECK-ASM: encoding: [0x76,0xbe]
+qc.cm.popret {ra, s0-s2}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s2}, 32
+# CHECK-ASM: encoding: [0x76,0xbe]
+qc.cm.popret {x1, x8-x9, x18}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s3}, 32
+# CHECK-ASM: encoding: [0x82,0xbe]
+qc.cm.popret {ra, s0-s3}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s3}, 32
+# CHECK-ASM: encoding: [0x82,0xbe]
+qc.cm.popret {x1, x8-x9, x18-x19}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s5}, 32
+# CHECK-ASM: encoding: [0xa2,0xbe]
+qc.cm.popret {ra, s0-s5}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s5}, 32
+# CHECK-ASM: encoding: [0xa2,0xbe]
+qc.cm.popret {x1, x8-x9, x18-x21}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s7}, 48
+# CHECK-ASM: encoding: [0xc2,0xbe]
+qc.cm.popret {ra, s0-s7}, 48
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s7}, 48
+# CHECK-ASM: encoding: [0xc2,0xbe]
+qc.cm.popret {x1, x8-x9, x18-x23}, 48
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s11}, 112
+# CHECK-ASM: encoding: [0xfe,0xbe]
+qc.cm.popret {ra, s0-s11}, 112
+
+# CHECK-ASM-AND-OBJ: qc.cm.popret   {ra, s0-s11}, 112
+# CHECK-ASM: encoding: [0xfe,0xbe]
+qc.cm.popret {x1, x8-x9, x18-x27}, 112
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra}, 16
+# CHECK-ASM: encoding: [0x42,0xbc]
+qc.cm.popretz {ra}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra}, 16
+# CHECK-ASM: encoding: [0x42,0xbc]
+qc.cm.popretz {x1}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra}, 32
+# CHECK-ASM: encoding: [0x46,0xbc]
+qc.cm.popretz {ra}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra}, 32
+# CHECK-ASM: encoding: [0x46,0xbc]
+qc.cm.popretz {x1}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0}, 64
+# CHECK-ASM: encoding: [0x5e,0xbc]
+qc.cm.popretz {ra, s0}, 64
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0}, 64
+# CHECK-ASM: encoding: [0x5e,0xbc]
+qc.cm.popretz {x1, x8}, 64
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s1}, 16
+# CHECK-ASM: encoding: [0x62,0xbc]
+qc.cm.popretz {ra, s0-s1}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s1}, 16
+# CHECK-ASM: encoding: [0x62,0xbc]
+qc.cm.popretz {x1, x8-x9}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s2}, 32
+# CHECK-ASM: encoding: [0x76,0xbc]
+qc.cm.popretz {ra, s0-s2}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s2}, 32
+# CHECK-ASM: encoding: [0x76,0xbc]
+qc.cm.popretz {x1, x8-x9, x18}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s3}, 32
+# CHECK-ASM: encoding: [0x82,0xbc]
+qc.cm.popretz {ra, s0-s3}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s3}, 32
+# CHECK-ASM: encoding: [0x82,0xbc]
+qc.cm.popretz {x1, x8-x9, x18-x19}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s5}, 32
+# CHECK-ASM: encoding: [0xa2,0xbc]
+qc.cm.popretz {ra, s0-s5}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s5}, 32
+# CHECK-ASM: encoding: [0xa2,0xbc]
+qc.cm.popretz {x1, x8-x9, x18-x21}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s7}, 48
+# CHECK-ASM: encoding: [0xc2,0xbc]
+qc.cm.popretz {ra, s0-s7}, 48
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s7}, 48
+# CHECK-ASM: encoding: [0xc2,0xbc]
+qc.cm.popretz {x1, x8-x9, x18-x23}, 48
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s11}, 112
+# CHECK-ASM: encoding: [0xfe,0xbc]
+qc.cm.popretz {ra, s0-s11}, 112
+
+# CHECK-ASM-AND-OBJ: qc.cm.popretz   {ra, s0-s11}, 112
+# CHECK-ASM: encoding: [0xfe,0xbc]
+qc.cm.popretz {x1, x8-x9, x18-x27}, 112
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra}, 16
+# CHECK-ASM: encoding: [0x42,0xba]
+qc.cm.pop {ra}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra}, 16
+# CHECK-ASM: encoding: [0x42,0xba]
+qc.cm.pop {x1}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra}, 32
+# CHECK-ASM: encoding: [0x46,0xba]
+qc.cm.pop {ra}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra}, 32
+# CHECK-ASM: encoding: [0x46,0xba]
+qc.cm.pop {x1}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0}, 16
+# CHECK-ASM: encoding: [0x52,0xba]
+qc.cm.pop {ra, s0}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0}, 16
+# CHECK-ASM: encoding: [0x52,0xba]
+qc.cm.pop {x1, x8}, 16
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s1}, 32
+# CHECK-ASM: encoding: [0x66,0xba]
+qc.cm.pop {ra, s0-s1}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s1}, 32
+# CHECK-ASM: encoding: [0x66,0xba]
+qc.cm.pop {x1, x8-x9}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s2}, 32
+# CHECK-ASM: encoding: [0x76,0xba]
+qc.cm.pop {ra, s0-s2}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s2}, 32
+# CHECK-ASM: encoding: [0x76,0xba]
+qc.cm.pop {x1, x8-x9, x18}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s5}, 32
+# CHECK-ASM: encoding: [0xa2,0xba]
+qc.cm.pop {ra, s0-s5}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s5}, 32
+# CHECK-ASM: encoding: [0xa2,0xba]
+qc.cm.pop {x1, x8-x9, x18-x21}, 32
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s7}, 48
+# CHECK-ASM: encoding: [0xc2,0xba]
+qc.cm.pop {ra, s0-s7}, 48
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s7}, 48
+# CHECK-ASM: encoding: [0xc2,0xba]
+qc.cm.pop {x1, x8-x9, x18-x23}, 48
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s11}, 64
+# CHECK-ASM: encoding: [0xf2,0xba]
+qc.cm.pop {ra, s0-s11}, 64
+
+# CHECK-ASM-AND-OBJ: qc.cm.pop  {ra, s0-s11}, 64
+# CHECK-ASM: encoding: [0xf2,0xba]
+qc.cm.pop {x1, x8-x9, x18-x27}, 64
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra}, -16
+# CHECK-ASM: encoding: [0x42,0xb8]
+qc.cm.push {ra}, -16
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra}, -16
+# CHECK-ASM: encoding: [0x42,0xb8]
+qc.cm.push {x1}, -16
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0}, -32
+# CHECK-ASM: encoding: [0x56,0xb8]
+qc.cm.push {ra, s0}, -32
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0}, -32
+# CHECK-ASM: encoding: [0x56,0xb8]
+qc.cm.push {x1, x8}, -32
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s1}, -16
+# CHECK-ASM: encoding: [0x62,0xb8]
+qc.cm.push {ra, s0-s1}, -16
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s1}, -16
+# CHECK-ASM: encoding: [0x62,0xb8]
+qc.cm.push {x1, x8-x9}, -16
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s3}, -32
+# CHECK-ASM: encoding: [0x82,0xb8]
+qc.cm.push {ra, s0-s3}, -32
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s3}, -32
+# CHECK-ASM: encoding: [0x82,0xb8]
+qc.cm.push {x1, x8-x9, x18-x19}, -32
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s7}, -48
+# CHECK-ASM: encoding: [0xc2,0xb8]
+qc.cm.push {ra, s0-s7}, -48
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s7}, -48
+# CHECK-ASM: encoding: [0xc2,0xb8]
+qc.cm.push {x1, x8-x9, x18-x23}, -48
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s7}, -64
+# CHECK-ASM: encoding: [0xc6,0xb8]
+qc.cm.push {ra, s0-s7}, -64
+
+# CHECK-ASM-AND-OBJ: qc.cm.push {ra, s0-s7}, -64
+# ...
[truncated]

@lenary
Copy link
Member Author

lenary commented Feb 25, 2025

I realise this extension will generate questions about what we do for CodeGen support for this extension. I have been thinking about this, and I do think it will be possible to extend the current codegen (prolog epilog inserter mostly) to support this, fairly easily. I can put up a draft patch, based on this one, in time for Thursday's RISC-V sync-up.

@lenary lenary requested a review from apazos February 25, 2025 15:59
Copy link

github-actions bot commented Feb 25, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@pgodeq pgodeq self-requested a review February 25, 2025 16:12
Sched<[WriteIALU, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
WriteLDW, WriteLDW, WriteLDW, WriteLDW, WriteLDW, WriteLDW,
WriteLDW, WriteLDW, WriteLDW, ReadIALU]>;
} // DecoderNamespace = "RVZcmp", Predicates = [HasVendorXqccmp]...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Xqccmp

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@topperc
Copy link
Collaborator

topperc commented Feb 25, 2025

This extension adds a new instruction not present in Xqccmp

Should that say not present in Zcmp?

// Instructions
//===----------------------------------------------------------------------===//

// Zcmp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop the Zcmp comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

# CHECK-ERROR: error: invalid register list, {ra, s0-s10} or {x1, x8-x9, x18-x26} is not supported
qc.cm.popretz {ra, s0-s10}, 112

# CHECK-ERROR: error: stack adjustment is invalid for this instruction and register list; refer to Zc spec for a detailed range of stack adjustment
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message refers to "Zc". Probably should say Zcmp for the standard instruction and Xqccmp for Qualcomm's?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hoped I could "get away" with referring to Zc, but I will update it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done something here, but I'm not sure it's exactly what we want. Let me know what you think.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm ok with it for the purposes of this patch. I may see about generating a better error that doesn't point to a spec.

Copy link
Collaborator

@topperc topperc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@lenary lenary merged commit 5066d7b into llvm:main Feb 27, 2025
9 of 12 checks passed
@lenary lenary deleted the pr/xqccmp-assembly branch February 27, 2025 04:03
joaosaffran pushed a commit to joaosaffran/llvm-project that referenced this pull request Mar 3, 2025
Xqccmp is a new spec by Qualcomm that makes a vendor-specific effort to
solve the push/pop + frame pointers issue. Broadly, it takes the Zcmp
instructions and reverse the order they push/pop registers in, which
ends up matching the frame pointer convention.

This extension adds a new instruction not present in Zcmp,
`qc.cm.pushfp`, which will set `fp` to the incoming `sp` value after it
has pushed the registers.

This change duplicates the Zcmp implementation, with minor changes to
mnemonics (for the `qc.` prefix), predicates, and the addition of
`qc.cm.pushfp`. There is also new logic to prevent combining Xqccmp and
Zcmp. Xqccmp is kept separate to Xqci for decoding/encoding etc, as the
specs are separate today.

Specification:
https://github.com/quic/riscv-unified-db/releases/tag/Xqccmp_extension-0.1.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:RISC-V clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants