Skip to content

[RISCV] Vendor Relocations for Xqci extension #135400

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jun 4, 2025
Merged

Conversation

svs-quic
Copy link
Contributor

@svs-quic svs-quic commented Apr 11, 2025

This patch implements vendor relocation support for RISC-V, starting with the Xqci extensions.

Vendor Relocations need to emit the vendor symbol, and add an R_RISCV_VENDOR relocation against that symbol before the specific vendor relocation itself.

This patch adds a maybeAddVendorReloc function which is called from addReloc, to implement this functionality. Vendor identifier symbols are cached, so that at most one is emitted per vendor identifier.

Vendor identifiers symbols do not interfere with identically named symbols used by assembly.

@llvmbot llvmbot added backend:RISC-V mc Machine (object) code labels Apr 11, 2025
@svs-quic svs-quic requested review from topperc, pgodeq and hchandel April 11, 2025 16:40
@llvmbot
Copy link
Member

llvmbot commented Apr 11, 2025

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

@llvm/pr-subscribers-mc

Author: Sudharsan Veeravalli (svs-quic)

Changes

This patch adds support for vendor relocations on Qualcomm uC Xqci instructions.

It adds a new fixupNeedsMarkerELFRelocation function that targets can override to add any marker relocations in the ELF object.

The psabi doc describes the vendor relocations here https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#nonstandard-relocations-aka-vendor-specific-relocations

> These vendor-specific relocations must be preceded by a R_RISCV_VENDOR relocation against a vendor identifier symbol. The preceding R_RISCV_VENDOR relocation is used by the linker to choose the correct implementation for the associated nonstandard relocation.
>
> The vendor identifier symbol should be a defined symbol and should set the type to STT_NOTYPE, binding to STB_LOCAL, and the size of symbol to zero.


Full diff: https://github.com/llvm/llvm-project/pull/135400.diff

7 Files Affected:

  • (modified) llvm/include/llvm/MC/MCAsmBackend.h (+12)
  • (modified) llvm/lib/MC/ELFObjectWriter.cpp (+15)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp (+27)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h (+6)
  • (modified) llvm/test/MC/RISCV/xqcibi-relocations.s (+14)
  • (modified) llvm/test/MC/RISCV/xqcilb-relocations.s (+11)
  • (modified) llvm/test/MC/RISCV/xqcili-relocations.s (+9)
diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h
index 5953de30c2eb2..fcc67afdc6fc5 100644
--- a/llvm/include/llvm/MC/MCAsmBackend.h
+++ b/llvm/include/llvm/MC/MCAsmBackend.h
@@ -125,6 +125,18 @@ class MCAsmBackend {
     return false;
   }
 
+  /// Hook to add any marker relocations that the target may need in addition
+  /// to the normal relocations on instructions. Currently used only by RISCV
+  /// to add vendor relocations.
+  virtual bool fixupNeedsMarkerELFRelocation(MCAssembler &Asm,
+                                             const MCFragment &F,
+                                             const MCFixup &Fixup,
+                                             unsigned &PreRelocType,
+                                             MCSymbol *&PreRelocSymbol,
+                                             uint64_t &PreRelocAddend) const {
+    return false;
+  }
+
   /// Apply the \p Value for given \p Fixup into the provided data fragment, at
   /// the offset specified by the fixup and following the fixup kind as
   /// appropriate. Errors (such as an out of range fixup value) should be
diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp
index 23d5517920e7b..426937ee415bd 100644
--- a/llvm/lib/MC/ELFObjectWriter.cpp
+++ b/llvm/lib/MC/ELFObjectWriter.cpp
@@ -1435,6 +1435,21 @@ void ELFObjectWriter::recordRelocation(MCAssembler &Asm,
                         : C;
   FixedValue = usesRela(TO, FixupSection) ? 0 : Addend;
 
+  // Some RISC-V relocations need a marker relocation that appears before the
+  // relocation in question.
+  unsigned PreRelocType;
+  MCSymbol *PreRelocSymbol;
+  uint64_t PreRelocAddend;
+  if (Backend.fixupNeedsMarkerELFRelocation(Asm, *Fragment, Fixup, PreRelocType,
+                                            PreRelocSymbol, PreRelocAddend)) {
+    auto *SymbolELF = cast<MCSymbolELF>(PreRelocSymbol);
+    if (SymbolELF)
+      SymbolELF->setUsedInReloc();
+    ELFRelocationEntry Rec(FixupOffset, SymbolELF, PreRelocType,
+                           PreRelocAddend);
+    Relocations[&FixupSection].push_back(Rec);
+  }
+
   if (!RelocateWithSymbol) {
     const auto *SectionSymbol =
         SecA ? cast<MCSymbolELF>(SecA->getBeginSymbol()) : nullptr;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index 49c8c6957aa34..bbcb84ea78566 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -702,6 +702,33 @@ bool RISCVAsmBackend::handleAddSubRelocations(const MCAssembler &Asm,
   return true;
 }
 
+bool RISCVAsmBackend::fixupNeedsMarkerELFRelocation(
+    MCAssembler &Asm, const MCFragment &F, const MCFixup &Fixup,
+    unsigned &PreRelocType, MCSymbol *&PreRelocSymbol,
+    uint64_t &PreRelocAddend) const {
+  switch (Fixup.getTargetKind()) {
+  default:
+    return false;
+  case RISCV::fixup_riscv_qc_e_branch:
+  case RISCV::fixup_riscv_qc_e_32:
+  case RISCV::fixup_riscv_qc_abs20_u:
+  case RISCV::fixup_riscv_qc_e_jump_plt: {
+    MCContext &Ctx = Asm.getContext();
+    MCSymbol *VendorSymbol = Ctx.getOrCreateSymbol("QUALCOMM");
+    MCFragment &DF = F.getParent()->getDummyFragment();
+    VendorSymbol->setFragment(&DF);
+    VendorSymbol->setOffset(0);
+
+    Asm.registerSymbol(*VendorSymbol);
+
+    PreRelocType = ELF::R_RISCV_VENDOR;
+    PreRelocSymbol = VendorSymbol;
+    PreRelocAddend = 0;
+    return true;
+  }
+  }
+}
+
 void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
                                  const MCValue &Target,
                                  MutableArrayRef<char> Data, uint64_t Value,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
index f5e8d340d9bce..9076074987a37 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
@@ -56,6 +56,12 @@ class RISCVAsmBackend : public MCAsmBackend {
                                const MCFixup &Fixup, const MCValue &Target,
                                uint64_t &FixedValue) const override;
 
+  bool fixupNeedsMarkerELFRelocation(MCAssembler &Asm, const MCFragment &F,
+                                     const MCFixup &Fixup,
+                                     unsigned &PreRelocType,
+                                     MCSymbol *&PreRelocSymbol,
+                                     uint64_t &PreRelocAddend) const override;
+
   void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
                   const MCValue &Target, MutableArrayRef<char> Data,
                   uint64_t Value, bool IsResolved,
diff --git a/llvm/test/MC/RISCV/xqcibi-relocations.s b/llvm/test/MC/RISCV/xqcibi-relocations.s
index 7028e8a737c86..a0ab7a450349d 100644
--- a/llvm/test/MC/RISCV/xqcibi-relocations.s
+++ b/llvm/test/MC/RISCV/xqcibi-relocations.s
@@ -2,11 +2,13 @@
 # RUN:     | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s
 # RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcibi %s -o %t.o
 # RUN: llvm-readobj -r %t.o | FileCheck -check-prefix=RELOC %s
+# RUN: llvm-readelf -s %t.o | FileCheck -check-prefix=VENDORSYM %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.
+# VENDORSYM - Check the vendor symbol.
 
 .text
 
@@ -22,6 +24,15 @@ qc.e.bgeui x8, 12, foo
 # INSTR: qc.e.bgeui s0, 12, foo
 # FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_branch
 
+# Check that we print the correct relocations in exact mode
+.option exact
+qc.e.bgeui x8, 12, foo
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
+# RELOC: R_RISCV_CUSTOM193 foo 0x0
+# INSTR: qc.e.bgeui s0, 12, foo
+# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_branch
+.option noexact
+
 # Check that a label in a different section is handled similar to an undefined
 # symbol and gets relaxed to (qc.e.bgeui + jal)
 qc.e.bltui x4, 9, .bar
@@ -34,6 +45,9 @@ qc.e.beqi x7, 8, .L1
 # INSTR: qc.e.beqi t2, 8, .L1
 # FIXUP: fixup A - offset: 0, value: .L1, kind: fixup_riscv_qc_e_branch
 
+# Check that there is only one vendor symbol created and that it is local and NOTYPE
+# VENDORSYM-COUNT-1: 00000000     0 NOTYPE  LOCAL  DEFAULT     2 QUALCOMM
+
 .L1:
   ret
 
diff --git a/llvm/test/MC/RISCV/xqcilb-relocations.s b/llvm/test/MC/RISCV/xqcilb-relocations.s
index a475cde3f6bfd..f9787df352f36 100644
--- a/llvm/test/MC/RISCV/xqcilb-relocations.s
+++ b/llvm/test/MC/RISCV/xqcilb-relocations.s
@@ -2,41 +2,49 @@
 # 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
+# RUN: llvm-readelf -s %t.o | FileCheck -check-prefix=VENDORSYM %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.
+# VENDORSYM - Check the vendor symbol.
 
 .text
 
 qc.e.j foo
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
 # 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@plt
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
 # 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@plt
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
 # 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_VENDOR QUALCOMM 0x0
 # 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_VENDOR QUALCOMM 0x0
 # 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_VENDOR QUALCOMM 0x0
 # 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
@@ -50,6 +58,9 @@ qc.e.jal .L1
 # INSTR:qc.e.jal .L1
 # FIXUP: fixup A - offset: 0, value: .L1, kind: fixup_riscv_qc_e_jump_plt
 
+# Check that there is only one vendor symbol created and that it is local and NOTYPE
+# VENDORSYM-COUNT-1: 00000000     0 NOTYPE  LOCAL  DEFAULT     2 QUALCOMM
+
 .L1:
   ret
 
diff --git a/llvm/test/MC/RISCV/xqcili-relocations.s b/llvm/test/MC/RISCV/xqcili-relocations.s
index b0a3f3bae11d5..b7ed8fedb35f8 100644
--- a/llvm/test/MC/RISCV/xqcili-relocations.s
+++ b/llvm/test/MC/RISCV/xqcili-relocations.s
@@ -2,31 +2,37 @@
 # RUN:     | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s
 # RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcili %s -o %t.o
 # RUN: llvm-readobj -r %t.o | FileCheck -check-prefix=RELOC %s
+# RUN: llvm-readelf -s %t.o | FileCheck -check-prefix=VENDORSYM %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.
+# VENDORSYM - Check the vendor symbol.
 
 .text
 
 qc.li x4, %qc.abs20(foo)
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
 # RELOC: R_RISCV_CUSTOM192 foo 0x0
 # INSTR: qc.li tp, %qc.abs20(foo)
 # FIXUP: fixup A - offset: 0, value: %qc.abs20(foo), kind: fixup_riscv_qc_abs20_u
 
 qc.e.li x5, foo
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
 # RELOC: R_RISCV_CUSTOM194 foo 0x0
 # INSTR: qc.e.li t0, foo
 # FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_qc_e_32
 
 # Check that a label in a different section is handled similar to an undefined symbol
 qc.li x9, %qc.abs20(.bar)
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
 # RELOC: R_RISCV_CUSTOM192 .bar 0x0
 # INSTR: qc.li s1, %qc.abs20(.bar)
 # FIXUP: fixup A - offset: 0, value: %qc.abs20(.bar), kind: fixup_riscv_qc_abs20_u
 
 qc.e.li x8, .bar
+# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
 # RELOC: R_RISCV_CUSTOM194 .bar 0x0
 # INSTR: qc.e.li s0, .bar
 # FIXUP: fixup A - offset: 0, value: .bar, kind: fixup_riscv_qc_e_32
@@ -40,6 +46,9 @@ qc.e.li x6, .L1
 # INSTR: qc.e.li t1, .L1
 # FIXUP: fixup A - offset: 0, value: .L1, kind: fixup_riscv_qc_e_32
 
+# Check that there is only one vendor symbol created and that it is local and NOTYPE
+# VENDORSYM-COUNT-1: 00000000     0 NOTYPE  LOCAL  DEFAULT     2 QUALCOMM
+
 .L1:
   ret
 

@svs-quic svs-quic requested a review from lenary April 11, 2025 16:40
@lenary lenary requested a review from MaskRay April 11, 2025 18:30
@lenary
Copy link
Member

lenary commented Apr 11, 2025

@MaskRay I've tagged you in the review because this hits target-independent MC code. We weren't sure of a better way to accomplish what is needed for the RISC-V R_RISCV_VENDOR relocations. We didn't want a separate fixup (as for Relaxation) because we didn't want to emit the R_RISCV_VENDOR relocation if the fixup could be evaluated/applied by the compiler, and that kind of "evaluating one fixup deletes another fixup" seemed complex and error-prone. Hopefully you find our approach reasonable.

There is linker support for these relocations, in https://github.com/qualcomm/eld - which requires the R_RISCV_VENDOR relocations - but it does look like the tests are missing from this repo.

@lenary
Copy link
Member

lenary commented Apr 11, 2025

Tests for the eld implementation are here: qualcomm/eld#16

unsigned PreRelocType;
MCSymbol *PreRelocSymbol;
uint64_t PreRelocAddend;
if (Backend.fixupNeedsMarkerELFRelocation(Asm, *Fragment, Fixup, PreRelocType,
Copy link
Member

Choose a reason for hiding this comment

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

(MCAssembler.cpp and ELFObjectWriter.cpp have significant tech debt. I try to clean up them from time to time. Just noticed that my latest change 19730e3 might need you to rebase.)

I think we should refrain from adding new hooks. (I added Backend.handleAddSubRelocations in the past, and on reflection we might need some cleanup so that it could be combined with getRelocType).

Can you add an extra fixup when fixup_riscv_qc_e_branch is appended? Or the hook is here because in some scenarios the fixup can be resolved and you don't want to add a redundant marker?

Copy link
Member

Choose a reason for hiding this comment

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

I think we should refrain from adding new hooks.

I do too, but "refrain from" doesn't mean "never". We could not find another way to accomplish this.

Or the hook is here because in some scenarios the fixup can be resolved and you don't want to add a redundant marker?

This. For instance, branches can be evaluated when not relaxing.

From my comment, above:

We didn't want a separate fixup (as for Relaxation) because we didn't want to emit the R_RISCV_VENDOR relocation if the fixup could be evaluated/applied by the compiler, and that kind of "evaluating one fixup deletes another fixup" seemed complex and error-prone.

Copy link
Member

Choose a reason for hiding this comment

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

You could customize evaluateTargetFixup. If the fixup is not resolved, call recordRelocation to append the marker relocation, and then rely on the default recordRelocation in evaluateFixup to append the actual relocation.

Copy link
Member

Choose a reason for hiding this comment

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

I looked into this, and evaluateTargetFixup doesn't get any info about the fixup being resolved (the info comes from this function when the IsTarget fixup flag is given) - to work this way we'd need to pass more information into this function (the RecordReloc bool), and replicate lots of the logic in evaluateFixup which is bypassed if you have the IsTarget fixup flag.

With all this in mind, I continue to think that the new hook is the best way to achieve vendor relocations for RISC-V.

I realise you've been away recently (me too), I'm happy to discuss this further when you're back.

Copy link
Member

Choose a reason for hiding this comment

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

@MaskRay Ping?

Copy link
Member

Choose a reason for hiding this comment

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

I have made the change, and commented about the duplicated code, which I don't like having to copy-paste. There is also complex logic working out when IsResolved might not be enough of a signal that a relocation is required, especially as RISC-V forces relocations during relaxation (which is also hard to test).

Copy link
Member

@MaskRay MaskRay May 17, 2025

Choose a reason for hiding this comment

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

Cc @jrtc27 who introduced evaluateTargetFixup

Currently, the MCFixupKindInfo::FKF_IsTarget code path in MCAssembler.cpp skips the common code paths (getAddSym/getSubSym), because PCREL_LO12[IS] don't need them.

Now evaluateTargetFixup is reused by QUIC relocations, leading to some duplication. I think we can make evaluateTargetFixup return more information so that QUIC relocations keep reusing the common code path.

// MCAssembler.cpp
  if (FixupFlags & MCFixupKindInfo::FKF_IsTarget) {
    IsResolved =
        getBackend().evaluateTargetFixup(*this, Fixup, DF, Target, STI, Value);
  } else {
    // QUIC relocations should reuse this code path while PCREL_LO12[IS] skip it.
    const MCSymbol *Add = Target.getAddSym(); 7 refs
    const MCSymbol *Sub = Target.getSubSym();

The latter half of the duplication that adds a R_RISCV_VENDOR relocation can probably be refactored to reuse Type = TargetObjectWriter->getRelocType(Ctx, Target, Fixup, IsPCRel);

Copy link
Collaborator

Choose a reason for hiding this comment

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

evaluteTargetFixup did exactly what was needed at the time and nothing more. If there are cases now where more of the surrounding code needs reusing then coming up with a way to communicate that seems perfectly reasonable to me.

Copy link
Member

Choose a reason for hiding this comment

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

I've refactored the relocation generation further. There is now an addReloc hook and #140494 demonstrates how to add two relocations from one fixup. For VENDOR, we just need to call .recordRelocation for a specific fixup kind.

Copy link
Member

Choose a reason for hiding this comment

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

Now done. Thanks for the cleanup, it is indeed a much easier structure to work with for vendor relocations.

@lenary lenary requested a review from tclin914 April 29, 2025 05:51
@lenary
Copy link
Member

lenary commented Apr 29, 2025

@tclin914 I've tagged you because you'll likely want this functionality for the andes extension with relocations, and it would be good to avoid duplicating this work.

# Check that we print the correct relocations in exact mode
.option exact
qc.e.bgeui x8, 12, foo
# RELOC: R_RISCV_VENDOR QUALCOMM 0x0
Copy link
Member

@MaskRay MaskRay May 7, 2025

Choose a reason for hiding this comment

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

For relocation generation testing we should use llvm-objdump -dr to test r_offset and test that we don't incorrectly emit redundant relocations. See Sparc/Relocations/relocation-specifier.s for a modern example using llvm-objdump -dr

Looks like R_RISCV_VENDOR is only generated with exact. We should test that without exact there is no QUALCOMM. This is unclear from the PR description.

FIXUP from -show-encoding output is not really useful for relocations. Just remove it.

Copy link
Member

Choose a reason for hiding this comment

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

QUALCOMM should be emitted even without exact, we have started to use exact to avoid instruction compression happening and make testing more specific.

I will move to objdump -dr, I agree it's often clearer. I will revisit these tests.

@resistor
Copy link
Collaborator

Just wanted to note that we (CHERI, and specifically CHERIoT) are very interested in seeing R_RISCV_VENDOR support landed. Is this still moving ahead?

@lenary
Copy link
Member

lenary commented May 16, 2025

@resistor I'm going to try to make some more progress on this today.

This patch implements vendor relocation support for RISC-V, starting
with the Xqci extensions.

This is done by re-organising `RISCVAsmBackend::evaluateTargetFixup`, to
also handle emitting the vendor relocation when the fixup is unresolved.

An extra parameter `bool RecordReloc` is added to `evaluateTargetFixup`,
so that the relocation is only emitted when it should be, as
`evaluateTargetFixup` works the same way.

This adds tests showing resolveable and unresolvable symbols for the
xqci fixups (some of which are absolute and some of which are
pc-relative), including tests when relaxation is enabled which forces
relocations that might otherwise not be needed.

Co-authored-by: Sudharsan Veeravalli <quic_svs@quicinc.com>
@lenary lenary changed the title [RISCV] Add support for vendor relocations on Xqci extensions [RISCV] Vendor Relocations for Xqci extension May 16, 2025
Copy link

github-actions bot commented May 16, 2025

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

@lenary lenary removed their request for review May 17, 2025 01:46

.text
## This test checks that we emit the right relocations for Xqcilb
## relative jumps. These can be resolved within the same section
Copy link
Member

Choose a reason for hiding this comment

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

These can be resolved within the same section and the destination symbol is local?

Otherwise I think there will be a relocation as well. (We also emit a relocation when the destination is a gnu ifunc symbol, which is an edge case that we don't necessarily describe).

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I think these always need a relocation. I forgot these are to the PLT, was just focussed on them being pc-relative.

I don't think I found the LLVM code to always emit the relocation (that I expect for call_plt), i will look over it all again.

Copy link
Member

Choose a reason for hiding this comment

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

I added tests to show that for non-local symbols, we still get the relocation, but for local symbols we don't (which matches when call is resolved directly to the symbol in the assembler)

Copy link
Member

Choose a reason for hiding this comment

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

You might want to edit one of the tests to define QUALCOMM: and use llvm-objdump -t to check the symbol table. There will be two QUALCOMM local symbols, which is fine.

Copy link
Member

Choose a reason for hiding this comment

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

I agree a test for this is needed. I'll add it as a separate test, for clarity.

cast<MCSymbolELF>(Ctx.createLocalSymbol(VendorIdentifier));

// Vendor Symbols are Absolute, Local, NOTYPE.
VendorSymbol->setType(ELF::STT_NOTYPE);
Copy link
Member

Choose a reason for hiding this comment

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

The symbol is by default NOTYPE and LOCAL. In the rare scenario that the users changes the type/binding, we should respect that.

So setType/setBinding should be omitted.

Copy link
Member

Choose a reason for hiding this comment

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

Done.

@lenary
Copy link
Member

lenary commented May 28, 2025

Apologies if this is hard to review vs the previous push because the most recent commit is a merge, but the "all commits" view should be small enough to review.

@lenary lenary requested a review from MaskRay May 28, 2025 05:00

// Some Fixups require a vendor relocation, record it (directly) before we add
// the relocation.
if (!IsResolved || shouldForceRelocation(Fixup, Target)) {
Copy link
Member

Choose a reason for hiding this comment

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

If we are going to call shouldForceRelocation, just avoid the generic MCAsmBackend::addReloc and call the underlying recordRelocation directly.

Copy link
Member

Choose a reason for hiding this comment

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

Done

qc.e.j same_section

# ASM: qc.e.jal same_section
# OBJ: qc.e.jal 0x30
Copy link
Member

Choose a reason for hiding this comment

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

If we use OBJ-NEXT:, OBJ-NOT: R_RISCV will not be needed

Copy link
Member

Choose a reason for hiding this comment

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

I thought the obj-not way would be more maintainable for future additions to the tests, but i've changed to the obj-next way.

There are also changes to xqcilb to take in that jump targets are now printed properly.

const MCExpr *VendorExpr = MCSymbolRefExpr::create(VendorSymbol, Ctx);
MCFixup VendorFixup =
MCFixup::create(Fixup.getOffset(), VendorExpr,
FirstLiteralRelocationKind + ELF::R_RISCV_VENDOR);
Copy link
Member

Choose a reason for hiding this comment

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

s/FirstLiteralRelocationKind + ELF::R_RISCV_VENDOR/ELF::R_RISCV_VENDOR/`

Copy link
Member

Choose a reason for hiding this comment

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

Done

@lenary
Copy link
Member

lenary commented May 28, 2025

Failure is unrelated.

@@ -660,7 +698,19 @@ bool RISCVAsmBackend::addReloc(const MCFragment &F, const MCFixup &Fixup,
if (IsResolved &&
(getFixupKindInfo(Fixup.getKind()).Flags & MCFixupKindInfo::FKF_IsPCRel))
IsResolved = isPCRelFixupResolved(Target.getAddSym(), F);
IsResolved = MCAsmBackend::addReloc(F, Fixup, Target, FixedValue, IsResolved);

if (IsResolved && shouldForceRelocation(Fixup, Target))
Copy link
Member

Choose a reason for hiding this comment

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

The default impl shouldForceRelocation just returns false. We should just delete this if and its body

Copy link
Member

Choose a reason for hiding this comment

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

Done

@@ -20,6 +21,7 @@ namespace llvm {
class MCAssembler;
class MCObjectTargetWriter;
class raw_ostream;
class MCSymbolELF;
Copy link
Member

Choose a reason for hiding this comment

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

The header file doesn't use MCSymbolELF

Copy link
Member

Choose a reason for hiding this comment

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

Done

@@ -47,9 +51,15 @@ class RISCVAsmBackend : public MCAsmBackend {
bool evaluateTargetFixup(const MCFixup &Fixup, const MCValue &Target,
uint64_t &Value) override;

std::optional<StringRef>
getVendorIdentifierForFixup(unsigned TargetFixupKind) const;
Copy link
Member

Choose a reason for hiding this comment

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

rename this to FixupKind to be consistent with the impl.

(Beside FK_Data_ fixup kinds are almost always target-specific. "Target" probably does not convey extra meaning. )

Copy link
Member

Choose a reason for hiding this comment

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

Done

@lenary
Copy link
Member

lenary commented May 31, 2025

The linux build failure is unrelated, it's an issue with precompiled headers in Flang. I will file a separate ticket about it if there isn't one.

@svs-quic
Copy link
Contributor Author

svs-quic commented Jun 3, 2025

Thanks for taking this up and working on it, Sam! The changes look good to me.

@@ -38,7 +39,7 @@ static cl::opt<bool> ULEB128Reloc(
RISCVAsmBackend::RISCVAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI,
bool Is64Bit, const MCTargetOptions &Options)
: MCAsmBackend(llvm::endianness::little), STI(STI), OSABI(OSABI),
Is64Bit(Is64Bit), TargetOptions(Options) {
Is64Bit(Is64Bit), TargetOptions(Options), VendorSymbols() {
Copy link
Member

Choose a reason for hiding this comment

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

delete , VendorSymbols(). just rely the implicit default initialization

Copy link
Member

Choose a reason for hiding this comment

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

Done

if (!IsResolved) {
// Some Fixups require a vendor relocation, record it (directly) before we
// add the relocation.
if (std::optional<StringRef> VendorIdentifier =
Copy link
Member

Choose a reason for hiding this comment

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

Combine the two functions and just call sth like maybeAddVendorReloc(F, Fixup)

Copy link
Member

Choose a reason for hiding this comment

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

Done

const MCExpr *VendorExpr = MCSymbolRefExpr::create(VendorSymbol, Ctx);
MCFixup VendorFixup =
MCFixup::create(Fixup.getOffset(), VendorExpr, ELF::R_RISCV_VENDOR);
// Explicitly create MCValue so that the absolute symbol is not evaluated to
Copy link
Member

Choose a reason for hiding this comment

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

This comment is not correct. recordRelocation always append a new relocation, unrelated to how you create a MCValue.

Copy link
Member

Choose a reason for hiding this comment

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

The point in this comment is to say why we didn't use VendorExpr->evaluateAsRelocatable to create VendorTarget - because it would return an MCValue of Constant Zero. I have clarified the comment in this direction.

StringRef VendorIdentifier) {
MCContext &Ctx = Asm->getContext();

auto It = VendorSymbols.find(VendorIdentifier);
Copy link
Member

Choose a reason for hiding this comment

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

find/try_emplace performs the hash table lookup twice. Just call try_emplace and use the second element of the return value.

Copy link
Member

Choose a reason for hiding this comment

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

Add a comment:

Create a local symbol for the vendor relocation to reference. It's fine if the symbol has the same name as an existing symbol.

Copy link
Member

Choose a reason for hiding this comment

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

Done


.text
## This test checks that we emit the right relocations for Xqcilb
## relative jumps. These can be resolved within the same section
Copy link
Member

Choose a reason for hiding this comment

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

You might want to edit one of the tests to define QUALCOMM: and use llvm-objdump -t to check the symbol table. There will be two QUALCOMM local symbols, which is fine.

# OBJ: qc.e.j 0x0 <this_section>
# OBJ-NEXT: R_RISCV_VENDOR QUALCOMM{{$}}
# OBJ-NEXT: R_RISCV_CUSTOM195 undef{{$}}
qc.e.j undef
Copy link
Member

Choose a reason for hiding this comment

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

In the absence of .option exact, qc.e.j undef will be converted to a shorter-range jal, is that intended?

Copy link
Member

Choose a reason for hiding this comment

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

No, I'll upload a separate patch to fix this.

Copy link
Member

Choose a reason for hiding this comment

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

Addressed in #142702

@MaskRay
Copy link
Member

MaskRay commented Jun 3, 2025

Can you edit the description to describe some properties? Like we create a local symbol and a R_RISCV_VENDOR relocation before another one

void RISCVAsmBackend::maybeAddVendorReloc(const MCFragment &F,
const MCFixup &Fixup) {
MCContext &Ctx = Asm->getContext();

Copy link
Member

Choose a reason for hiding this comment

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

I'd delete the first two blank lines.

Copy link
Member

Choose a reason for hiding this comment

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

Done, with a little reorder to not grab the context until as late as possible


const MCExpr *VendorExpr = MCSymbolRefExpr::create(VendorSymbol, Ctx);
MCFixup VendorFixup =
MCFixup::create(Fixup.getOffset(), VendorExpr, ELF::R_RISCV_VENDOR);
Copy link
Member

Choose a reason for hiding this comment

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

MCFixup::create(Fixup.getOffset(), nullptr, ELF::R_RISCV_VENDOR) Then delete MCSymbolRefExpr::create . MCFixup::Value isn't used

Copy link
Member

Choose a reason for hiding this comment

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

Done

Asm->registerSymbol(*VendorSymbol);
} else {
// Fetch the existing symbol
VendorSymbol = It->getValue();
Copy link
Member

Choose a reason for hiding this comment

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

the objdump -t CHECK lines need some -NOT pattern to check that we don't generate redundat local symbols.

Copy link
Member

Choose a reason for hiding this comment

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

Done

Copy link
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

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

This has been a long time in review, so I'm probably going to merge it fairly early tomorrow (pacific time) if there are no more comments.

Asm->registerSymbol(*VendorSymbol);
} else {
// Fetch the existing symbol
VendorSymbol = It->getValue();
Copy link
Member

Choose a reason for hiding this comment

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

Done


const MCExpr *VendorExpr = MCSymbolRefExpr::create(VendorSymbol, Ctx);
MCFixup VendorFixup =
MCFixup::create(Fixup.getOffset(), VendorExpr, ELF::R_RISCV_VENDOR);
Copy link
Member

Choose a reason for hiding this comment

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

Done

void RISCVAsmBackend::maybeAddVendorReloc(const MCFragment &F,
const MCFixup &Fixup) {
MCContext &Ctx = Asm->getContext();

Copy link
Member

Choose a reason for hiding this comment

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

Done, with a little reorder to not grab the context until as late as possible

@lenary lenary merged commit c83c01f into llvm:main Jun 4, 2025
11 checks passed
@svs-quic svs-quic deleted the vendorsym branch June 6, 2025 05:18
rorth pushed a commit to rorth/llvm-project that referenced this pull request Jun 11, 2025
This patch implements vendor relocation support for RISC-V, starting
with the Xqci extensions.

Vendor Relocations need to emit the vendor symbol, and add an
`R_RISCV_VENDOR` relocation against that symbol before the specific
vendor relocation itself.

This patch adds a `maybeAddVendorReloc` function which is called from
`addReloc`, to implement this functionality. Vendor identifier symbols
are cached, so that at most one is emitted per vendor identifier.

Vendor identifiers symbols do not interfere with identically named
symbols used by assembly.

Co-authored-by: Sam Elliott <quic_aelliott@quicinc.com>
DhruvSrivastavaX pushed a commit to DhruvSrivastavaX/lldb-for-aix that referenced this pull request Jun 12, 2025
This patch implements vendor relocation support for RISC-V, starting
with the Xqci extensions.

Vendor Relocations need to emit the vendor symbol, and add an
`R_RISCV_VENDOR` relocation against that symbol before the specific
vendor relocation itself.

This patch adds a `maybeAddVendorReloc` function which is called from
`addReloc`, to implement this functionality. Vendor identifier symbols
are cached, so that at most one is emitted per vendor identifier.

Vendor identifiers symbols do not interfere with identically named
symbols used by assembly.

Co-authored-by: Sam Elliott <quic_aelliott@quicinc.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:RISC-V mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants