[llvm] [RISCV] Mark QC Instructions for Relaxation (PR #188671)
Sam Elliott via llvm-commits
llvm-commits at lists.llvm.org
Thu May 7 14:04:17 PDT 2026
https://github.com/lenary updated https://github.com/llvm/llvm-project/pull/188671
>From 190da79f81b3ec64e7d5bdafd286530712b5b659 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Fri, 6 Mar 2026 23:28:35 -0800
Subject: [PATCH 1/4] [RISCV] Mark QC Instructions for Relaxation
This adds a pass and the infrastructure to mark specific Loads/Stores as
eligible for a new relaxation we are specifying in the Qualcomm psABI
extensions.
---
.../ELFRelocs/RISCV_nonstandard.def | 2 +
.../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 11 +
llvm/lib/Target/RISCV/CMakeLists.txt | 1 +
.../RISCV/MCTargetDesc/RISCVAsmBackend.cpp | 12 +
.../RISCV/MCTargetDesc/RISCVBaseInfo.cpp | 1 +
.../Target/RISCV/MCTargetDesc/RISCVBaseInfo.h | 2 +
.../MCTargetDesc/RISCVELFObjectWriter.cpp | 4 +
.../RISCV/MCTargetDesc/RISCVFixupKinds.h | 3 +
.../RISCV/MCTargetDesc/RISCVInstPrinter.cpp | 1 +
.../RISCV/MCTargetDesc/RISCVMCAsmInfo.h | 1 +
.../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 100 ++++++
.../Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp | 3 +
llvm/lib/Target/RISCV/RISCV.h | 3 +
llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp | 3 +
llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 4 +-
llvm/lib/Target/RISCV/RISCVInstrInfoC.td | 9 +-
llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td | 113 +++++++
llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp | 228 +++++++++++++
llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 3 +
llvm/test/CodeGen/RISCV/O0-pipeline.ll | 1 +
llvm/test/CodeGen/RISCV/O3-pipeline.ll | 1 +
llvm/test/CodeGen/RISCV/qc-relax-marking.ll | 310 ++++++++++++++++++
22 files changed, 814 insertions(+), 2 deletions(-)
create mode 100644 llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
create mode 100644 llvm/test/CodeGen/RISCV/qc-relax-marking.ll
diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV_nonstandard.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV_nonstandard.def
index fb0e29b2aaae5..ba81072fc5cb4 100644
--- a/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV_nonstandard.def
+++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV_nonstandard.def
@@ -26,6 +26,8 @@ ELF_RISCV_NONSTANDARD_RELOC(QUALCOMM, R_RISCV_QC_ABS20_U, 192)
ELF_RISCV_NONSTANDARD_RELOC(QUALCOMM, R_RISCV_QC_E_BRANCH, 193)
ELF_RISCV_NONSTANDARD_RELOC(QUALCOMM, R_RISCV_QC_E_32, 194)
ELF_RISCV_NONSTANDARD_RELOC(QUALCOMM, R_RISCV_QC_E_CALL_PLT, 195)
+ELF_RISCV_NONSTANDARD_RELOC(QUALCOMM, R_RISCV_QC_ACCESS_16, 196)
+ELF_RISCV_NONSTANDARD_RELOC(QUALCOMM, R_RISCV_QC_ACCESS_32, 197)
// Andes Nonstandard Relocations
// Calculation: S + A - P (10-bit PC-relative branch offset)
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 0d9b9b06f9973..0413ddea7423a 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -619,6 +619,17 @@ struct RISCVOperand final : public MCParsedAsmOperand {
VK == ELF::R_RISCV_TLSDESC_CALL;
}
+ bool isQCAccessSymbol() const {
+ int64_t Imm;
+ // Must be of 'immediate' type but not a constant.
+ if (!isExpr() || evaluateConstantExpr(getExpr(), Imm))
+ return false;
+
+ RISCV::Specifier VK = RISCV::S_None;
+ return RISCVAsmParser::classifySymbolRef(getExpr(), VK) &&
+ VK == RISCV::S_QC_ACCESS;
+ }
+
bool isCSRSystemRegister() const { return isSystemRegister(); }
// If the last operand of the vsetvli/vsetvli instruction is a constant
diff --git a/llvm/lib/Target/RISCV/CMakeLists.txt b/llvm/lib/Target/RISCV/CMakeLists.txt
index 3b529c1471d54..bfe4a72f7b37b 100644
--- a/llvm/lib/Target/RISCV/CMakeLists.txt
+++ b/llvm/lib/Target/RISCV/CMakeLists.txt
@@ -61,6 +61,7 @@ add_llvm_target(RISCVCodeGen
RISCVPostRAExpandPseudoInsts.cpp
RISCVPromoteConstant.cpp
RISCVPushPopOptimizer.cpp
+ RISCVQCRelaxMarking.cpp
RISCVRedundantCopyElimination.cpp
RISCVRegisterInfo.cpp
RISCVSelectionDAGInfo.cpp
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index c2d1b7cbddcee..2b3161a8aa33b 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -88,10 +88,13 @@ MCFixupKindInfo RISCVAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
{"fixup_riscv_call", 0, 64, 0},
{"fixup_riscv_call_plt", 0, 64, 0},
+ // Qualcomm fixups
{"fixup_riscv_qc_e_branch", 0, 48, 0},
{"fixup_riscv_qc_e_32", 16, 32, 0},
{"fixup_riscv_qc_abs20_u", 0, 32, 0},
{"fixup_riscv_qc_e_call_plt", 0, 48, 0},
+ {"fixup_qc_access_16", 0, 0, 0},
+ {"fixup_qc_access_32", 0, 0, 0},
// Andes fixups
{"fixup_riscv_nds_branch_10", 0, 32, 0},
@@ -638,6 +641,9 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
(Bit15_13 << 17) | (Bit4_1 << 8) | (Bit11 << 7);
return Value;
}
+ case RISCV::fixup_qc_access_16:
+ case RISCV::fixup_qc_access_32:
+ return 0;
case RISCV::fixup_riscv_nds_branch_10: {
if (!isInt<11>(Value))
Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
@@ -737,6 +743,10 @@ std::optional<bool> RISCVAsmBackend::evaluateFixup(const MCFragment &,
default:
// Use default handling for `Value` and `IsResolved`.
return {};
+ case RISCV::fixup_qc_access_16:
+ case RISCV::fixup_qc_access_32:
+ // Never resolved in the assembler
+ return false;
case RISCV::fixup_riscv_pcrel_lo12_i:
case RISCV::fixup_riscv_pcrel_lo12_s: {
AUIPCFixup =
@@ -787,6 +797,8 @@ void RISCVAsmBackend::maybeAddVendorReloc(const MCFragment &F,
case RISCV::fixup_riscv_qc_abs20_u:
case RISCV::fixup_riscv_qc_e_32:
case RISCV::fixup_riscv_qc_e_call_plt:
+ case RISCV::fixup_qc_access_16:
+ case RISCV::fixup_qc_access_32:
VendorIdentifier = "QUALCOMM";
break;
case RISCV::fixup_riscv_nds_branch_10:
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
index 62cd6cca49c0e..7f43e47a51b3f 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp
@@ -13,6 +13,7 @@
#include "RISCVBaseInfo.h"
#include "RISCVInstrInfo.h"
+#include "RISCVMCAsmInfo.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
index b5aade96d4093..78ea685afa788 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -78,6 +78,7 @@ enum OperandType : unsigned {
OPERAND_SIMM10_LSB0000_NONZERO,
OPERAND_SIMM10_UNSIGNED,
OPERAND_SIMM11,
+ OPERAND_SIMM12,
OPERAND_SIMM12_LSB00000,
OPERAND_SIMM16,
OPERAND_SIMM16_NONZERO,
@@ -471,6 +472,7 @@ enum {
MO_TLSDESC_LOAD_LO = 14,
MO_TLSDESC_ADD_LO = 15,
MO_TLSDESC_CALL = 16,
+ MO_QC_ACCESS = 17,
// Used to differentiate between target-specific "direct" flags and "bitmask"
// flags. A machine operand can only have one "direct" flag, but can have
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
index 2885e3cca8722..e47102b508cda 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
@@ -142,6 +142,10 @@ unsigned RISCVELFObjectWriter::getRelocType(const MCFixup &Fixup,
return ELF::R_RISCV_QC_E_32;
case RISCV::fixup_riscv_qc_abs20_u:
return ELF::R_RISCV_QC_ABS20_U;
+ case RISCV::fixup_qc_access_16:
+ return ELF::R_RISCV_QC_ACCESS_16;
+ case RISCV::fixup_qc_access_32:
+ return ELF::R_RISCV_QC_ACCESS_32;
}
}
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
index a2b75e4a42e76..8104ee0291b34 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
@@ -59,6 +59,9 @@ enum Fixups {
// 32-bit fixup for symbol references in the 48-bit qc.j/qc.jal instructions
fixup_riscv_qc_e_call_plt,
+ fixup_qc_access_16,
+ fixup_qc_access_32,
+
// Andes specific fixups
// 10-bit fixup for symbol references in the xandesperf branch instruction
fixup_riscv_nds_branch_10,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp
index b937d81bb98cb..a1c148888c9b3 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp
@@ -12,6 +12,7 @@
#include "RISCVInstPrinter.h"
#include "RISCVBaseInfo.h"
+#include "RISCVMCAsmInfo.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
index d030c3b5cf867..b49b4383b8e1b 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
@@ -48,6 +48,7 @@ enum {
// Vendor-specific relocation types might conflict across vendors.
// Refer to them using Specifier constants.
S_QC_ABS20,
+ S_QC_ACCESS,
};
Specifier parseSpecifierName(StringRef name);
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 971b881e1ab28..0c531498da278 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -72,6 +72,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI, unsigned Size) const;
+ void expandPseudoQCAccess(const MCInst &MI, SmallVectorImpl<char> &CB,
+ SmallVectorImpl<MCFixup> &Fixups,
+ const MCSubtargetInfo &STI) const;
+
/// TableGen'erated function for getting the binary encoding for an
/// instruction.
uint64_t getBinaryCodeForInstr(const MCInst &MI,
@@ -420,6 +424,77 @@ void RISCVMCCodeEmitter::expandQCLongCondBrImm(const MCInst &MI,
}
}
+void RISCVMCCodeEmitter::expandPseudoQCAccess(
+ const MCInst &MI, SmallVectorImpl<char> &CB,
+ SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const {
+ unsigned AccessOpc;
+
+ switch (MI.getOpcode()) {
+#define QC_ACCESS_CASE(_Suffix) \
+ case RISCV::PseudoQCAccess##_Suffix: \
+ AccessOpc = RISCV::_Suffix; \
+ break;
+ // clang-format off
+ QC_ACCESS_CASE(LB)
+ QC_ACCESS_CASE(LBU)
+ QC_ACCESS_CASE(LH)
+ QC_ACCESS_CASE(LHU)
+ QC_ACCESS_CASE(LW)
+ QC_ACCESS_CASE(SB)
+ QC_ACCESS_CASE(SH)
+ QC_ACCESS_CASE(SW)
+ QC_ACCESS_CASE(C_LBU)
+ QC_ACCESS_CASE(C_LH)
+ QC_ACCESS_CASE(C_LHU)
+ QC_ACCESS_CASE(C_LW)
+ QC_ACCESS_CASE(C_SB)
+ QC_ACCESS_CASE(C_SH)
+ QC_ACCESS_CASE(C_SW)
+ // clang-format on
+ default:
+ llvm_unreachable("Unhandled QC Access Opcode");
+ };
+
+ MCInst TmpAccess = MCInstBuilder(AccessOpc)
+ .addOperand(MI.getOperand(0))
+ .addOperand(MI.getOperand(1))
+ .addOperand(MI.getOperand(2));
+ unsigned Size = MCII.get(AccessOpc).getSize();
+ uint16_t FixupKind;
+ switch (Size) {
+ default:
+ llvm_unreachable("Unhandled QC Access Instruction Size");
+ case 2: {
+ uint16_t AccessBinary = getBinaryCodeForInstr(TmpAccess, Fixups, STI);
+ support::endian::write(CB, AccessBinary, llvm::endianness::little);
+ FixupKind = RISCV::fixup_qc_access_16;
+ break;
+ }
+ case 4: {
+ uint32_t AccessBinary = getBinaryCodeForInstr(TmpAccess, Fixups, STI);
+ support::endian::write(CB, AccessBinary, llvm::endianness::little);
+ FixupKind = RISCV::fixup_qc_access_32;
+ break;
+ }
+ }
+ // Only emit the qc.access fixup if linker relaxation is enabled. The pass has
+ // already checked for this before using the Pseudos, but the user may have
+ // written the instructions directly in assembly.
+ if (!STI.hasFeature(RISCV::FeatureRelax))
+ return;
+
+ MCOperand AccessSymbol = MI.getOperand(3);
+ assert(AccessSymbol.isExpr() && "Expected expression in PseudoQCAccess");
+
+ const auto *AccessExpr = dyn_cast<MCSpecifierExpr>(AccessSymbol.getExpr());
+ assert(AccessExpr && AccessExpr->getSpecifier() == RISCV::S_QC_ACCESS &&
+ "Expected qc.access specifier on symbol");
+
+ addFixup(Fixups, /*Offset=*/0, AccessExpr, FixupKind);
+ // The added fixup is always linker relaxable.
+ Fixups.back().setLinkerRelaxable();
+}
+
void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
SmallVectorImpl<char> &CB,
SmallVectorImpl<MCFixup> &Fixups,
@@ -478,6 +553,24 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
expandTLSDESCCall(MI, CB, Fixups, STI);
MCNumEmitted += 1;
return;
+ case RISCV::PseudoQCAccessLB:
+ case RISCV::PseudoQCAccessLBU:
+ case RISCV::PseudoQCAccessLH:
+ case RISCV::PseudoQCAccessLHU:
+ case RISCV::PseudoQCAccessLW:
+ case RISCV::PseudoQCAccessSB:
+ case RISCV::PseudoQCAccessSH:
+ case RISCV::PseudoQCAccessSW:
+ case RISCV::PseudoQCAccessC_LBU:
+ case RISCV::PseudoQCAccessC_LH:
+ case RISCV::PseudoQCAccessC_LHU:
+ case RISCV::PseudoQCAccessC_LW:
+ case RISCV::PseudoQCAccessC_SB:
+ case RISCV::PseudoQCAccessC_SH:
+ case RISCV::PseudoQCAccessC_SW:
+ expandPseudoQCAccess(MI, CB, Fixups, STI);
+ MCNumEmitted += 1;
+ return;
}
switch (Size) {
@@ -649,6 +742,12 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
// encounter it here is an error.
llvm_unreachable(
"ELF::R_RISCV_TPREL_ADD should not represent an instruction operand");
+ case RISCV::S_QC_ACCESS:
+ // The same logic for tprel_add applies to S_QC_ACCESS, for similar
+ // reasons, but we use a specifier becuase %qc.access() gets expanded
+ // differently depending on the underlying instruction.
+ llvm_unreachable(
+ "S_QC_ACCESS should not represent an instruction operand");
case RISCV::S_LO:
if (MIFrm == RISCVII::InstFormatI)
FixupKind = RISCV::fixup_riscv_lo12_i;
@@ -700,6 +799,7 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
FixupKind = RISCV::fixup_riscv_qc_abs20_u;
RelaxCandidate = true;
break;
+
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TPREL_HI20:
case ELF::R_RISCV_TLSDESC_HI20:
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
index e6366af9163e6..cf9db21a47dcb 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
@@ -37,6 +37,7 @@ RISCV::Specifier RISCV::parseSpecifierName(StringRef name) {
.Case("tlsdesc_add_lo", ELF::R_RISCV_TLSDESC_ADD_LO12)
.Case("tlsdesc_call", ELF::R_RISCV_TLSDESC_CALL)
.Case("qc.abs20", RISCV::S_QC_ABS20)
+ .Case("qc.access", RISCV::S_QC_ACCESS)
// Used in data directives
.Case("pltpcrel", ELF::R_RISCV_PLT32)
.Case("gotpcrel", ELF::R_RISCV_GOT32_PCREL)
@@ -85,6 +86,8 @@ StringRef RISCV::getSpecifierName(Specifier S) {
return "pltpcrel";
case RISCV::S_QC_ABS20:
return "qc.abs20";
+ case RISCV::S_QC_ACCESS:
+ return "qc.access";
}
llvm_unreachable("Invalid ELF symbol kind");
}
diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h
index 048db205e2289..76c373710a207 100644
--- a/llvm/lib/Target/RISCV/RISCV.h
+++ b/llvm/lib/Target/RISCV/RISCV.h
@@ -132,6 +132,9 @@ void initializeRISCVVLOptimizerPass(PassRegistry &);
FunctionPass *createRISCVVMV0EliminationPass();
void initializeRISCVVMV0EliminationPass(PassRegistry &);
+FunctionPass *createRISCVQCRelaxMarkingPass();
+void initializeRISCVQCRelaxMarkingPass(PassRegistry &);
+
void initializeRISCVAsmPrinterPass(PassRegistry &);
} // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
index eb15227a72a83..f9e067923cb76 100644
--- a/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
+++ b/llvm/lib/Target/RISCV/RISCVAsmPrinter.cpp
@@ -1020,6 +1020,9 @@ static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
case RISCVII::MO_TLSDESC_CALL:
Kind = ELF::R_RISCV_TLSDESC_CALL;
break;
+ case RISCVII::MO_QC_ACCESS:
+ Kind = RISCV::S_QC_ACCESS;
+ break;
}
const MCExpr *ME = MCSymbolRefExpr::create(Sym, Ctx);
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index 089683a43f800..26a5ca67732d8 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -3070,6 +3070,7 @@ bool RISCVInstrInfo::verifyInstruction(const MachineInstr &MI,
CASE_OPERAND_SIMM(8)
CASE_OPERAND_SIMM(10)
CASE_OPERAND_SIMM(11)
+ CASE_OPERAND_SIMM(12)
CASE_OPERAND_SIMM(26)
// clang-format on
case RISCVOp::OPERAND_SIMM5_PLUS1:
@@ -3602,7 +3603,8 @@ RISCVInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
{MO_TLSDESC_HI, "riscv-tlsdesc-hi"},
{MO_TLSDESC_LOAD_LO, "riscv-tlsdesc-load-lo"},
{MO_TLSDESC_ADD_LO, "riscv-tlsdesc-add-lo"},
- {MO_TLSDESC_CALL, "riscv-tlsdesc-call"}};
+ {MO_TLSDESC_CALL, "riscv-tlsdesc-call"},
+ {MO_QC_ACCESS, "riscv-qc-access"}};
return ArrayRef(TargetFlags);
}
bool RISCVInstrInfo::isFunctionSafeToOutlineFrom(
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoC.td b/llvm/lib/Target/RISCV/RISCVInstrInfoC.td
index 8f76fa3b5bfd3..7ff9e83f4d0bc 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoC.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoC.td
@@ -108,7 +108,14 @@ class RISCVUImmLsbZeroOp<int width, int zeros>
}
// A 7-bit unsigned immediate where the least significant two bits are zero.
-def uimm7_lsb00 : RISCVUImmLsbZeroOp<7, 2>;
+def uimm7_lsb00 : RISCVUImmLsbZeroOp<7, 2> {
+ let MCOperandPredicate = [{
+ int64_t Imm;
+ if (!MCOp.evaluateAsConstantImm(Imm))
+ return false;
+ return isShiftedUInt<5, 2>(Imm);
+ }];
+}
// A 8-bit unsigned immediate where the least significant two bits are zero.
def uimm8_lsb00 : RISCVUImmLsbZeroOp<8, 2>;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index d868a7aa80d38..10a5e84b2d9df 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1359,6 +1359,119 @@ def PseudoCCQC_E_LHU : SFBQCLoad;
def PseudoCCQC_E_LBU : SFBQCLoad;
}
+def simm12 : RISCVSImmOp<12>;
+
+def QCAccessSymbol : AsmOperandClass {
+ let Name = "QCAccessSymbol";
+ let RenderMethod = "addImmOperands";
+ let DiagnosticType = "InvalidQCAccessSymbol";
+ let DiagnosticString = "operand must be a symbol with a %qc.access specifier";
+ let ParserMethod = "parseOperandWithSpecifier";
+}
+
+def qc_access_symbol : Operand<XLenVT> {
+ let ParserMatchClass = QCAccessSymbol;
+
+ let MCOperandPredicate = [{
+ return MCOp.isExpr() && MCOp.getExpr()->getKind() == MCExpr::Specifier &&
+ cast<MCSpecifierExpr>(MCOp.getExpr())->getSpecifier() == RISCV::S_QC_ACCESS;
+ }];
+}
+
+// This is modelled after `PseudoAddTPRel` but with `mayLoad = true`
+let hasSideEffects = false, mayLoad = true, mayStore = false, Size = 32,
+ isCodeGenOnly = false in
+class PseudoQCAccessLoad_ri<string opcodestr>
+ : Pseudo<(outs GPR:$rd),
+ (ins GPRMem:$rs1, simm12:$imm12, qc_access_symbol:$expr), [],
+ opcodestr, "$rd, ${imm12}(${rs1}), $expr">;
+
+let hasSideEffects = false, mayLoad = false, mayStore = true, Size = 32,
+ isCodeGenOnly = false in
+class PseudoQCAccessStore_rri<string opcodestr>
+ : Pseudo<(outs), (ins GPR:$rs2, GPRMem:$rs1, simm12:$imm12, qc_access_symbol:$expr),
+ [], opcodestr, "$rs2, ${imm12}(${rs1}), $expr">;
+
+
+let Predicates = [HasVendorXqcili] in {
+def PseudoQCAccessLB : PseudoQCAccessLoad_ri<"lb">;
+def PseudoQCAccessLBU : PseudoQCAccessLoad_ri<"lbu">;
+def PseudoQCAccessLH : PseudoQCAccessLoad_ri<"lh">;
+def PseudoQCAccessLHU : PseudoQCAccessLoad_ri<"lhu">;
+def PseudoQCAccessLW : PseudoQCAccessLoad_ri<"lw">;
+
+let EmitPriority = 0 in {
+def : InstAlias<"lb $rd, (${rs1}), $expr", (PseudoQCAccessLB GPR:$rd, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"lbu $rd, (${rs1}), $expr", (PseudoQCAccessLB GPR:$rd, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"lh $rd, (${rs1}), $expr", (PseudoQCAccessLH GPR:$rd, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"lhu $rd, (${rs1}), $expr", (PseudoQCAccessLHU GPR:$rd, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"lw $rd, (${rs1}), $expr", (PseudoQCAccessLW GPR:$rd, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+}
+
+def PseudoQCAccessSW : PseudoQCAccessStore_rri<"sw">;
+def PseudoQCAccessSH : PseudoQCAccessStore_rri<"sh">;
+def PseudoQCAccessSB : PseudoQCAccessStore_rri<"sb">;
+
+let EmitPriority = 0 in {
+def : InstAlias<"sb $rs2, (${rs1}), $expr", (PseudoQCAccessSB GPR:$rs2, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"sh $rs2, (${rs1}), $expr", (PseudoQCAccessSH GPR:$rs2, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"sw $rs2, (${rs1}), $expr", (PseudoQCAccessSW GPR:$rs2, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
+}
+}
+
+let hasSideEffects = false, mayLoad = true, mayStore = false, Size = 16,
+ isCodeGenOnly = false in
+class PseudoQCAccessCLoad_ri<string opcodestr, DAGOperand immop>
+ : Pseudo<(outs GPRC:$rd),
+ (ins GPRCMem:$rs1, immop:$imm, qc_access_symbol:$expr),
+ [], opcodestr, "$rd, ${imm}(${rs1}), $expr">;
+
+let hasSideEffects = false, mayLoad = false, mayStore = true, Size = 16,
+ isCodeGenOnly = false in
+class PseudoQCAccessCStore_rri<string opcodestr, DAGOperand immop>
+ : Pseudo<(outs),
+ (ins GPRC:$rs2, GPRCMem:$rs1, immop:$imm, qc_access_symbol:$expr),
+ [], opcodestr, "$rs2, ${imm}(${rs1}), $expr">;
+
+let Predicates = [HasVendorXqcili, HasStdExtZca] in {
+def PseudoQCAccessC_LW : PseudoQCAccessCLoad_ri<"c.lw", uimm7_lsb00>;
+let EmitPriority = 0 in
+def : InstAlias<"c.lw $rd, (${rs1}), $expr", (PseudoQCAccessC_LW GPRC:$rd, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
+
+def PseudoQCAccessC_SW : PseudoQCAccessCStore_rri<"c.sw", uimm7_lsb00>;
+let EmitPriority = 0 in
+def : InstAlias<"c.sw $rs2, (${rs1}), $expr", (PseudoQCAccessC_SW GPRC:$rs2, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
+}
+
+let Predicates = [HasVendorXqcili, HasStdExtZcb] in {
+// no C_LB so no PseudoQCAccessC_LB
+def PseudoQCAccessC_LBU : PseudoQCAccessCLoad_ri<"c.lbu", uimm2>;
+def PseudoQCAccessC_LH : PseudoQCAccessCLoad_ri<"c.lh", uimm2_lsb0>;
+def PseudoQCAccessC_LHU : PseudoQCAccessCLoad_ri<"c.lhu", uimm2_lsb0>;
+
+let EmitPriority = 0 in {
+// No c.lb so no alias
+def : InstAlias<"c.lbu $rd, (${rs1}), $expr", (PseudoQCAccessC_LBU GPRC:$rd, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"c.lh $rd, (${rs1}), $expr", (PseudoQCAccessC_LH GPRC:$rd, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"c.lhu $rd, (${rs1}), $expr", (PseudoQCAccessC_LHU GPRC:$rd, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
+}
+
+def PseudoQCAccessC_SB : PseudoQCAccessCStore_rri<"c.sb", uimm2>;
+def PseudoQCAccessC_SH : PseudoQCAccessCStore_rri<"c.sh", uimm2_lsb0>;
+
+let EmitPriority = 0 in {
+def : InstAlias<"c.sb $rs2, (${rs1}), $expr", (PseudoQCAccessC_SB GPRC:$rs2, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
+def : InstAlias<"c.sh $rs2, (${rs1}), $expr", (PseudoQCAccessC_SH GPRC:$rs2, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
+}
+}
+
+// FIXME: We would like to add these but they are not supported by CompressPat.
+// let Predicates = [HasVendorXqcili, HasStdExtZca] in {
+// def : CompressPat<(PseudoQCAccessLW GPRC:$rd, GPRCMem:$rs1, uimm7_lsb00:$imm, qc_access_symbol:$expr),
+// (PseudoQCAccessC_LW GPRC:$rd, GPRCMem:$rs1, uimm7_lsb00:$imm, qc_access_symbol:$expr)>;
+// }
+
+
//===----------------------------------------------------------------------===//
// Code Gen Patterns
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp b/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
new file mode 100644
index 0000000000000..9956bc82efe00
--- /dev/null
+++ b/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
@@ -0,0 +1,228 @@
+//===-- RISCVQCRelaxMarking.cpp - Mark Instructions for QC Relaxations ----===//
+//
+// 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 pass adds access tags to some instructions which are used by the
+// assembler to emit marker relocations, which enable some code-size relaxations
+// for Xqcilo/Xqcili.
+//
+// The pass is looking for the following sequences:
+//
+// $dst1 = QC_E_LI sym
+// $dst2 = Load killed $dst1, 0
+//
+// $dst1 = QC_E_LI sym
+// Store $dst2, killed $dst1, 0
+//
+// In either case, the Load/Store is modified to become a
+// PseudoQCAccess<Load/Store>, with an additional operand that represents the
+// accessed symbolic address, which will become the contents of a
+// `R_RISCV_QC_ACCESS_*` relocation on the emitted instruction.
+//
+// FIXME: The intention is this pass does not change the size of any
+// instructions, but right now it has to do instruction compression as the
+// CompressPat infrastructure cannot handle compressing the `%qc.access(...)`
+// operand. Symbolic operands are not usually compressible, but this one is as
+// we have relocations for both 32-bit and 16-bit instructions (and the
+// relocation does not care about the fields of the instruction).
+
+#include "RISCV.h"
+#include "RISCVSubtarget.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/CodeGen/RegisterScavenging.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "riscv-qc-relax-marking"
+#define RISCV_QC_RELAX_MARKING_NAME "RISC-V QC Relaxation Marking"
+
+STATISTIC(NumMarked, "Number of Loads/Stores Marked");
+
+namespace {
+
+struct RISCVQCRelaxMarking : public MachineFunctionPass {
+ static char ID;
+
+ bool runOnMachineFunction(MachineFunction &) override;
+
+ RISCVQCRelaxMarking() : MachineFunctionPass(ID) {}
+
+ StringRef getPassName() const override { return RISCV_QC_RELAX_MARKING_NAME; }
+};
+
+} // end namespace
+
+char RISCVQCRelaxMarking::ID = 0;
+
+INITIALIZE_PASS(RISCVQCRelaxMarking, DEBUG_TYPE, RISCV_QC_RELAX_MARKING_NAME,
+ false, false)
+
+/// Returns an instance of the Make Compressible Optimization pass.
+FunctionPass *llvm::createRISCVQCRelaxMarkingPass() {
+ return new RISCVQCRelaxMarking();
+}
+
+static bool isLoad(MachineInstr &MI) {
+ return llvm::is_contained(
+ {RISCV::LW, RISCV::LH, RISCV::LHU, RISCV::LB, RISCV::LBU},
+ MI.getOpcode());
+}
+
+static bool isStore(MachineInstr &MI) {
+ return llvm::is_contained({RISCV::SW, RISCV::SH, RISCV::SB}, MI.getOpcode());
+}
+
+static bool isUImm7LSB000(const MachineOperand &MO) {
+ return MO.isImm() && isShiftedUInt<4, 3>(MO.getImm());
+}
+
+static bool isUImm2LSB0(const MachineOperand &MO) {
+ return MO.isImm() && isShiftedUInt<1, 1>(MO.getImm());
+}
+
+static bool isUImm2(const MachineOperand &MO) {
+ return MO.isImm() && isUInt<2>(MO.getImm());
+}
+
+static bool isGPRC(const MachineOperand &MO) {
+ return MO.isReg() && RISCV::GPRCRegClass.contains(MO.getReg());
+}
+
+static unsigned getQCMarkedOpcode(const MachineInstr &MI,
+ const RISCVSubtarget &STI) {
+ switch (MI.getOpcode()) {
+ case RISCV::LB:
+ // No c.lb
+ return RISCV::PseudoQCAccessLB;
+ case RISCV::LBU:
+ if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) &&
+ isGPRC(MI.getOperand(1)) && isUImm2(MI.getOperand(2)))
+ return RISCV::PseudoQCAccessC_LBU;
+ return RISCV::PseudoQCAccessLBU;
+ case RISCV::LH:
+ if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) &&
+ isGPRC(MI.getOperand(1)) && isUImm2LSB0(MI.getOperand(2)))
+ return RISCV::PseudoQCAccessC_LH;
+ return RISCV::PseudoQCAccessLH;
+ case RISCV::LHU:
+ if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) &&
+ isGPRC(MI.getOperand(1)) && isUImm2LSB0(MI.getOperand(2)))
+ return RISCV::PseudoQCAccessC_LHU;
+ return RISCV::PseudoQCAccessLHU;
+ case RISCV::LW:
+ if (STI.hasStdExtZca() && isGPRC(MI.getOperand(0)) &&
+ isGPRC(MI.getOperand(1)) && isUImm7LSB000(MI.getOperand(2)))
+ return RISCV::PseudoQCAccessC_LW;
+ return RISCV::PseudoQCAccessLW;
+ case RISCV::SB:
+ if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) &&
+ isGPRC(MI.getOperand(1)) && isUImm2(MI.getOperand(2)))
+ return RISCV::PseudoQCAccessC_SB;
+ return RISCV::PseudoQCAccessSB;
+ case RISCV::SH:
+ if (STI.hasStdExtZcb() && isGPRC(MI.getOperand(0)) &&
+ isGPRC(MI.getOperand(1)) && isUImm2LSB0(MI.getOperand(2)))
+ return RISCV::PseudoQCAccessC_SH;
+ return RISCV::PseudoQCAccessSH;
+ case RISCV::SW:
+ if (STI.hasStdExtZca() && isGPRC(MI.getOperand(0)) &&
+ isGPRC(MI.getOperand(1)) && isUImm7LSB000(MI.getOperand(2)))
+ return RISCV::PseudoQCAccessC_SW;
+ return RISCV::PseudoQCAccessSW;
+ default:
+ reportFatalInternalError(
+ "Unhandled Opcode: No Corresponding Marked Opcode");
+ }
+}
+
+bool RISCVQCRelaxMarking::runOnMachineFunction(MachineFunction &MF) {
+ // This is only relevant for QC.E.LI with a symbol, which we only use in the
+ // small code model.
+ if (MF.getTarget().getCodeModel() != CodeModel::Small)
+ return false;
+
+ auto &STI = MF.getSubtarget<RISCVSubtarget>();
+ // We need QC.E.LI instructions to perform this optimisation, which needs
+ // 32-bit and Xqcili. The markers are only needed when linker relaxations are
+ // enabled.
+ if (STI.is64Bit() || !STI.hasVendorXqcili() || !STI.enableLinkerRelax())
+ return false;
+
+ const RISCVInstrInfo *TII = STI.getInstrInfo();
+
+ bool Changed = false;
+ for (MachineBasicBlock &MBB : MF) {
+ for (auto MI = MBB.begin(), E = MBB.end(); MI != E; MI++) {
+ auto NextMI = std::next(MI);
+ if (NextMI == E)
+ break;
+
+ // Looking for QC.E.LI followed by a load or store
+ if (MI->getOpcode() != RISCV::QC_E_LI ||
+ !(isLoad(*NextMI) || isStore(*NextMI)))
+ continue;
+
+ LLVM_DEBUG(llvm::dbgs() << "Found QC_E_LI " << *MI);
+ LLVM_DEBUG(llvm::dbgs() << "Followed by Load/Store " << *NextMI);
+
+ MachineOperand &OffsetOp = NextMI->getOperand(2);
+ if (OffsetOp.getImm() != 0)
+ continue;
+ if (MI->getOperand(0).getReg() != NextMI->getOperand(1).getReg())
+ continue;
+ if (!NextMI->getOperand(1).isKill())
+ continue;
+
+ // This is unsafe for stores where the access address is being stored.
+ if (isStore(*NextMI) &&
+ MI->getOperand(0).getReg() == NextMI->getOperand(0).getReg())
+ continue;
+
+ MachineOperand &SymOp = MI->getOperand(1);
+ if (!SymOp.isSymbol() && !SymOp.isGlobal() && !SymOp.isMCSymbol() && !SymOp.isCPI())
+ continue;
+
+ unsigned NewOpc = getQCMarkedOpcode(*NextMI, STI);
+ LLVM_DEBUG(dbgs() << "Load/Store " << TII->getName(NextMI->getOpcode())
+ << " will become " << TII->getName(NewOpc) << "\n");
+ MachineInstrBuilder MIB =
+ BuildMI(MBB, NextMI, NextMI->getDebugLoc(), TII->get(NewOpc))
+ .add(NextMI->getOperand(0))
+ .add(NextMI->getOperand(1))
+ .add(OffsetOp)
+ .cloneMemRefs(*NextMI);
+
+ if (SymOp.isSymbol()) {
+ MIB.addExternalSymbol(SymOp.getSymbolName(), RISCVII::MO_QC_ACCESS);
+ } else if (SymOp.isGlobal()) {
+ MIB.addGlobalAddress(SymOp.getGlobal(), SymOp.getOffset(),
+ RISCVII::MO_QC_ACCESS);
+ } else if (SymOp.isMCSymbol()) {
+ MachineOperand MO = MachineOperand::CreateMCSymbol(
+ SymOp.getMCSymbol(), RISCVII::MO_QC_ACCESS);
+ MO.setOffset(SymOp.getOffset());
+ MIB.add(MO);
+ } else if (SymOp.isCPI()) {
+ MIB.addConstantPoolIndex(SymOp.getIndex(), SymOp.getOffset(),
+ RISCVII::MO_QC_ACCESS);
+ } else {
+ reportFatalInternalError("Unhandled SymOp Kind");
+ }
+
+ NextMI->removeFromParent();
+ NumMarked++;
+ Changed |= true;
+ }
+ }
+
+ return Changed;
+}
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index 54b286d128dfa..f85046b6c1f83 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -594,6 +594,9 @@ void RISCVPassConfig::addPreEmitPass2() {
}
addPass(createRISCVExpandPseudoPass());
+ // Add QC Relaxation Markers as late as possible.
+ addPass(createRISCVQCRelaxMarkingPass());
+
// Schedule the expansion of AMOs at the last possible moment, avoiding the
// possibility for other passes to break the requirements for forward
// progress in the LR/SC block.
diff --git a/llvm/test/CodeGen/RISCV/O0-pipeline.ll b/llvm/test/CodeGen/RISCV/O0-pipeline.ll
index 847a8bd96c6d6..84c005fb539dd 100644
--- a/llvm/test/CodeGen/RISCV/O0-pipeline.ll
+++ b/llvm/test/CodeGen/RISCV/O0-pipeline.ll
@@ -76,6 +76,7 @@
; CHECK-NEXT: Machine Optimization Remark Emitter
; CHECK-NEXT: Stack Frame Layout Analysis
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
+; CHECK-NEXT: RISC-V QC Relaxation Marking
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
; CHECK-NEXT: Unpack machine instruction bundles
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
diff --git a/llvm/test/CodeGen/RISCV/O3-pipeline.ll b/llvm/test/CodeGen/RISCV/O3-pipeline.ll
index 2bbf67b04dff5..91b0005d4fd6e 100644
--- a/llvm/test/CodeGen/RISCV/O3-pipeline.ll
+++ b/llvm/test/CodeGen/RISCV/O3-pipeline.ll
@@ -227,6 +227,7 @@
; CHECK-NEXT: RISC-V Zcmp move merging pass
; CHECK-NEXT: RISC-V Zcmp Push/Pop optimization pass
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
+; CHECK-NEXT: RISC-V QC Relaxation Marking
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
; CHECK-NEXT: Unpack machine instruction bundles
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
diff --git a/llvm/test/CodeGen/RISCV/qc-relax-marking.ll b/llvm/test/CodeGen/RISCV/qc-relax-marking.ll
new file mode 100644
index 0000000000000..a0669d3118738
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/qc-relax-marking.ll
@@ -0,0 +1,310 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv32 -mattr=+xqcili,+relax -code-model=small \
+; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=NO-C
+; RUN: llc -mtriple=riscv32 -mattr=+xqcili,+relax,+zca,+zcb -code-model=small \
+; RUN: -verify-machineinstrs < %s | FileCheck %s --check-prefix=WITH-C
+
+ at global = external global [8 x i8]
+
+define zeroext i8 @load_unsigned_byte() nounwind optsize {
+; NO-C-LABEL: load_unsigned_byte:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: lbu a0, 0(a0), %qc.access(global)
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_unsigned_byte:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: c.lbu a0, 0(a0), %qc.access(global)
+; WITH-C-NEXT: ret
+ %1 = load i8, ptr @global, align 1
+ ret i8 %1
+}
+
+define zeroext i8 @load_unsigned_byte_offset() nounwind optsize {
+; NO-C-LABEL: load_unsigned_byte_offset:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global+1
+; NO-C-NEXT: lbu a0, 0(a0), %qc.access(global+1)
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_unsigned_byte_offset:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global+1
+; WITH-C-NEXT: c.lbu a0, 0(a0), %qc.access(global+1)
+; WITH-C-NEXT: ret
+ %1 = getelementptr i8, ptr @global, i32 1
+ %2 = load i8, ptr %1, align 1
+ ret i8 %2
+}
+
+define zeroext i16 @load_two_unsigned_byte() nounwind optsize {
+; NO-C-LABEL: load_two_unsigned_byte:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: lbu a1, 1(a0)
+; NO-C-NEXT: lbu a0, 0(a0)
+; NO-C-NEXT: slli a1, a1, 8
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_two_unsigned_byte:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: lbu a1, 1(a0)
+; WITH-C-NEXT: lbu a0, 0(a0)
+; WITH-C-NEXT: slli a1, a1, 8
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: ret
+ %1 = load i16, ptr @global, align 1
+ ret i16 %1
+}
+
+define zeroext i8 @load_unsigned_byte_twice() nounwind optsize {
+; NO-C-LABEL: load_unsigned_byte_twice:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: lbu a1, 0(a0)
+; NO-C-NEXT: lbu a0, 0(a0)
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: zext.b a0, a0
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_unsigned_byte_twice:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: lbu a1, 0(a0)
+; WITH-C-NEXT: lbu a0, 0(a0)
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: zext.b a0, a0
+; WITH-C-NEXT: ret
+ %1 = load volatile i8, ptr @global, align 1
+ %2 = load volatile i8, ptr @global, align 1
+ %3 = or i8 %1, %2
+ ret i8 %3
+}
+
+define zeroext i8 @load_unsigned_byte_twice_offset() nounwind optsize {
+; NO-C-LABEL: load_unsigned_byte_twice_offset:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global+1
+; NO-C-NEXT: lbu a1, 0(a0)
+; NO-C-NEXT: lbu a0, 0(a0)
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: zext.b a0, a0
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_unsigned_byte_twice_offset:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global+1
+; WITH-C-NEXT: lbu a1, 0(a0)
+; WITH-C-NEXT: lbu a0, 0(a0)
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: zext.b a0, a0
+; WITH-C-NEXT: ret
+ %1 = getelementptr i8, ptr @global, i32 1
+ %2 = load volatile i8, ptr %1, align 1
+ %3 = load volatile i8, ptr %1, align 1
+ %4 = or i8 %2, %3
+ ret i8 %4
+}
+
+
+define i16 @load_halfword() nounwind optsize {
+; NO-C-LABEL: load_halfword:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: lh a0, 0(a0), %qc.access(global)
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_halfword:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: c.lh a0, 0(a0), %qc.access(global)
+; WITH-C-NEXT: ret
+ %1 = load i16, ptr @global, align 2
+ ret i16 %1
+}
+
+define i16 @load_halfword_offset() nounwind optsize {
+; NO-C-LABEL: load_halfword_offset:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global+2
+; NO-C-NEXT: lh a0, 0(a0), %qc.access(global+2)
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_halfword_offset:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global+2
+; WITH-C-NEXT: c.lh a0, 0(a0), %qc.access(global+2)
+; WITH-C-NEXT: ret
+ %1 = getelementptr i8, ptr @global, i32 2
+ %2 = load i16, ptr %1, align 2
+ ret i16 %2
+}
+
+define i32 @load_two_halfword() nounwind optsize {
+; NO-C-LABEL: load_two_halfword:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: lhu a1, 2(a0)
+; NO-C-NEXT: lhu a0, 0(a0)
+; NO-C-NEXT: slli a1, a1, 16
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_two_halfword:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: lhu a1, 2(a0)
+; WITH-C-NEXT: lhu a0, 0(a0)
+; WITH-C-NEXT: slli a1, a1, 16
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: ret
+ %1 = load i32, ptr @global, align 2
+ ret i32 %1
+}
+
+define i16 @load_halfword_twice() nounwind optsize {
+; NO-C-LABEL: load_halfword_twice:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: lh a1, 0(a0)
+; NO-C-NEXT: lh a0, 0(a0)
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_halfword_twice:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: lh a1, 0(a0)
+; WITH-C-NEXT: lh a0, 0(a0)
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: ret
+ %1 = load volatile i16, ptr @global, align 2
+ %2 = load volatile i16, ptr @global, align 2
+ %3 = or i16 %1, %2
+ ret i16 %3
+}
+
+define i16 @load_halfword_twice_offset() nounwind optsize {
+; NO-C-LABEL: load_halfword_twice_offset:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global+4
+; NO-C-NEXT: lh a1, 0(a0)
+; NO-C-NEXT: lh a0, 0(a0)
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_halfword_twice_offset:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global+4
+; WITH-C-NEXT: lh a1, 0(a0)
+; WITH-C-NEXT: lh a0, 0(a0)
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: ret
+ %1 = getelementptr i16, ptr @global, i32 2
+ %2 = load volatile i16, ptr %1, align 2
+ %3 = load volatile i16, ptr %1, align 2
+ %4 = or i16 %2, %3
+ ret i16 %4
+}
+
+
+
+define i32 @load_word() nounwind optsize {
+; NO-C-LABEL: load_word:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: c.lw a0, 0(a0), %qc.access(global)
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_word:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: c.lw a0, 0(a0), %qc.access(global)
+; WITH-C-NEXT: ret
+ %1 = load i32, ptr @global, align 4
+ ret i32 %1
+}
+
+define i32 @load_word_offset() nounwind optsize {
+; NO-C-LABEL: load_word_offset:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global+4
+; NO-C-NEXT: c.lw a0, 0(a0), %qc.access(global+4)
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_word_offset:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global+4
+; WITH-C-NEXT: c.lw a0, 0(a0), %qc.access(global+4)
+; WITH-C-NEXT: ret
+ %1 = getelementptr i8, ptr @global, i32 4
+ %2 = load i32, ptr %1, align 4
+ ret i32 %2
+}
+
+define i64 @load_two_word() nounwind optsize {
+; NO-C-LABEL: load_two_word:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a1, global
+; NO-C-NEXT: lw a0, 0(a1)
+; NO-C-NEXT: lw a1, 4(a1)
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_two_word:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a1, global
+; WITH-C-NEXT: lw a0, 0(a1)
+; WITH-C-NEXT: lw a1, 4(a1)
+; WITH-C-NEXT: ret
+ %1 = load i64, ptr @global, align 4
+ ret i64 %1
+}
+
+define i32 @load_word_twice() nounwind optsize {
+; NO-C-LABEL: load_word_twice:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global
+; NO-C-NEXT: lw a1, 0(a0)
+; NO-C-NEXT: lw a0, 0(a0)
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_word_twice:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global
+; WITH-C-NEXT: lw a1, 0(a0)
+; WITH-C-NEXT: lw a0, 0(a0)
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: ret
+ %1 = load volatile i32, ptr @global, align 4
+ %2 = load volatile i32, ptr @global, align 4
+ %3 = or i32 %1, %2
+ ret i32 %3
+}
+
+define i32 @load_word_twice_offset() nounwind optsize {
+; NO-C-LABEL: load_word_twice_offset:
+; NO-C: # %bb.0:
+; NO-C-NEXT: qc.e.li a0, global+8
+; NO-C-NEXT: lw a1, 0(a0)
+; NO-C-NEXT: lw a0, 0(a0)
+; NO-C-NEXT: or a0, a0, a1
+; NO-C-NEXT: ret
+;
+; WITH-C-LABEL: load_word_twice_offset:
+; WITH-C: # %bb.0:
+; WITH-C-NEXT: qc.e.li a0, global+8
+; WITH-C-NEXT: lw a1, 0(a0)
+; WITH-C-NEXT: lw a0, 0(a0)
+; WITH-C-NEXT: or a0, a0, a1
+; WITH-C-NEXT: ret
+ %1 = getelementptr i32, ptr @global, i32 2
+ %2 = load volatile i32, ptr %1, align 4
+ %3 = load volatile i32, ptr %1, align 4
+ %4 = or i32 %2, %3
+ ret i32 %4
+}
>From 5c49a9dc978776588cad7f656438cbd7bd4577ef Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Fri, 27 Mar 2026 16:29:50 -0700
Subject: [PATCH 2/4] clang-format, add asm tests
---
llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td | 6 +-
llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp | 3 +-
llvm/test/MC/RISCV/rvi-pseudos-invalid.s | 11 +-
llvm/test/MC/RISCV/xqci-access-pseudos.s | 151 ++++++++++++++++++
4 files changed, 163 insertions(+), 8 deletions(-)
create mode 100644 llvm/test/MC/RISCV/xqci-access-pseudos.s
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index 10a5e84b2d9df..724b1a5d8a4c7 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1393,7 +1393,7 @@ class PseudoQCAccessStore_rri<string opcodestr>
[], opcodestr, "$rs2, ${imm12}(${rs1}), $expr">;
-let Predicates = [HasVendorXqcili] in {
+let Predicates = [IsRV32] in {
def PseudoQCAccessLB : PseudoQCAccessLoad_ri<"lb">;
def PseudoQCAccessLBU : PseudoQCAccessLoad_ri<"lbu">;
def PseudoQCAccessLH : PseudoQCAccessLoad_ri<"lh">;
@@ -1433,7 +1433,7 @@ class PseudoQCAccessCStore_rri<string opcodestr, DAGOperand immop>
(ins GPRC:$rs2, GPRCMem:$rs1, immop:$imm, qc_access_symbol:$expr),
[], opcodestr, "$rs2, ${imm}(${rs1}), $expr">;
-let Predicates = [HasVendorXqcili, HasStdExtZca] in {
+let Predicates = [IsRV32, HasStdExtZca] in {
def PseudoQCAccessC_LW : PseudoQCAccessCLoad_ri<"c.lw", uimm7_lsb00>;
let EmitPriority = 0 in
def : InstAlias<"c.lw $rd, (${rs1}), $expr", (PseudoQCAccessC_LW GPRC:$rd, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
@@ -1443,7 +1443,7 @@ let EmitPriority = 0 in
def : InstAlias<"c.sw $rs2, (${rs1}), $expr", (PseudoQCAccessC_SW GPRC:$rs2, GPRCMem:$rs1, 0, qc_access_symbol:$expr)>;
}
-let Predicates = [HasVendorXqcili, HasStdExtZcb] in {
+let Predicates = [IsRV32, HasStdExtZcb] in {
// no C_LB so no PseudoQCAccessC_LB
def PseudoQCAccessC_LBU : PseudoQCAccessCLoad_ri<"c.lbu", uimm2>;
def PseudoQCAccessC_LH : PseudoQCAccessCLoad_ri<"c.lh", uimm2_lsb0>;
diff --git a/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp b/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
index 9956bc82efe00..b4f1ed9ec87da 100644
--- a/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
+++ b/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
@@ -188,7 +188,8 @@ bool RISCVQCRelaxMarking::runOnMachineFunction(MachineFunction &MF) {
continue;
MachineOperand &SymOp = MI->getOperand(1);
- if (!SymOp.isSymbol() && !SymOp.isGlobal() && !SymOp.isMCSymbol() && !SymOp.isCPI())
+ if (!SymOp.isSymbol() && !SymOp.isGlobal() && !SymOp.isMCSymbol() &&
+ !SymOp.isCPI())
continue;
unsigned NewOpc = getQCMarkedOpcode(*NextMI, STI);
diff --git a/llvm/test/MC/RISCV/rvi-pseudos-invalid.s b/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
index 2dcab572fb7b6..d5b6db3b80b21 100644
--- a/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
+++ b/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
@@ -1,5 +1,5 @@
-# RUN: not llvm-mc %s -triple=riscv32 2>&1 | FileCheck %s
-# RUN: not llvm-mc %s -triple=riscv64 2>&1 | FileCheck %s
+# RUN: not llvm-mc %s -triple=riscv32 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-RV32 %s
+# RUN: not llvm-mc %s -triple=riscv64 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-RV64 %s
lga x1, 1234 # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
lga x1, %pcrel_hi(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
@@ -11,9 +11,12 @@ lga x1, %lo(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol na
lga x1, %hi(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
lga x1, %lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-sw a2, %hi(a_symbol), a3 # CHECK: :[[@LINE]]:8: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo specifier or an integer in the range [-2048, 2047]
+sw a2, %hi(a_symbol), a3
+# CHECK-RV32: :[[@LINE-1]]:8: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK-RV64: :[[@LINE-2]]:8: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo specifier or an integer in the range [-2048, 2047]
+
sw a2, %lo(a_symbol), a3 # CHECK: :[[@LINE]]:23: error: invalid operand for instruction
-sw a2, %lo(a_symbol)(a4), a3 # CHECK: :[[@LINE]]:27: error: invalid operand for instruction
+sw a2, %lo(a_symbol)(a4), a3 # CHECK: :[[@LINE]]:27: error: expected '%' relocation specifier
# Too few operands must be rejected
sw a2, a_symbol # CHECK: :[[@LINE]]:1: error: too few operands for instruction
diff --git a/llvm/test/MC/RISCV/xqci-access-pseudos.s b/llvm/test/MC/RISCV/xqci-access-pseudos.s
new file mode 100644
index 0000000000000..aaabc15b0712a
--- /dev/null
+++ b/llvm/test/MC/RISCV/xqci-access-pseudos.s
@@ -0,0 +1,151 @@
+# RUN: llvm-mc %s -triple=riscv32 -mattr=+zca,+zcb -M no-aliases -show-encoding \
+# RUN: | FileCheck -check-prefixes=CHECK-ENC,CHECK-INST,CHECK-ASM %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+zca,+zcb < %s \
+# RUN: | llvm-objdump --mattr=+zca,+zcb -M no-aliases --no-print-imm-hex -d - \
+# RUN: | FileCheck -check-prefix=CHECK-INST %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+zca,+zcb,+relax < %s \
+# RUN: | llvm-objdump --mattr=+zca,+zcb -M no-aliases --no-print-imm-hex -dr - \
+# RUN: | FileCheck -check-prefix=CHECK-INST,CHECK-RELOC %s
+
+# RUN: not llvm-mc %s -triple=riscv64 -mattr=+zca,+zcb 2>&1 \
+# RUN: | FileCheck -check-prefix=CHECK-RV64 %s
+
+## This tests the instructions which accept %qc.access(...), which are the
+## RVI, Zca, and Zcb loads and stores, on RV32 only.
+
+lb a0, 0(a1), %qc.access(extern1)
+# CHECK-INST: lb a0, 0(a1)
+# CHECK-ASM-SAME: , %qc.access(extern1)
+# CHECK-ENC-SAME: [0x03,0x85,0x05,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern1
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+lbu a0, 0(a1), %qc.access(extern1)
+# CHECK-INST: lbu a0, 0(a1)
+# CHECK-ASM-SAME: , %qc.access(extern1)
+# CHECK-ENC-SAME: [0x03,0xc5,0x05,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern1
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+lh a2, 0(a3), %qc.access(extern2)
+# CHECK-INST: lh a2, 0(a3)
+# CHECK-ASM-SAME: , %qc.access(extern2)
+# CHECK-ENC-SAME: [0x03,0x96,0x06,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern2
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+lhu a2, 0(a3), %qc.access(extern2)
+# CHECK-INST: lhu a2, 0(a3)
+# CHECK-ASM-SAME: , %qc.access(extern2)
+# CHECK-ENC-SAME: [0x03,0xd6,0x06,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern2
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+lw a4, 0(a5), %qc.access(extern4)
+# CHECK-INST: lw a4, 0(a5)
+# CHECK-ASM-SAME: , %qc.access(extern4)
+# CHECK-ENC-SAME: [0x03,0xa7,0x07,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern4
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+sb a0, 0(a1), %qc.access(extern1)
+# CHECK-INST: sb a0, 0(a1)
+# CHECK-ASM-SAME: , %qc.access(extern1)
+# CHECK-ENC-SAME: [0x23,0x80,0xa5,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern1
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+sh a2, 0(a3), %qc.access(extern2)
+# CHECK-INST: sh a2, 0(a3)
+# CHECK-ASM-SAME: , %qc.access(extern2)
+# CHECK-ENC-SAME: [0x23,0x90,0xc6,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern2
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+sw a4, 0(a5), %qc.access(extern4)
+# CHECK-INST: sw a4, 0(a5)
+# CHECK-ASM-SAME: , %qc.access(extern4)
+# CHECK-ENC-SAME: [0x23,0xa0,0xe7,0x00]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_32 extern4
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+## No c.lb
+
+c.lbu a0, 0(a1), %qc.access(extern1)
+# CHECK-INST: c.lbu a0, 0(a1)
+# CHECK-ASM-SAME: , %qc.access(extern1)
+# CHECK-ENC-SAME: [0x88,0x81]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_16 extern1
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+c.lh a2, 0(a3), %qc.access(extern2)
+# CHECK-INST: c.lh a2, 0(a3)
+# CHECK-ASM-SAME: , %qc.access(extern2)
+# CHECK-ENC-SAME: [0xd0,0x86]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_16 extern2
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+c.lhu a2, 0(a3), %qc.access(extern2)
+# CHECK-INST: c.lhu a2, 0(a3)
+# CHECK-ASM-SAME: , %qc.access(extern2)
+# CHECK-ENC-SAME: [0x90,0x86]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_16 extern2
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+c.lw a4, 0(a5), %qc.access(extern4)
+# CHECK-INST: c.lw a4, 0(a5)
+# CHECK-ASM-SAME: , %qc.access(extern4)
+# CHECK-ENC-SAME: [0x98,0x43]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_16 extern4
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+c.sb a0, 0(a1), %qc.access(extern1)
+# CHECK-INST: c.sb a0, 0(a1)
+# CHECK-ASM-SAME: , %qc.access(extern1)
+# CHECK-ENC-SAME: [0x88,0x89]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_16 extern1
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+c.sh a2, 0(a3), %qc.access(extern2)
+# CHECK-INST: c.sh a2, 0(a3)
+# CHECK-ASM-SAME: , %qc.access(extern2)
+# CHECK-ENC-SAME: [0x90,0x8e]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_16 extern2
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
+
+c.sw a4, 0(a5), %qc.access(extern4)
+# CHECK-INST: c.sw a4, 0(a5)
+# CHECK-ASM-SAME: , %qc.access(extern4)
+# CHECK-ENC-SAME: [0x98,0xc3]
+# CHECK-RELOC: R_RISCV_VENDOR QUALCOMM
+# CHECK-RELOC-NEXT: R_RISCV_QC_ACCESS_16 extern4
+# CHECK-RELOC-NEXT: R_RISCV_RELAX
+# CHECK-RV64: [[@LINE-7]]:1: error: instruction requires the following: RV32I Base Instruction Set
>From e6044c130b190def647cca57d329e4f55a6f5dc1 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Fri, 27 Mar 2026 19:13:40 -0700
Subject: [PATCH 3/4] review feedback
---
.../Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 3 +--
llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td | 12 ++++++------
llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp | 8 ++++----
3 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 0c531498da278..b103eee1d81f5 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -483,7 +483,7 @@ void RISCVMCCodeEmitter::expandPseudoQCAccess(
if (!STI.hasFeature(RISCV::FeatureRelax))
return;
- MCOperand AccessSymbol = MI.getOperand(3);
+ const MCOperand &AccessSymbol = MI.getOperand(3);
assert(AccessSymbol.isExpr() && "Expected expression in PseudoQCAccess");
const auto *AccessExpr = dyn_cast<MCSpecifierExpr>(AccessSymbol.getExpr());
@@ -799,7 +799,6 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
FixupKind = RISCV::fixup_riscv_qc_abs20_u;
RelaxCandidate = true;
break;
-
case ELF::R_RISCV_GOT_HI20:
case ELF::R_RISCV_TPREL_HI20:
case ELF::R_RISCV_TLSDESC_HI20:
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index 724b1a5d8a4c7..b53f41b4cf6f2 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1379,14 +1379,14 @@ def qc_access_symbol : Operand<XLenVT> {
}
// This is modelled after `PseudoAddTPRel` but with `mayLoad = true`
-let hasSideEffects = false, mayLoad = true, mayStore = false, Size = 32,
+let hasSideEffects = false, mayLoad = true, mayStore = false, Size = 4,
isCodeGenOnly = false in
class PseudoQCAccessLoad_ri<string opcodestr>
: Pseudo<(outs GPR:$rd),
(ins GPRMem:$rs1, simm12:$imm12, qc_access_symbol:$expr), [],
opcodestr, "$rd, ${imm12}(${rs1}), $expr">;
-let hasSideEffects = false, mayLoad = false, mayStore = true, Size = 32,
+let hasSideEffects = false, mayLoad = false, mayStore = true, Size = 4,
isCodeGenOnly = false in
class PseudoQCAccessStore_rri<string opcodestr>
: Pseudo<(outs), (ins GPR:$rs2, GPRMem:$rs1, simm12:$imm12, qc_access_symbol:$expr),
@@ -1398,7 +1398,7 @@ def PseudoQCAccessLB : PseudoQCAccessLoad_ri<"lb">;
def PseudoQCAccessLBU : PseudoQCAccessLoad_ri<"lbu">;
def PseudoQCAccessLH : PseudoQCAccessLoad_ri<"lh">;
def PseudoQCAccessLHU : PseudoQCAccessLoad_ri<"lhu">;
-def PseudoQCAccessLW : PseudoQCAccessLoad_ri<"lw">;
+def PseudoQCAccessLW : PseudoQCAccessLoad_ri<"lw">;
let EmitPriority = 0 in {
def : InstAlias<"lb $rd, (${rs1}), $expr", (PseudoQCAccessLB GPR:$rd, GPRMem:$rs1, 0, qc_access_symbol:$expr)>;
@@ -1419,14 +1419,14 @@ def : InstAlias<"sw $rs2, (${rs1}), $expr", (PseudoQCAccessSW GPR:$rs2, GPRMem:$
}
}
-let hasSideEffects = false, mayLoad = true, mayStore = false, Size = 16,
+let hasSideEffects = false, mayLoad = true, mayStore = false, Size = 2,
isCodeGenOnly = false in
class PseudoQCAccessCLoad_ri<string opcodestr, DAGOperand immop>
: Pseudo<(outs GPRC:$rd),
(ins GPRCMem:$rs1, immop:$imm, qc_access_symbol:$expr),
[], opcodestr, "$rd, ${imm}(${rs1}), $expr">;
-let hasSideEffects = false, mayLoad = false, mayStore = true, Size = 16,
+let hasSideEffects = false, mayLoad = false, mayStore = true, Size = 2,
isCodeGenOnly = false in
class PseudoQCAccessCStore_rri<string opcodestr, DAGOperand immop>
: Pseudo<(outs),
@@ -1446,7 +1446,7 @@ def : InstAlias<"c.sw $rs2, (${rs1}), $expr", (PseudoQCAccessC_SW GPRC:$rs2, GPR
let Predicates = [IsRV32, HasStdExtZcb] in {
// no C_LB so no PseudoQCAccessC_LB
def PseudoQCAccessC_LBU : PseudoQCAccessCLoad_ri<"c.lbu", uimm2>;
-def PseudoQCAccessC_LH : PseudoQCAccessCLoad_ri<"c.lh", uimm2_lsb0>;
+def PseudoQCAccessC_LH : PseudoQCAccessCLoad_ri<"c.lh", uimm2_lsb0>;
def PseudoQCAccessC_LHU : PseudoQCAccessCLoad_ri<"c.lhu", uimm2_lsb0>;
let EmitPriority = 0 in {
diff --git a/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp b/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
index b4f1ed9ec87da..c0f8dae87ea7b 100644
--- a/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
+++ b/llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp
@@ -71,13 +71,13 @@ FunctionPass *llvm::createRISCVQCRelaxMarkingPass() {
return new RISCVQCRelaxMarking();
}
-static bool isLoad(MachineInstr &MI) {
+static bool isLoad(const MachineInstr &MI) {
return llvm::is_contained(
{RISCV::LW, RISCV::LH, RISCV::LHU, RISCV::LB, RISCV::LBU},
MI.getOpcode());
}
-static bool isStore(MachineInstr &MI) {
+static bool isStore(const MachineInstr &MI) {
return llvm::is_contained({RISCV::SW, RISCV::SH, RISCV::SB}, MI.getOpcode());
}
@@ -171,8 +171,8 @@ bool RISCVQCRelaxMarking::runOnMachineFunction(MachineFunction &MF) {
!(isLoad(*NextMI) || isStore(*NextMI)))
continue;
- LLVM_DEBUG(llvm::dbgs() << "Found QC_E_LI " << *MI);
- LLVM_DEBUG(llvm::dbgs() << "Followed by Load/Store " << *NextMI);
+ LLVM_DEBUG(dbgs() << "Found QC_E_LI " << *MI);
+ LLVM_DEBUG(dbgs() << "Followed by Load/Store " << *NextMI);
MachineOperand &OffsetOp = NextMI->getOperand(2);
if (OffsetOp.getImm() != 0)
>From a2c8d20760e3b693d7365a0f5f00bd267384bbdc Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Sat, 28 Mar 2026 02:27:35 -0700
Subject: [PATCH 4/4] rv32 only pass
---
llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 5 +++--
llvm/test/CodeGen/RISCV/O0-pipeline.ll | 4 ++--
llvm/test/CodeGen/RISCV/O3-pipeline.ll | 4 ++--
3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
index f85046b6c1f83..e9e7e5f958e94 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp
@@ -594,8 +594,9 @@ void RISCVPassConfig::addPreEmitPass2() {
}
addPass(createRISCVExpandPseudoPass());
- // Add QC Relaxation Markers as late as possible.
- addPass(createRISCVQCRelaxMarkingPass());
+ // Add QC Relaxation Markers as late as possible, and only for RV32
+ if (TM->getTargetTriple().isRISCV32())
+ addPass(createRISCVQCRelaxMarkingPass());
// Schedule the expansion of AMOs at the last possible moment, avoiding the
// possibility for other passes to break the requirements for forward
diff --git a/llvm/test/CodeGen/RISCV/O0-pipeline.ll b/llvm/test/CodeGen/RISCV/O0-pipeline.ll
index 84c005fb539dd..049d39bef2493 100644
--- a/llvm/test/CodeGen/RISCV/O0-pipeline.ll
+++ b/llvm/test/CodeGen/RISCV/O0-pipeline.ll
@@ -1,6 +1,6 @@
; RUN: llc -mtriple=riscv32 -O0 -debug-pass=Structure < %s -o /dev/null 2>&1 | \
; RUN: grep -v "Verify generated machine code" | \
-; RUN: FileCheck %s --check-prefixes=CHECK
+; RUN: FileCheck %s --check-prefixes=CHECK,RV32
; RUN: llc -mtriple=riscv64 -O0 -debug-pass=Structure < %s -o /dev/null 2>&1 | \
; RUN: grep -v "Verify generated machine code" | \
; RUN: FileCheck %s --check-prefixes=CHECK
@@ -76,7 +76,7 @@
; CHECK-NEXT: Machine Optimization Remark Emitter
; CHECK-NEXT: Stack Frame Layout Analysis
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
-; CHECK-NEXT: RISC-V QC Relaxation Marking
+; RV32-NEXT: RISC-V QC Relaxation Marking
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
; CHECK-NEXT: Unpack machine instruction bundles
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
diff --git a/llvm/test/CodeGen/RISCV/O3-pipeline.ll b/llvm/test/CodeGen/RISCV/O3-pipeline.ll
index 91b0005d4fd6e..6244a00fbc15a 100644
--- a/llvm/test/CodeGen/RISCV/O3-pipeline.ll
+++ b/llvm/test/CodeGen/RISCV/O3-pipeline.ll
@@ -1,6 +1,6 @@
; RUN: llc -mtriple=riscv32 -O3 -debug-pass=Structure < %s -o /dev/null 2>&1 | \
; RUN: grep -v "Verify generated machine code" | \
-; RUN: FileCheck %s --check-prefixes=CHECK
+; RUN: FileCheck %s --check-prefixes=CHECK,RV32
; RUN: llc -mtriple=riscv64 -O3 -debug-pass=Structure < %s -o /dev/null 2>&1 | \
; RUN: grep -v "Verify generated machine code" | \
; RUN: FileCheck %s --check-prefixes=CHECK,RV64
@@ -227,7 +227,7 @@
; CHECK-NEXT: RISC-V Zcmp move merging pass
; CHECK-NEXT: RISC-V Zcmp Push/Pop optimization pass
; CHECK-NEXT: RISC-V pseudo instruction expansion pass
-; CHECK-NEXT: RISC-V QC Relaxation Marking
+; RV32-NEXT: RISC-V QC Relaxation Marking
; CHECK-NEXT: RISC-V atomic pseudo instruction expansion pass
; CHECK-NEXT: Unpack machine instruction bundles
; CHECK-NEXT: Lazy Machine Block Frequency Analysis
More information about the llvm-commits
mailing list