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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/test/Driver/print-supported-extensions-riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.4 '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)
Expand Down
3 changes: 3 additions & 0 deletions llvm/docs/RISCVUsage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,9 @@ The current vendor extensions supported are:
``Xwchc``
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-Xqccmp``
LLVM implements `version 0.1 of the 16-bit Push/Pop instructions and double-moves extension specification <https://github.com/quic/riscv-unified-db/releases/tag/Xqccmp_extension-0.1.0>`__ by Qualcomm. All instructions are prefixed with `qc.` as described in the specification.

``experimental-Xqcia``
LLVM implements `version 0.4 of the Qualcomm uC Arithmetic extension specification <https://github.com/quic/riscv-unified-db/releases/latest>`__ by Qualcomm. All instructions are prefixed with `qc.` as described in the specification. These instructions are only available for riscv32.

Expand Down
2 changes: 2 additions & 0 deletions llvm/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ Changes to the RISC-V Backend

* Adds experimental assembler support for the Qualcomm uC 'Xqcilia` (Large Immediate Arithmetic)
extension.
* Adds experimental assembler support for the Qualcomm 'Xqccmp' extension, which
is a frame-pointer convention compatible version of Zcmp.

Changes to the WebAssembly Backend
----------------------------------
Expand Down
13 changes: 12 additions & 1 deletion llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,17 @@ bool RISCVAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
case Match_InvalidRnumArg: {
return generateImmOutOfRangeError(Operands, ErrorInfo, 0, 10);
}
case Match_InvalidStackAdj: {
SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc();
StringRef SpecName = "Zc";
if (getSTI().hasFeature(RISCV::FeatureVendorXqccmp))
SpecName = "Xqccmp";

return Error(ErrorLoc,
Twine("stack adjustment is invalid for this instruction") +
" and register list; refer to " + SpecName +
" spec for a detailed range of stack adjustment");
}
}

if (const char *MatchDiag = getMatchKindDiag((RISCVMatchResultTy)Result)) {
Expand Down Expand Up @@ -3640,7 +3651,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) {
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,9 @@ DecodeStatus RISCVDisassembler::getInstruction16(MCInst &MI, uint64_t &Size,

TRY_TO_DECODE_FEATURE_ANY(XqciFeatureGroup, DecoderTableXqci16,
"Qualcomm uC 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,
Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/RISCV/RISCVFeatures.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 FeatureVendorXRivosVisni
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -2153,6 +2153,7 @@ include "RISCVInstrInfoSFB.td"
include "RISCVInstrInfoXCV.td"
include "RISCVInstrInfoXwch.td"
include "RISCVInstrInfoXqci.td"
include "RISCVInstrInfoXqccmp.td"
include "RISCVInstrInfoXMips.td"
include "RISCVInstrInfoXRivos.td"

Expand Down
95 changes: 95 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfoXqccmp.td
Original file line number Diff line number Diff line change
@@ -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
//===----------------------------------------------------------------------===//

let DecoderNamespace = "Xqccmp", Predicates = [HasVendorXqccmp] in {

let 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]>;
} // hasSideEffects = 0, mayLoad = 0, mayStore = 0

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 = "Xqccmp", Predicates = [HasVendorXqccmp]

//===----------------------------------------------------------------------===//
// Aliases
//===----------------------------------------------------------------------===//

2 changes: 0 additions & 2 deletions llvm/lib/Target/RISCV/RISCVInstrInfoZc.td
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ def StackAdjAsmOperand : AsmOperandClass {
let Name = "StackAdj";
let ParserMethod = "parseZcmpStackAdj";
let DiagnosticType = "InvalidStackAdj";
let DiagnosticString = "stack adjustment is invalid for this instruction and register list; "
"refer to Zc spec for a detailed range of stack adjustment";
let PredicateMethod = "isSpimm";
let RenderMethod = "addSpimmOperands";
}
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/TargetParser/RISCVISAInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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();
}

Expand Down
5 changes: 5 additions & 0 deletions llvm/test/CodeGen/RISCV/attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
; RUN: llc -mtriple=riscv32 -mattr=+xtheadmempair %s -o - | FileCheck --check-prefix=RV32XTHEADMEMPAIR %s
; RUN: llc -mtriple=riscv32 -mattr=+xtheadsync %s -o - | FileCheck --check-prefix=RV32XTHEADSYNC %s
; RUN: llc -mtriple=riscv32 -mattr=+xwchc %s -o - | FileCheck --check-prefix=RV32XWCHC %s
; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqccmp %s -o - | FileCheck --check-prefix=RV32XQCCMP %s
; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcia %s -o - | FileCheck --check-prefix=RV32XQCIA %s
; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqciac %s -o - | FileCheck --check-prefix=RV32XQCIAC %s
; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcicli %s -o - | FileCheck --check-prefix=RV32XQCICLI %s
Expand Down Expand Up @@ -302,6 +303,8 @@
; RUN: llc -mtriple=riscv64 -mattr=+experimental-ssctr %s -o - | FileCheck --check-prefix=RV64SSCTR %s
; RUN: llc -mtriple=riscv64 -mattr=+experimental-sdext %s -o - | FileCheck --check-prefix=RV64SDEXT %s
; RUN: llc -mtriple=riscv64 -mattr=+experimental-sdtrig %s -o - | FileCheck --check-prefix=RV64SDTRIG %s
; RUN: llc -mtriple=riscv64 -mattr=+experimental-xqccmp %s -o - | FileCheck --check-prefix=RV64XQCCMP %s


; Tests for profile features.
; RUN: llc -mtriple=riscv32 -mattr=+rvi20u32 %s -o - | FileCheck --check-prefix=RVI20U32 %s
Expand Down Expand Up @@ -398,6 +401,7 @@
; RV32XTHEADMEMPAIR: .attribute 5, "rv32i2p1_xtheadmempair1p0"
; RV32XTHEADSYNC: .attribute 5, "rv32i2p1_xtheadsync1p0"
; RV32XWCHC: .attribute 5, "rv32i2p1_xwchc2p2"
; RV32XQCCMP: .attribute 5, "rv32i2p1_zca1p0_xqccmp0p1"
; RV32XQCIA: .attribute 5, "rv32i2p1_xqcia0p4"
; RV32XQCIAC: .attribute 5, "rv32i2p1_zca1p0_xqciac0p3"
; RV32XQCICLI: .attribute 5, "rv32i2p1_xqcicli0p2"
Expand Down Expand Up @@ -617,6 +621,7 @@
; RV64SSCTR: .attribute 5, "rv64i2p1_sscsrind1p0_ssctr1p0"
; RV64SDEXT: .attribute 5, "rv64i2p1_sdext1p0"
; RV64SDTRIG: .attribute 5, "rv64i2p1_sdtrig1p0"
; RV64XQCCMP: .attribute 5, "rv64i2p1_zca1p0_xqccmp0p1"

; RVI20U32: .attribute 5, "rv32i2p1"
; RVI20U64: .attribute 5, "rv64i2p1"
Expand Down
35 changes: 35 additions & 0 deletions llvm/test/MC/RISCV/rv32xqccmp-invalid.s
Original file line number Diff line number Diff line change
@@ -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 Xqccmp 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 Xqccmp 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 Xqccmp 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 Xqccmp 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 Xqccmp 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 Xqccmp 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 Xqccmp spec for a detailed range of stack adjustment
qc.cm.pop {ra, s0-s1}, -40
Loading
Loading