[llvm] [RISCV] Add symbol parsing support for Xqcilb long branch instructions (PR #135044)

Sudharsan Veeravalli via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 9 20:23:08 PDT 2025


https://github.com/svs-quic updated https://github.com/llvm/llvm-project/pull/135044

>From b59becf6f2935114409be5b8faafece631bc5ca3 Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Wed, 9 Apr 2025 21:50:58 +0530
Subject: [PATCH 1/2] [RISCV] Add symbol parsing support for Xqcilb long branch
 instructions

---
 .../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 40 +++++++++++++
 .../RISCV/MCTargetDesc/RISCVAsmBackend.cpp    | 16 +++++
 .../MCTargetDesc/RISCVELFObjectWriter.cpp     |  2 +
 .../RISCV/MCTargetDesc/RISCVFixupKinds.h      |  2 +
 .../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 32 ++++++++++
 .../Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp |  5 +-
 .../Target/RISCV/MCTargetDesc/RISCVMCExpr.h   |  1 +
 llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td   | 24 +++++++-
 llvm/test/MC/RISCV/xqcilb-relocations.s       | 59 +++++++++++++++++++
 9 files changed, 179 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/MC/RISCV/xqcilb-relocations.s

diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index c57c123ab01dc..2af8282ac6361 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -202,6 +202,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
   ParseStatus parseOperandWithSpecifier(OperandVector &Operands);
   ParseStatus parseBareSymbol(OperandVector &Operands);
   ParseStatus parseCallSymbol(OperandVector &Operands);
+  ParseStatus parsePseudoQCJumpSymbol(OperandVector &Operands);
   ParseStatus parsePseudoJumpSymbol(OperandVector &Operands);
   ParseStatus parseJALOffset(OperandVector &Operands);
   ParseStatus parseVTypeI(OperandVector &Operands);
@@ -590,6 +591,17 @@ struct RISCVOperand final : public MCParsedAsmOperand {
            (VK == RISCVMCExpr::VK_CALL || VK == RISCVMCExpr::VK_CALL_PLT);
   }
 
+  bool isPseudoQCJumpSymbol() const {
+    int64_t Imm;
+    // Must be of 'immediate' type but not a constant.
+    if (!isImm() || evaluateConstantImm(getImm(), Imm))
+      return false;
+
+    RISCVMCExpr::Specifier VK = RISCVMCExpr::VK_None;
+    return RISCVAsmParser::classifySymbolRef(getImm(), VK) &&
+           VK == RISCVMCExpr::VK_QC_E_JUMP_PLT;
+  }
+
   bool isPseudoJumpSymbol() const {
     int64_t Imm;
     // Must be of 'immediate' type but not a constant.
@@ -2136,6 +2148,34 @@ ParseStatus RISCVAsmParser::parseCallSymbol(OperandVector &Operands) {
   return ParseStatus::Success;
 }
 
+ParseStatus RISCVAsmParser::parsePseudoQCJumpSymbol(OperandVector &Operands) {
+  SMLoc S = getLoc();
+  const MCExpr *Res;
+
+  if (getLexer().getKind() != AsmToken::Identifier)
+    return ParseStatus::NoMatch;
+  std::string Identifier(getTok().getIdentifier());
+
+  if (getLexer().peekTok().is(AsmToken::At)) {
+    Lex();
+    Lex();
+    StringRef PLT;
+    if (getParser().parseIdentifier(PLT) || PLT != "plt")
+      return ParseStatus::Failure;
+  } else {
+    Lex();
+  }
+
+  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
+  RISCVMCExpr::Specifier Kind = RISCVMCExpr::VK_QC_E_JUMP_PLT;
+
+  MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier);
+  Res = MCSymbolRefExpr::create(Sym, getContext());
+  Res = RISCVMCExpr::create(Res, Kind, getContext());
+  Operands.push_back(RISCVOperand::createImm(Res, S, E, isRV64()));
+  return ParseStatus::Success;
+}
+
 ParseStatus RISCVAsmParser::parsePseudoJumpSymbol(OperandVector &Operands) {
   SMLoc S = getLoc();
   SMLoc E;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index c6c2e0810b75e..49c8c6957aa34 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -95,6 +95,7 @@ RISCVAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
       {"fixup_riscv_qc_e_branch", 0, 48, MCFixupKindInfo::FKF_IsPCRel},
       {"fixup_riscv_qc_e_32", 16, 32, 0},
       {"fixup_riscv_qc_abs20_u", 12, 20, 0},
+      {"fixup_riscv_qc_e_jump_plt", 0, 48, MCFixupKindInfo::FKF_IsPCRel},
   };
   static_assert((std::size(Infos)) == RISCV::NumTargetFixupKinds,
                 "Not all fixup kinds added to Infos array");
@@ -575,6 +576,21 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
     Value = (Bit19 << 31) | (Bit14_0 << 16) | (Bit18_15 << 12);
     return Value;
   }
+  case RISCV::fixup_riscv_qc_e_jump_plt: {
+    if (!isInt<32>(Value))
+      Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
+    if (Value & 0x1)
+      Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned");
+    uint64_t Bit31_16 = (Value >> 16) & 0xffff;
+    uint64_t Bit12 = (Value >> 12) & 0x1;
+    uint64_t Bit10_5 = (Value >> 5) & 0x3f;
+    uint64_t Bit15_13 = (Value >> 13) & 0x7;
+    uint64_t Bit4_1 = (Value >> 1) & 0xf;
+    uint64_t Bit11 = (Value >> 11) & 0x1;
+    Value = (Bit31_16 << 32ull) | (Bit12 << 31) | (Bit10_5 << 25) |
+            (Bit15_13 << 17) | (Bit4_1 << 8) | (Bit11 << 7);
+    return Value;
+  }
   }
 }
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
index 9859bf39bcc5e..59dc79b57b456 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
@@ -119,6 +119,8 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
       return ELF::R_RISCV_CALL_PLT;
     case RISCV::fixup_riscv_qc_e_branch:
       return ELF::R_RISCV_QC_E_BRANCH;
+    case RISCV::fixup_riscv_qc_e_jump_plt:
+      return ELF::R_RISCV_QC_E_JUMP_PLT;
     }
   }
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
index a4f5673fca225..596c4eb5fffaa 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
@@ -84,6 +84,8 @@ enum Fixups {
   fixup_riscv_qc_e_32,
   // 20-bit fixup for symbol references in the 32-bit qc.li instruction
   fixup_riscv_qc_abs20_u,
+  // 32-bit fixup for symbol references in the 48-bit qc.j/qc.jal instructions
+  fixup_riscv_qc_e_jump_plt,
 
   // Used as a sentinel, must be the last
   fixup_riscv_invalid,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index 95858da45f202..f324907d49fd9 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
@@ -56,6 +56,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
                           SmallVectorImpl<MCFixup> &Fixups,
                           const MCSubtargetInfo &STI) const;
 
+  void expandQCJump(const MCInst &MI, SmallVectorImpl<char> &CB,
+                    SmallVectorImpl<MCFixup> &Fixups,
+                    const MCSubtargetInfo &STI) const;
+
   void expandTLSDESCCall(const MCInst &MI, SmallVectorImpl<char> &CB,
                          SmallVectorImpl<MCFixup> &Fixups,
                          const MCSubtargetInfo &STI) const;
@@ -169,6 +173,26 @@ void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI,
   support::endian::write(CB, Binary, llvm::endianness::little);
 }
 
+void RISCVMCCodeEmitter::expandQCJump(const MCInst &MI,
+                                      SmallVectorImpl<char> &CB,
+                                      SmallVectorImpl<MCFixup> &Fixups,
+                                      const MCSubtargetInfo &STI) const {
+  MCOperand Func = MI.getOperand(0);
+  assert(Func.isExpr() && "Expected expression");
+
+  auto Opcode =
+      (MI.getOpcode() == RISCV::PseudoQC_E_J) ? RISCV::QC_E_J : RISCV::QC_E_JAL;
+  MCInst Jump = MCInstBuilder(Opcode).addExpr(Func.getExpr());
+
+  uint64_t Bits = getBinaryCodeForInstr(Jump, Fixups, STI) & 0xffff'ffff'ffffu;
+  SmallVector<char, 8> Encoding;
+  support::endian::write(Encoding, Bits, llvm::endianness::little);
+  assert(Encoding[6] == 0 && Encoding[7] == 0 &&
+         "Unexpected encoding for 48-bit instruction");
+  Encoding.truncate(6);
+  CB.append(Encoding);
+}
+
 void RISCVMCCodeEmitter::expandTLSDESCCall(const MCInst &MI,
                                            SmallVectorImpl<char> &CB,
                                            SmallVectorImpl<MCFixup> &Fixups,
@@ -440,6 +464,11 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI,
     expandTLSDESCCall(MI, CB, Fixups, STI);
     MCNumEmitted += 1;
     return;
+  case RISCV::PseudoQC_E_J:
+  case RISCV::PseudoQC_E_JAL:
+    expandQCJump(MI, CB, Fixups, STI);
+    MCNumEmitted += 1;
+    return;
   }
 
   switch (Size) {
@@ -656,6 +685,9 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
     case RISCVMCExpr::VK_QC_ABS20:
       FixupKind = RISCV::fixup_riscv_qc_abs20_u;
       break;
+    case RISCVMCExpr::VK_QC_E_JUMP_PLT:
+      FixupKind = RISCV::fixup_riscv_qc_e_jump_plt;
+      break;
     }
   } else if (Kind == MCExpr::SymbolRef || Kind == MCExpr::Binary) {
     // FIXME: Sub kind binary exprs have chance of underflow.
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
index d6650e156c8b3..99f72620f97ed 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
@@ -34,7 +34,8 @@ const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, Specifier S,
 
 void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const {
   Specifier S = getSpecifier();
-  bool HasVariant = ((S != VK_None) && (S != VK_CALL) && (S != VK_CALL_PLT));
+  bool HasVariant = ((S != VK_None) && (S != VK_CALL) && (S != VK_CALL_PLT) &&
+                     (S != VK_QC_E_JUMP_PLT));
 
   if (HasVariant)
     OS << '%' << getSpecifierName(S) << '(';
@@ -167,6 +168,8 @@ StringRef RISCVMCExpr::getSpecifierName(Specifier S) {
     return "pltpcrel";
   case VK_QC_ABS20:
     return "qc.abs20";
+  case VK_QC_E_JUMP_PLT:
+    return "qc_e_jump_plt";
   }
   llvm_unreachable("Invalid ELF symbol kind");
 }
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
index e0aa7ff244521..d60879d34dc17 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
@@ -44,6 +44,7 @@ class RISCVMCExpr : public MCTargetExpr {
     VK_TLSDESC_ADD_LO,
     VK_TLSDESC_CALL,
     VK_QC_ABS20,
+    VK_QC_E_JUMP_PLT
   };
 
 private:
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index 78bf6337b4a00..4ac17c8283866 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -153,6 +153,18 @@ def simm32_lsb0 : Operand<OtherVT> {
   let OperandType = "OPERAND_PCREL";
 }
 
+def PseudoQCJumpSymbol : AsmOperandClass {
+  let Name = "PseudoQCJumpSymbol";
+  let RenderMethod = "addImmOperands";
+  let DiagnosticType = "InvalidPseudoQCJumpSymbol";
+  let DiagnosticString = "operand must be a valid jump target";
+  let ParserMethod = "parsePseudoQCJumpSymbol";
+}
+
+def pseudo_qc_jump_symbol : Operand<XLenVT> {
+  let ParserMatchClass = PseudoQCJumpSymbol;
+}
+
 //===----------------------------------------------------------------------===//
 // Instruction Formats
 //===----------------------------------------------------------------------===//
@@ -708,7 +720,7 @@ class QCIRVInstEI<bits<3> funct3, bits<2> funct2, string opcodestr>
 let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
 class QCIRVInst48EJ<bits<2> func2, string opcodestr>
     : RVInst48<(outs), (ins simm32_lsb0:$imm31),
-               opcodestr, "$imm31", [], InstFormatOther> {
+               opcodestr, "$imm31", [], InstFormatQC_EJ> {
   bits<31> imm31;
 
   let Inst{47-32} = imm31{30-15};
@@ -1219,6 +1231,16 @@ def PseudoQC_E_SH : PseudoStore<"qc.e.sh">;
 def PseudoQC_E_SW : PseudoStore<"qc.e.sw">;
 } // Predicates = [HasVendorXqcilo, IsRV32]
 
+let isCall = 0, isBarrier = 1, isTerminator = 1,
+    isCodeGenOnly = 0, hasSideEffects = 0, mayStore = 0, mayLoad = 0 in
+def PseudoQC_E_J : Pseudo<(outs), (ins pseudo_qc_jump_symbol:$func), [],
+                          "qc.e.j", "$func">;
+
+let isCall = 1, Defs = [X1], isCodeGenOnly = 0, hasSideEffects = 0,
+    mayStore = 0, mayLoad = 0 in
+def PseudoQC_E_JAL: Pseudo<(outs), (ins pseudo_qc_jump_symbol:$func), [],
+                           "qc.e.jal", "$func">;
+
 //===----------------------------------------------------------------------===//
 // Code Gen Patterns
 //===----------------------------------------------------------------------===//
diff --git a/llvm/test/MC/RISCV/xqcilb-relocations.s b/llvm/test/MC/RISCV/xqcilb-relocations.s
new file mode 100644
index 0000000000000..a475cde3f6bfd
--- /dev/null
+++ b/llvm/test/MC/RISCV/xqcilb-relocations.s
@@ -0,0 +1,59 @@
+# RUN: llvm-mc -triple riscv32 -mattr=+experimental-xqcilb %s -show-encoding \
+# RUN:     | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s
+# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcilb %s -o %t.o
+# RUN: llvm-readobj -r %t.o | FileCheck -check-prefix=RELOC %s
+
+# Check prefixes:
+# RELOC - Check the relocation in the object.
+# FIXUP - Check the fixup on the instruction.
+# INSTR - Check the instruction is handled properly by the ASMPrinter.
+
+.text
+
+qc.e.j foo
+# RELOC: R_RISCV_CUSTOM195 foo 0x0
+# INSTR: qc.e.j foo
+# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt
+
+qc.e.j foo at plt
+# RELOC: R_RISCV_CUSTOM195 foo 0x0
+# INSTR: qc.e.j foo
+# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt
+
+qc.e.jal foo at plt
+# RELOC: R_RISCV_CUSTOM195 foo 0x0
+# INSTR: qc.e.jal foo
+# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt
+
+qc.e.jal foo
+# RELOC: R_RISCV_CUSTOM195 foo 0x0
+# INSTR: qc.e.jal foo
+# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_jump_plt
+
+# Check that a label in a different section is handled similar to an undefined symbol
+qc.e.j .bar
+# RELOC: R_RISCV_CUSTOM195 .bar 0x0
+# INSTR: qc.e.j .bar
+# FIXUP: fixup A - offset: 0, value: .bar, kind: fixup_riscv_qc_e_jump_plt
+
+qc.e.jal .bar
+# RELOC: R_RISCV_CUSTOM195 .bar 0x0
+# INSTR: qc.e.jal .bar
+# FIXUP: fixup A - offset: 0, value: .bar, kind: fixup_riscv_qc_e_jump_plt
+
+# Check that jumps to a defined symbol are handled correctly
+qc.e.j .L1
+# INSTR:qc.e.j .L1
+# FIXUP: fixup A - offset: 0, value: .L1, kind: fixup_riscv_qc_e_jump_plt
+
+qc.e.jal .L1
+# INSTR:qc.e.jal .L1
+# FIXUP: fixup A - offset: 0, value: .L1, kind: fixup_riscv_qc_e_jump_plt
+
+.L1:
+  ret
+
+.section .t2
+
+.bar:
+  ret

>From 6a52127bd5c1bcc078d60257271af8830cd4453d Mon Sep 17 00:00:00 2001
From: Sudharsan Veeravalli <quic_svs at quicinc.com>
Date: Thu, 10 Apr 2025 08:51:57 +0530
Subject: [PATCH 2/2] Add error and correct EndLoc

---
 llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp | 8 ++++++--
 llvm/test/MC/RISCV/xqcilb-invalid.s                | 3 +++
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 2af8282ac6361..252111313bbc0 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -2154,19 +2154,23 @@ ParseStatus RISCVAsmParser::parsePseudoQCJumpSymbol(OperandVector &Operands) {
 
   if (getLexer().getKind() != AsmToken::Identifier)
     return ParseStatus::NoMatch;
+
   std::string Identifier(getTok().getIdentifier());
+  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
 
   if (getLexer().peekTok().is(AsmToken::At)) {
     Lex();
     Lex();
+    SMLoc PLTLoc = getLoc();
     StringRef PLT;
     if (getParser().parseIdentifier(PLT) || PLT != "plt")
-      return ParseStatus::Failure;
+      return Error(PLTLoc,
+                   "'@plt' is the only valid operand for this instruction");
+    E = SMLoc::getFromPointer(S.getPointer() + /*@plt*/ 4);
   } else {
     Lex();
   }
 
-  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
   RISCVMCExpr::Specifier Kind = RISCVMCExpr::VK_QC_E_JUMP_PLT;
 
   MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier);
diff --git a/llvm/test/MC/RISCV/xqcilb-invalid.s b/llvm/test/MC/RISCV/xqcilb-invalid.s
index 1a9009b26b691..10d456c8ac0aa 100644
--- a/llvm/test/MC/RISCV/xqcilb-invalid.s
+++ b/llvm/test/MC/RISCV/xqcilb-invalid.s
@@ -22,3 +22,6 @@ qc.e.jal 2147483649
 
 # CHECK-MINUS: :[[@LINE+1]]:1: error: instruction requires the following: 'Xqcilb' (Qualcomm uC Long Branch Extension)
 qc.e.jal 2147483640
+
+# CHECK: :[[@LINE+1]]:12: error: '@plt' is the only valid operand for this instruction
+qc.e.j foo at rlt



More information about the llvm-commits mailing list