Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[RISCV] Support Qualcomm Access Relocations#188671

Merged
lenary merged 10 commits into
llvm:mainfrom
lenary:pr/riscv-qc-e-li-annotate
Jun 4, 2026
Merged

[RISCV] Support Qualcomm Access Relocations#188671
lenary merged 10 commits into
llvm:mainfrom
lenary:pr/riscv-qc-e-li-annotate

Conversation

@lenary
Copy link
Copy Markdown
Member

@lenary lenary commented Mar 26, 2026

These QUALCOMM vendor relocations mark 16-bit compressed and 32-bit load/store instructions as candidates for relaxation from a QC_E_LI + Load/Store sequence.

This change adds support for assembling instructions with these relocations. These relocations are documented in https://github.com/quic/riscv-elf-psabi-quic-extensions

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 26, 2026

🐧 Linux x64 Test Results

  • 196141 tests passed
  • 5309 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 26, 2026

🪟 Windows x64 Test Results

  • 135350 tests passed
  • 3360 tests skipped

✅ The build succeeded and all tests passed.

@lenary lenary force-pushed the pr/riscv-qc-e-li-annotate branch from fe60291 to 1c3e13c Compare March 26, 2026 07:59
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Mar 26, 2026

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

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.
@lenary lenary force-pushed the pr/riscv-qc-e-li-annotate branch from 1c3e13c to 190da79 Compare March 26, 2026 18:46
Comment thread llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp Outdated
Comment thread llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp Outdated
Comment thread llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td Outdated
Comment thread llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp Outdated
Comment thread llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td Outdated
Comment thread llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp Outdated
Comment thread llvm/lib/Target/RISCV/RISCVQCRelaxMarking.cpp Outdated
Comment thread llvm/lib/Target/RISCV/RISCVTargetMachine.cpp Outdated
@lenary lenary changed the title [RISCV] Mark QC Instructions for Relaxation [RISCV] Support Qualcomm Access Relocations Jun 2, 2026
@lenary lenary marked this pull request as ready for review June 2, 2026 20:23
@llvmorg-github-actions
Copy link
Copy Markdown

llvmorg-github-actions Bot commented Jun 2, 2026

@llvm/pr-subscribers-llvm-binary-utilities

@llvm/pr-subscribers-backend-risc-v

Author: Sam Elliott (lenary)

Changes

These QUALCOMM vendor relocations mark 16-bit compressed and 32-bit load/store instructions as candidates for relaxation from a QC_E_LI + Load/Store sequence.

This change adds support for assembling instructions with these relocations. These relocations are documented in https://github.com/quic/riscv-elf-psabi-quic-extensions


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

15 Files Affected:

  • (modified) llvm/include/llvm/BinaryFormat/ELFRelocs/RISCV_nonstandard.def (+2)
  • (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+11)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp (+12)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.cpp (+1)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h (+1)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp (+4)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h (+3)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp (+1)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h (+1)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp (+99)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp (+3)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.cpp (+1)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td (+113)
  • (modified) llvm/test/MC/RISCV/rvi-pseudos-invalid.s (+7-4)
  • (added) llvm/test/MC/RISCV/xqci-access-pseudos.s (+151)
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 93fb1fa5a889f..ca5841e8e6c57 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -625,6 +625,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/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index 4e3ed3f5ae31f..8e4dab0a59c98 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 d5645dc61e6b8..72889be9ca5a2 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 7f1aa83075f1f..0eba38f2c8962 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
@@ -80,6 +80,7 @@ enum OperandType : unsigned {
   OPERAND_SIMM10_LSB0000_NONZERO,
   OPERAND_SIMM10_UNSIGNED,
   OPERAND_SIMM11,
+  OPERAND_SIMM12,
   OPERAND_SIMM12_LSB00000,
   OPERAND_SIMM16,
   OPERAND_SIMM16_NONZERO,
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 aa7ce5bf5e2a2..28e7839493c6d 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 f9477b00a6744..ee1fffc4886c8 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCAsmInfo.h
@@ -49,6 +49,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 7a8926b197cb7..1b947ecc5f2bd 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -76,6 +76,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,
@@ -477,6 +481,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;
+
+  const 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,
@@ -543,6 +618,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) {
@@ -714,6 +807,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;
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/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index c622d188c67a1..7ccffdbdb36d9 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -3075,6 +3075,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:
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index 96203641513e9..1c66893800e76 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1365,6 +1365,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 = 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 = 4,
+  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 = [IsRV32] 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 = 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 = 2,
+  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 = [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)>;
+
+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 = [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_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/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: operan...
[truncated]

Comment thread llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp Outdated
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)>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we wrap these closer to 80 columns?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

They're closer now, but not yet at 80 cols.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's fine. I'm not going be strict about it. They just looked quite a bit over.

Comment thread llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td Outdated
Comment thread llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td Outdated
Comment thread llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td Outdated
Comment thread llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td Outdated
@lenary lenary requested a review from topperc June 3, 2026 20:35
Copy link
Copy Markdown
Contributor

@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
Copy link
Copy Markdown
Member Author

lenary commented Jun 3, 2026

I will leave this another 24 hours before merging.

@lenary lenary merged commit a81c5a6 into llvm:main Jun 4, 2026
10 checks passed
@lenary lenary deleted the pr/riscv-qc-e-li-annotate branch June 4, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants