[lld] [llvm] [RISCV][MC] Reference ISA mapping symbols in R_RISCV_RELAX relocations (PR #201308)

Kito Cheng via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 01:33:50 PDT 2026


https://github.com/kito-cheng updated https://github.com/llvm/llvm-project/pull/201308

>From 077f1be0b8da0d3df5f5dc19917a46e470512bac Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Mon, 20 Apr 2026 01:52:36 -0700
Subject: [PATCH 1/3] [RISCV][MC] Reference ISA mapping symbols in
 R_RISCV_RELAX relocations

Build on the ISA mapping symbols emitted by the previous patch to let
the linker determine per-region ISA capabilities during relaxation.

Before any ISA-changing directive R_RISCV_RELAX continues to use a null
symbol, preserving backward compatibility with existing object files.
Deduplication (introduced in the previous patch) ensures that redundant
option directives do not create extra symbols or change RELAX targets.

[1] https://github.com/riscv/riscv-elf-psabi-doc/pull/393

Assisted-by: Opus 4.6
---
 lld/test/ELF/riscv-relax-align.s              |   2 +-
 lld/test/ELF/riscv-relax-emit-relocs.s        |  12 +-
 lld/test/ELF/riscv-relocatable-align.s        |   6 +-
 lld/test/ELF/riscv-tlsdesc-relax.s            |   2 +-
 .../RISCV/MCTargetDesc/RISCVAsmBackend.cpp    |   9 +-
 .../RISCV/MCTargetDesc/RISCVAsmBackend.h      |   4 +
 .../RISCV/MCTargetDesc/RISCVELFStreamer.cpp   |  34 +++++-
 .../RISCV/MCTargetDesc/RISCVELFStreamer.h     |  13 ++-
 .../CodeGen/RISCV/option-relax-relocation.ll  |   4 +-
 .../CodeGen/RISCV/relax-per-target-feature.ll |   2 +-
 .../MC/RISCV/Relocations/align-after-relax.s  |   2 +-
 .../RISCV/Relocations/align-non-executable.s  |   2 +-
 llvm/test/MC/RISCV/Relocations/align-norvc.s  |   4 +-
 llvm/test/MC/RISCV/Relocations/leb128.s       |   4 +-
 llvm/test/MC/RISCV/align.s                    |   4 +-
 llvm/test/MC/RISCV/linker-relaxation.s        |  44 ++++----
 .../MC/RISCV/option-arch-relax-mapping-sym.s  |  51 +++++++++
 llvm/test/MC/RISCV/option-exact.s             |   4 +-
 llvm/test/MC/RISCV/option-pushpop.s           |   8 +-
 llvm/test/MC/RISCV/option-relax.s             |   4 +-
 .../MC/RISCV/option-rvc-relax-mapping-sym.s   | 106 ++++++++++++++++++
 llvm/test/MC/RISCV/pcrel-fixups.s             |  22 ++--
 llvm/test/MC/RISCV/xqcibi-linker-relaxation.s |   8 +-
 llvm/test/MC/RISCV/xqcibi-relocations.s       |   4 +-
 llvm/test/MC/RISCV/xqcili-linker-relaxation.s |   4 +-
 25 files changed, 281 insertions(+), 78 deletions(-)
 create mode 100644 llvm/test/MC/RISCV/option-arch-relax-mapping-sym.s
 create mode 100644 llvm/test/MC/RISCV/option-rvc-relax-mapping-sym.s

diff --git a/lld/test/ELF/riscv-relax-align.s b/lld/test/ELF/riscv-relax-align.s
index b3db2c943d85d..d2dd6a3d63cfa 100644
--- a/lld/test/ELF/riscv-relax-align.s
+++ b/lld/test/ELF/riscv-relax-align.s
@@ -84,7 +84,7 @@
 # CHECKR:       <_start>:
 # CHECKR-NEXT:          lui     a0, 0x0
 # CHECKR-NEXT:          0000000000000000:  R_RISCV_HI20         _start
-# CHECKR-NEXT:          0000000000000000:  R_RISCV_RELAX        *ABS*
+# CHECKR-NEXT:          0000000000000000:  R_RISCV_RELAX        {{\$xrv.*}}
 # CHECKR-EMPTY:
 # CHECKR-NEXT:  <a>:
 # CHECKR-NEXT:          addi    a0, a0, 0x2
diff --git a/lld/test/ELF/riscv-relax-emit-relocs.s b/lld/test/ELF/riscv-relax-emit-relocs.s
index 358a288777e93..95e3cf17ead36 100644
--- a/lld/test/ELF/riscv-relax-emit-relocs.s
+++ b/lld/test/ELF/riscv-relax-emit-relocs.s
@@ -22,10 +22,10 @@
 # CHECK:      <_start>:
 # CHECK-NEXT:     jal ra, 0x10008 <f>
 # CHECK-NEXT:         R_RISCV_JAL f
-# CHECK-NEXT:         R_RISCV_RELAX *ABS*
+# CHECK-NEXT:         R_RISCV_RELAX {{\$xrv.*}}
 # CHECK-NEXT:     jal ra, 0x10008 <f>
 # CHECK-NEXT:         R_RISCV_JAL f
-# CHECK-NEXT:         R_RISCV_RELAX *ABS*
+# CHECK-NEXT:         R_RISCV_RELAX {{\$xrv.*}}
 # CHECK-EMPTY:
 # CHECK-NEXT: <f>:
 # CHECK-NEXT:     jalr zero, 0x0(ra)
@@ -34,11 +34,11 @@
 # CHECKR:      <_start>:
 # CHECKR-NEXT:     auipc ra, 0x0
 # CHECKR-NEXT:         R_RISCV_CALL_PLT f
-# CHECKR-NEXT:         R_RISCV_RELAX *ABS*
+# CHECKR-NEXT:         R_RISCV_RELAX {{\$xrv.*}}
 # CHECKR-NEXT:     jalr ra, 0x0(ra)
 # CHECKR-NEXT:     auipc ra, 0x0
 # CHECKR-NEXT:         R_RISCV_CALL_PLT f
-# CHECKR-NEXT:         R_RISCV_RELAX *ABS*
+# CHECKR-NEXT:         R_RISCV_RELAX {{\$xrv.*}}
 # CHECKR-NEXT:     jalr ra, 0x0(ra)
 # CHECKR-NEXT:     addi zero, zero, 0x0
 # CHECKR-NEXT:         R_RISCV_ALIGN *ABS*+0x4
@@ -49,11 +49,11 @@
 # CHECKNORELAX:      <_start>:
 # CHECKNORELAX-NEXT:     auipc ra, 0x0
 # CHECKNORELAX-NEXT:         R_RISCV_CALL_PLT f
-# CHECKNORELAX-NEXT:         R_RISCV_RELAX *ABS*
+# CHECKNORELAX-NEXT:         R_RISCV_RELAX {{\$xrv.*}}
 # CHECKNORELAX-NEXT:     jalr ra, 0x10(ra)
 # CHECKNORELAX-NEXT:     auipc ra, 0x0
 # CHECKNORELAX-NEXT:         R_RISCV_CALL_PLT f
-# CHECKNORELAX-NEXT:         R_RISCV_RELAX *ABS*
+# CHECKNORELAX-NEXT:         R_RISCV_RELAX {{\$xrv.*}}
 # CHECKNORELAX-NEXT:     jalr ra, 0x8(ra)
 # CHECKNORELAX-EMPTY:
 # CHECKNORELAX-NEXT: <f>:
diff --git a/lld/test/ELF/riscv-relocatable-align.s b/lld/test/ELF/riscv-relocatable-align.s
index 24b5b108a4790..1f9d123d6b93f 100644
--- a/lld/test/ELF/riscv-relocatable-align.s
+++ b/lld/test/ELF/riscv-relocatable-align.s
@@ -33,7 +33,7 @@
 # CHECK-NEXT: <_start>:
 # CHECK-NEXT:   c: 00000097             auipc   ra, 0x0
 # CHECK-NEXT:           000000000000000c:  R_RISCV_CALL_PLT     foo
-# CHECK-NEXT:           000000000000000c:  R_RISCV_RELAX        *ABS*
+# CHECK-NEXT:           000000000000000c:  R_RISCV_RELAX        {{\$xrv.*}}
 # CHECK-NEXT:  10: 000080e7             jalr    ra, 0x0(ra) <_start>
 # CHECK-NEXT:  14: 0001                 c.nop
 # CHECK-NEXT:           0000000000000014:  R_RISCV_ALIGN        *ABS*+0x6
@@ -54,7 +54,7 @@
 # CHECK-NEXT: <c0>:
 # CHECK-NEXT:  2c: 00000097             auipc   ra, 0x0
 # CHECK-NEXT:           000000000000002c:  R_RISCV_CALL_PLT     foo
-# CHECK-NEXT:           000000000000002c:  R_RISCV_RELAX        *ABS*
+# CHECK-NEXT:           000000000000002c:  R_RISCV_RELAX        {{\$xrv.*}}
 # CHECK-NEXT:  30: 000080e7             jalr    ra, 0x0(ra) <c0>
 # CHECK-NEXT:  34: 0001                 c.nop
 # CHECK-NEXT:           0000000000000034:  R_RISCV_ALIGN        *ABS*+0x2
@@ -83,7 +83,7 @@
 # CHECK2:      <_start>:
 # CHECK2-NEXT:   0: 00000097             auipc   ra, 0x0
 # CHECK2-NEXT:           0000000000000000:  R_RISCV_CALL_PLT     foo
-# CHECK2-NEXT:           0000000000000000:  R_RISCV_RELAX        *ABS*
+# CHECK2-NEXT:           0000000000000000:  R_RISCV_RELAX        {{\$xrv.*}}
 # CHECK2-NEXT:   4: 000080e7             jalr    ra, 0x0(ra) <_start>
 # CHECK2-NEXT:   8: 0001                 c.nop
 # CHECK2-NEXT:           0000000000000008:  R_RISCV_ALIGN        *ABS*+0x6
diff --git a/lld/test/ELF/riscv-tlsdesc-relax.s b/lld/test/ELF/riscv-tlsdesc-relax.s
index 5718d4175be11..77917872a8539 100644
--- a/lld/test/ELF/riscv-tlsdesc-relax.s
+++ b/lld/test/ELF/riscv-tlsdesc-relax.s
@@ -65,7 +65,7 @@
 # LE64-NEXT:         c.add   a0, tp
 # LE64-NEXT:         jal     {{.*}} <foo>
 # LE64-NEXT:                 R_RISCV_JAL foo
-# LE64-NEXT:                 R_RISCV_RELAX *ABS*
+# LE64-NEXT:                 R_RISCV_RELAX {{\$xrv.*}}
 # LE64-LABEL: <.Ltlsdesc_hi1>:
 # LE64-NEXT:         addi    a0, zero, 0x7ff
 # LE64-NEXT:                 R_RISCV_TLSDESC_HI20 b
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
index 4e3ed3f5ae31f..785b9d4c1ea62 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "RISCVAsmBackend.h"
+#include "RISCVELFStreamer.h"
 #include "RISCVFixupKinds.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/MC/MCAsmInfo.h"
@@ -899,10 +900,14 @@ bool RISCVAsmBackend::addReloc(const MCFragment &F, const MCFixup &Fixup,
 
     if (NeedsRelax) {
       // Some Fixups get a RELAX relocation, record it (directly) after we add
-      // the relocation.
+      // the relocation. If there is an ISA mapping symbol associated with this
+      // fragment, reference it so the linker can determine which ISA extensions
+      // are available for relaxation in this code region.
+      MCSymbol *ISASym =
+          ELFStreamer ? ELFStreamer->getFragmentISASym(F) : nullptr;
       MCFixup RelaxFixup =
           MCFixup::create(Fixup.getOffset(), nullptr, ELF::R_RISCV_RELAX);
-      MCValue RelaxTarget = MCValue::get(nullptr);
+      MCValue RelaxTarget = MCValue::get(ISASym);
       uint64_t RelaxValue;
       Asm->getWriter().recordRelocation(F, RelaxFixup, RelaxTarget, RelaxValue);
     }
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
index 4b05284c9537d..d327bedf844d8 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
@@ -30,6 +30,8 @@ class RISCVAsmBackend : public MCAsmBackend {
   // Temporary symbol used to check whether a PC-relative fixup is resolved.
   MCSymbol *PCRelTemp = nullptr;
 
+  class RISCVELFStreamer *ELFStreamer = nullptr;
+
   bool isPCRelFixupResolved(const MCSymbol *SymA, const MCFragment &F);
 
   StringMap<MCSymbol *> VendorSymbols;
@@ -75,6 +77,8 @@ class RISCVAsmBackend : public MCAsmBackend {
                     const MCSubtargetInfo *STI) const override;
 
   const MCTargetOptions &getTargetOptions() const { return TargetOptions; }
+
+  void setELFStreamer(RISCVELFStreamer *S) { ELFStreamer = S; }
 };
 }
 
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 0bac00910b1fa..d368988534471 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -21,6 +21,7 @@
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCELFObjectWriter.h"
 #include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/RISCVAttributes.h"
 
 using namespace llvm;
 
@@ -47,6 +48,10 @@ RISCVTargetELFStreamer::RISCVTargetELFStreamer(MCStreamer &S,
     ArchString = InitialArchString;
     getStreamer().setMappingSymbolArch(ArchString);
   }
+
+  // Give the backend a pointer to this ELF streamer so it can look up the
+  // per-fragment ISA mapping symbol when recording R_RISCV_RELAX relocations.
+  MAB.setELFStreamer(&getStreamer());
 }
 
 RISCVELFStreamer::RISCVELFStreamer(MCContext &C,
@@ -91,6 +96,10 @@ void RISCVTargetELFStreamer::emitAttribute(unsigned Attribute, unsigned Value) {
 void RISCVTargetELFStreamer::emitTextAttribute(unsigned Attribute,
                                                StringRef String) {
   getStreamer().setAttributeItem(Attribute, String, /*OverwriteExisting=*/true);
+
+  // Track arch for emitting R_RISCV_RELAX with correct ISA string.
+  if (Attribute == RISCVAttrs::ARCH)
+    setArchString(String);
 }
 
 void RISCVTargetELFStreamer::emitIntTextAttribute(unsigned Attribute,
@@ -174,6 +183,9 @@ void RISCVELFStreamer::reset() {
   MappingSymbolArch.clear();
   LastEmittedArch.clear();
   LastEmittedArchInSection.clear();
+  CurrentISARelaxSym = nullptr;
+  FragmentISASym.clear();
+  LastISARelaxSymInSection.clear();
   // Call target streamer reset last: it may call setMappingSymbolArch to
   // re-seed the initial ISA after our state has been cleared.
   static_cast<RISCVTargetStreamer *>(getTargetStreamer())->reset();
@@ -197,21 +209,24 @@ void RISCVELFStreamer::emitInstructionsMappingSymbol() {
   bool NeedSymbol =
       LastEMS != EMS_Instructions || LastEmittedArch != MappingSymbolArch;
   if (NeedSymbol) {
-    if (MappingSymbolArch.empty())
+    if (MappingSymbolArch.empty()) {
       emitMappingSymbol("$x");
-    else
-      emitMappingSymbol("$x" + MappingSymbolArch);
+      CurrentISARelaxSym = nullptr;
+    } else {
+      CurrentISARelaxSym = emitMappingSymbol("$x" + MappingSymbolArch);
+    }
     LastEmittedArch = MappingSymbolArch;
   }
   LastEMS = EMS_Instructions;
 }
 
-void RISCVELFStreamer::emitMappingSymbol(StringRef Name) {
+MCSymbol *RISCVELFStreamer::emitMappingSymbol(StringRef Name) {
   auto *Symbol =
       static_cast<MCSymbolELF *>(getContext().createLocalSymbol(Name));
   emitLabel(Symbol);
   Symbol->setType(ELF::STT_NOTYPE);
   Symbol->setBinding(ELF::STB_LOCAL);
+  return Symbol;
 }
 
 void RISCVELFStreamer::setMappingSymbolArch(StringRef Arch) {
@@ -227,9 +242,11 @@ void RISCVELFStreamer::changeSection(MCSection *Section, uint32_t Subsection) {
   const MCSection *Prev = getPreviousSection().first;
   LastMappingSymbols[Prev] = LastEMS;
   LastEmittedArchInSection[Prev] = LastEmittedArch;
+  LastISARelaxSymInSection[Prev] = CurrentISARelaxSym;
   LastEMS = LastMappingSymbols.lookup(Section);
   auto It = LastEmittedArchInSection.find(Section);
   LastEmittedArch = It != LastEmittedArchInSection.end() ? It->second : "";
+  CurrentISARelaxSym = LastISARelaxSymInSection.lookup(Section);
 
   MCELFStreamer::changeSection(Section, Subsection);
 }
@@ -237,7 +254,16 @@ void RISCVELFStreamer::changeSection(MCSection *Section, uint32_t Subsection) {
 void RISCVELFStreamer::emitInstruction(const MCInst &Inst,
                                        const MCSubtargetInfo &STI) {
   emitInstructionsMappingSymbol();
+
+  MCFragment *PreFrag = getCurrentFragment();
   MCELFStreamer::emitInstruction(Inst, STI);
+  MCFragment *PostFrag = getCurrentFragment();
+
+  if (CurrentISARelaxSym) {
+    FragmentISASym[PreFrag] = CurrentISARelaxSym;
+    if (PostFrag != PreFrag)
+      FragmentISASym[PostFrag] = CurrentISARelaxSym;
+  }
 }
 
 void RISCVELFStreamer::emitBytes(StringRef Data) {
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
index ac738307922d7..6f7e68d2f8496 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
@@ -10,6 +10,7 @@
 #define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVELFSTREAMER_H
 
 #include "RISCVTargetStreamer.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/MC/MCELFStreamer.h"
 
@@ -19,7 +20,7 @@ class RISCVELFStreamer : public MCELFStreamer {
   void reset() override;
   void emitDataMappingSymbol();
   void emitInstructionsMappingSymbol();
-  void emitMappingSymbol(StringRef Name);
+  MCSymbol *emitMappingSymbol(StringRef Name);
 
   enum ElfMappingSymbol { EMS_None, EMS_Instructions, EMS_Data };
 
@@ -38,6 +39,10 @@ class RISCVELFStreamer : public MCELFStreamer {
   std::string LastEmittedArch;
   DenseMap<const MCSection *, std::string> LastEmittedArchInSection;
 
+  MCSymbol *CurrentISARelaxSym = nullptr;
+  DenseMap<const MCFragment *, MCSymbol *> FragmentISASym;
+  DenseMap<const MCSection *, MCSymbol *> LastISARelaxSymInSection;
+
 public:
   RISCVELFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> MAB,
                    std::unique_ptr<MCObjectWriter> MOW,
@@ -50,6 +55,12 @@ class RISCVELFStreamer : public MCELFStreamer {
   void emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) override;
 
   void setMappingSymbolArch(StringRef Arch);
+
+  // Returns the ISA mapping symbol for the given fragment, or nullptr if the
+  // fragment precedes any ISA-changing directive.
+  MCSymbol *getFragmentISASym(const MCFragment &F) const {
+    return FragmentISASym.lookup(&F);
+  }
 };
 
 class RISCVTargetELFStreamer : public RISCVTargetStreamer {
diff --git a/llvm/test/CodeGen/RISCV/option-relax-relocation.ll b/llvm/test/CodeGen/RISCV/option-relax-relocation.ll
index 62178e11f8713..a2bda8aff995b 100644
--- a/llvm/test/CodeGen/RISCV/option-relax-relocation.ll
+++ b/llvm/test/CodeGen/RISCV/option-relax-relocation.ll
@@ -11,14 +11,14 @@
 ; RELAX-NOT:            R_RISCV_RELAX
 ; CHECK-NEXT:   auipc   ra, 0x0
 ; CHECK-NEXT:           R_RISCV_CALL_PLT     f
-; RELAX-NEXT:           R_RISCV_RELAX        *ABS*
+; RELAX-NEXT:           R_RISCV_RELAX        $xrv64i2p1
 ; CHECK-NEXT:   jalr    ra
 ; CHECK-NEXT:   j       {{.*}}
 ; RELAX-NEXT:           R_RISCV_JAL  {{.*}}
 ; RELAX-NOT:            R_RISCV_RELAX
 ; CHECK-NEXT:   j       {{.*}}
 ; RELAX-NEXT:           R_RISCV_JAL  {{.*}}
-; RELAX-NEXT:           R_RISCV_RELAX        *ABS*
+; RELAX-NEXT:           R_RISCV_RELAX        $xrv64i2p1
 ; NORELAX-NEXT: li      a0, 0x0
 ; RELAX-EMPTY:
 
diff --git a/llvm/test/CodeGen/RISCV/relax-per-target-feature.ll b/llvm/test/CodeGen/RISCV/relax-per-target-feature.ll
index 0706b0eae97b8..c4e27c367e5a5 100644
--- a/llvm/test/CodeGen/RISCV/relax-per-target-feature.ll
+++ b/llvm/test/CodeGen/RISCV/relax-per-target-feature.ll
@@ -11,7 +11,7 @@ declare dso_local i32 @ext(i32)
 ; CHECK-NEXT:    c.li a0, 0x1f
 ; CHECK-NEXT:    auipc t1, 0x0
 ; CHECK-NEXT:    R_RISCV_CALL_PLT     ext
-; CHECK-NEXT:    R_RISCV_RELAX *ABS*
+; CHECK-NEXT:    R_RISCV_RELAX $xrv64i2p1
 ; CHECK-NEXT:    jalr zero, 0x0(t1)
 define dso_local i32 @f() #0 {
 entry:
diff --git a/llvm/test/MC/RISCV/Relocations/align-after-relax.s b/llvm/test/MC/RISCV/Relocations/align-after-relax.s
index 95bef513dff97..abb3cee33cd96 100644
--- a/llvm/test/MC/RISCV/Relocations/align-after-relax.s
+++ b/llvm/test/MC/RISCV/Relocations/align-after-relax.s
@@ -10,7 +10,7 @@
 # CHECK-NEXT:       c: 00 00 00 00 .word 0x00000000
 # CHECK:           10: auipc   ra, 0x0
 # CHECK-NEXT:                R_RISCV_CALL_PLT     foo
-# CHECK-NEXT:                R_RISCV_RELAX        *ABS*
+# CHECK-NEXT:                R_RISCV_RELAX        {{.*}}
 # CHECK:           18: c.nop
 # CHECK-NEXT:                R_RISCV_ALIGN        *ABS*+0x6
 
diff --git a/llvm/test/MC/RISCV/Relocations/align-non-executable.s b/llvm/test/MC/RISCV/Relocations/align-non-executable.s
index 299f110b65698..a2c65baecb8f9 100644
--- a/llvm/test/MC/RISCV/Relocations/align-non-executable.s
+++ b/llvm/test/MC/RISCV/Relocations/align-non-executable.s
@@ -18,7 +18,7 @@
 # CHECK:       Relocations [
 # CHECK-NEXT:    Section ({{.*}}) .rela.dummy {
 # CHECK-NEXT:      0x0 R_RISCV_CALL_PLT func 0x0
-# RELAX-NEXT:      0x0 R_RISCV_RELAX - 0x0
+# RELAX-NEXT:      0x0 R_RISCV_RELAX {{.*}} 0x0
 # RELAX-NEXT:      0x8 R_RISCV_ADD64 .L2 0x0
 # RELAX-NEXT:      0x8 R_RISCV_SUB64 .L1 0x0
 # CHECK-NEXT:    }
diff --git a/llvm/test/MC/RISCV/Relocations/align-norvc.s b/llvm/test/MC/RISCV/Relocations/align-norvc.s
index c3fe71e65a205..054e07615f9c3 100644
--- a/llvm/test/MC/RISCV/Relocations/align-norvc.s
+++ b/llvm/test/MC/RISCV/Relocations/align-norvc.s
@@ -6,13 +6,13 @@
 # RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax -riscv-align-rvc=0 %s -o %t0
 # RUN: llvm-objdump -dr -M no-aliases %t0 | FileCheck %s --check-prefix=CHECK0
 
-# CHECK:               00000000: R_RISCV_RELAX        *ABS*
+# CHECK:               00000000: R_RISCV_RELAX        {{.*}}
 # CHECK-NEXT:       4: 0001      <unknown>
 # CHECK-NEXT:          00000004: R_RISCV_ALIGN        *ABS*+0x6
 # CHECK-NEXT:       6: 00000013  addi zero, zero, 0x0
 # CHECK-NEXT:       a: 00000537  lui a0, 0x0
 
-# CHECK0:              00000000: R_RISCV_RELAX        *ABS*
+# CHECK0:              00000000: R_RISCV_RELAX        {{.*}}
 # CHECK0-NEXT:      4: 00000013  addi zero, zero, 0x0
 # CHECK0-NEXT:         00000004: R_RISCV_ALIGN        *ABS*+0x4
 # CHECK0-NEXT:      8: 00000537  lui a0, 0x0
diff --git a/llvm/test/MC/RISCV/Relocations/leb128.s b/llvm/test/MC/RISCV/Relocations/leb128.s
index b765e3d56f111..4b805d77ea9d9 100644
--- a/llvm/test/MC/RISCV/Relocations/leb128.s
+++ b/llvm/test/MC/RISCV/Relocations/leb128.s
@@ -23,7 +23,7 @@
 # CHECK0:      Relocations [
 # CHECK0-NEXT:   .rela.alloc_w {
 # CHECK0-NEXT:     0x2 R_RISCV_CALL_PLT foo 0x0
-# RELAX0-NEXT:     0x2 R_RISCV_RELAX - 0x0
+# RELAX0-NEXT:     0x2 R_RISCV_RELAX {{.*}} 0x0
 # CHECK0-NEXT:   }
 # CHECK0-NEXT: ]
 
@@ -34,7 +34,7 @@
 # RELAX-NEXT:     0x1 R_RISCV_SET_ULEB128 w2 0x0
 # RELAX-NEXT:     0x1 R_RISCV_SUB_ULEB128 w1 0x0
 # CHECK-NEXT:     0x2 R_RISCV_CALL_PLT foo 0x0
-# RELAX-NEXT:     0x2 R_RISCV_RELAX - 0x0
+# RELAX-NEXT:     0x2 R_RISCV_RELAX {{.*}} 0x0
 # RELAX-NEXT:     0xA R_RISCV_SET_ULEB128 w2 0x0
 # RELAX-NEXT:     0xA R_RISCV_SUB_ULEB128 w1 0x0
 # RELAX-NEXT:     0xB R_RISCV_SET_ULEB128 w2 0x78
diff --git a/llvm/test/MC/RISCV/align.s b/llvm/test/MC/RISCV/align.s
index 92862d903ce14..a309e3d7ff8b7 100644
--- a/llvm/test/MC/RISCV/align.s
+++ b/llvm/test/MC/RISCV/align.s
@@ -158,7 +158,7 @@ data2:
 # NORELAX-RELOC:    .rela.text3a
 # RELAX-RELOC:      .rela.text3a {
 # RELAX-RELOC-NEXT:    0x0  R_RISCV_CALL_PLT foo 0x0
-# RELAX-RELOC-NEXT:    0x0  R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT:    0x0  R_RISCV_RELAX {{.*}} 0x0
 # RELAX-RELOC-NEXT:    0x8  R_RISCV_BRANCH .Ltmp[[#]] 0x0
 # RELAX-RELOC-NEXT:    0xC  R_RISCV_BRANCH .Ltmp[[#]] 0x0
 # RELAX-RELOC-NEXT:    0x10 R_RISCV_ALIGN - 0x6
@@ -185,7 +185,7 @@ bnez t1, 2b
 # RELAX-RELOC-NEXT:    0xE  R_RISCV_BRANCH .Ltmp[[#]] 0x0
 # RELAX-RELOC-NEXT:    0x12 R_RISCV_BRANCH .Ltmp[[#]] 0x0
 # RELAX-RELOC-NEXT:    0x16 R_RISCV_CALL_PLT foo 0x0
-# RELAX-RELOC-NEXT:    0x16 R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT:    0x16 R_RISCV_RELAX {{.*}} 0x0
 # RELAX-RELOC-NEXT: }
 .section .text3b, "ax"
 bnez t1, 1f
diff --git a/llvm/test/MC/RISCV/linker-relaxation.s b/llvm/test/MC/RISCV/linker-relaxation.s
index c5c4e4877ff2e..7e45925537f02 100644
--- a/llvm/test/MC/RISCV/linker-relaxation.s
+++ b/llvm/test/MC/RISCV/linker-relaxation.s
@@ -15,50 +15,50 @@ call foo
 # NORELAX-RELOC-NEXT: R_RISCV_CALL_PLT foo 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_CALL_PLT foo 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 jal foo
 # NORELAX-RELOC-NEXT: R_RISCV_JAL foo 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_JAL foo 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 lui t1, %hi(foo)
 # NORELAX-RELOC-NEXT: R_RISCV_HI20 foo 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_HI20 foo 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 addi t1, t1, %lo(foo)
 # NORELAX-RELOC-NEXT: R_RISCV_LO12_I foo 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_LO12_I foo 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 sb t1, %lo(foo)(a2)
 # NORELAX-RELOC-NEXT: R_RISCV_LO12_S foo 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_LO12_S foo 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 1:
 auipc t1, %pcrel_hi(foo)
 # NORELAX-RELOC-NEXT: R_RISCV_PCREL_HI20 foo 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_PCREL_HI20 foo 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 addi t1, t1, %pcrel_lo(1b)
 # NORELAX-RELOC-NEXT: R_RISCV_PCREL_LO12_I .Ltmp0 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_PCREL_LO12_I .Ltmp0 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 sb t1, %pcrel_lo(1b)(a2)
 # NORELAX-RELOC-NEXT: R_RISCV_PCREL_LO12_S .Ltmp0 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_PCREL_LO12_S .Ltmp0 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 
 # Check behaviour when a locally defined symbol is referenced.
@@ -71,13 +71,13 @@ call bar
 # NORELAX-RELOC-NOT: R_RISCV_CALL
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_CALL_PLT bar 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 jal bar
 # NORELAX-RELOC-NOT: R_RISCV_JAL
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_JAL bar 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 beq s1, s1, bar
 # NORELAX-RELOC-NOT: R_RISCV_BRANCH
@@ -87,73 +87,73 @@ lui t1, %hi(bar)
 # NORELAX-RELOC-NEXT: R_RISCV_HI20 bar 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_HI20 bar 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 addi t1, t1, %lo(bar)
 # NORELAX-RELOC-NEXT: R_RISCV_LO12_I bar 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_LO12_I bar 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 sb t1, %lo(bar)(a2)
 # NORELAX-RELOC-NEXT: R_RISCV_LO12_S bar 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_LO12_S bar 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 2:
 auipc t1, %pcrel_hi(bar)
 # NORELAX-RELOC-NOT: R_RISCV_PCREL_HI20
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_PCREL_HI20 bar 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 addi t1, t1, %pcrel_lo(2b)
 # NORELAX-RELOC-NOT: R_RISCV_PCREL_LO12_I
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_PCREL_LO12_I .Ltmp1 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 sb t1, %pcrel_lo(2b)(a2)
 # NORELAX-RELOC-NOT: R_RISCV_PCREL_LO12_S
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_PCREL_LO12_S .Ltmp1 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 auipc t1, %got_pcrel_hi(bar)
 # NORELAX-RELOC-NEXT: R_RISCV_GOT_HI20 bar 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_GOT_HI20 bar 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 lui t1, %tprel_hi(baz)
 # NORELAX-RELOC-NEXT: R_RISCV_TPREL_HI20 baz 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_TPREL_HI20 baz 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 auipc t1, %tlsdesc_hi(baz)
 # NORELAX-RELOC-NEXT: R_RISCV_TLSDESC_HI20 baz 0x0
 # NORELAX-RELOC-NOT: R_RISCV_RELAX
 # RELAX-RELOC-NEXT: R_RISCV_TLSDESC_HI20 baz 0x0
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 ## %hi/%lo on an absolute symbol (not yet defined) leads to relocations when relaxation is enabled.
 lui t2, %hi(abs)
 # NORELAX-RELOC-NOT: R_RISCV_
 # RELAX-RELOC-NEXT:      R_RISCV_HI20 - 0x12345
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 addi t2, t2, %lo(abs)
 # NORELAX-RELOC-NOT: R_RISCV_
 # RELAX-RELOC-NEXT:      R_RISCV_LO12_I - 0x12345
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 .set abs, 0x12345
 
 lui t3, %hi(abs)
 # RELAX-RELOC-NEXT:      R_RISCV_HI20 - 0x12345
-# RELAX-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# RELAX-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 
 # Check that a relocation is not emitted for a symbol difference which has
 # been folded to a fixup with an absolute value. This can happen when a
diff --git a/llvm/test/MC/RISCV/option-arch-relax-mapping-sym.s b/llvm/test/MC/RISCV/option-arch-relax-mapping-sym.s
new file mode 100644
index 0000000000000..dc5b4896b420f
--- /dev/null
+++ b/llvm/test/MC/RISCV/option-arch-relax-mapping-sym.s
@@ -0,0 +1,51 @@
+# Test that .option arch emits ISA mapping symbols referenced by R_RISCV_RELAX,
+# enabling the linker to determine per-region relaxation capabilities.
+#
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+relax %s -o %t
+# RUN: llvm-readobj -r --symbols %t | FileCheck %s
+
+call no_arch_yet
+
+# CHECK: R_RISCV_CALL_PLT no_arch_yet
+# CHECK-NEXT: R_RISCV_RELAX $xrv64i2p1 0x0
+
+.option arch, rv64imafdc
+call with_c
+
+# CHECK: R_RISCV_CALL_PLT with_c
+# CHECK-NEXT: R_RISCV_RELAX $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0 0x0
+
+.option arch, rv64imafd
+call without_c
+
+# CHECK: R_RISCV_CALL_PLT without_c
+# CHECK-NEXT: R_RISCV_RELAX $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0 0x0
+
+.option arch, rv64imafdc
+call with_c_again
+
+# CHECK: R_RISCV_CALL_PLT with_c_again
+# CHECK-NEXT: R_RISCV_RELAX $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0 0x0
+
+.option push
+.option arch, rv64imafd
+call inside_push_no_c
+
+# CHECK: R_RISCV_CALL_PLT inside_push_no_c
+# CHECK-NEXT: R_RISCV_RELAX $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0 0x0
+
+.option pop
+call after_pop_with_c
+
+# CHECK: R_RISCV_CALL_PLT after_pop_with_c
+# CHECK-NEXT: R_RISCV_RELAX $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0 0x0
+
+# The initial base-ISA symbol appears at 0x0.
+# CHECK: Name: $xrv64i2p1
+# CHECK-NEXT: Value: 0x0
+# The with-C symbol appears at 0x8 (start of second instruction pair).
+# CHECK: Name: $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0
+# CHECK-NEXT: Value: 0x8
+# The without-C symbol appears at 0x10.
+# CHECK: Name: $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_zicsr2p0_zmmul1p0_zaamo1p0_zalrsc1p0
+# CHECK-NEXT: Value: 0x10
diff --git a/llvm/test/MC/RISCV/option-exact.s b/llvm/test/MC/RISCV/option-exact.s
index 83e25817bfc08..d71c2eb172a0c 100644
--- a/llvm/test/MC/RISCV/option-exact.s
+++ b/llvm/test/MC/RISCV/option-exact.s
@@ -31,7 +31,7 @@ c.lw a0, 0(a0)
 # CHECK-ASM-SAME: # encoding: [0x97'A',A,A,A,0xe7'A',0x80'A',A,A]
 # CHECK-OBJDUMP: auipc ra, 0
 # CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
-# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX {{.*}}
 # CHECK-OBJDUMP-NEXT: jalr ra
 call undefined at plt
 
@@ -98,7 +98,7 @@ c.lw a0, 0(a0)
 # CHECK-ASM-SAME: # encoding: [0x97'A',A,A,A,0xe7'A',0x80'A',A,A]
 # CHECK-OBJDUMP: auipc ra, 0
 # CHECK-OBJDUMP-NEXT: R_RISCV_CALL_PLT undefined
-# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-OBJDUMP-NEXT: R_RISCV_RELAX {{.*}}
 # CHECK-OBJDUMP-NEXT: jalr ra, 0(ra)
 call undefined at plt
 
diff --git a/llvm/test/MC/RISCV/option-pushpop.s b/llvm/test/MC/RISCV/option-pushpop.s
index 68d60be9f888d..411442e822616 100644
--- a/llvm/test/MC/RISCV/option-pushpop.s
+++ b/llvm/test/MC/RISCV/option-pushpop.s
@@ -21,7 +21,7 @@
 
 # CHECK-INST: call foo
 # CHECK-RELOC: R_RISCV_CALL_PLT foo 0x0
-# CHECK-RELOC-NOT: R_RISCV_RELAX - 0x0
+# CHECK-RELOC-NOT: R_RISCV_RELAX {{.*}} 0x0
 call foo
 
 # CHECK-INST: addi s0, sp, 1020
@@ -36,7 +36,7 @@ addi s0, sp, 1020
 # CHECK-INST: .option relax
 # CHECK-INST: call bar
 # CHECK-RELOC-NEXT: R_RISCV_CALL_PLT bar 0x0
-# CHECK-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# CHECK-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 call bar
 
 .option push    # Push relax=true, rvc=false
@@ -58,14 +58,14 @@ addi s0, sp, 1020
 
 # CHECK-INST: call bar
 # CHECK-RELOC-NEXT: R_RISCV_CALL_PLT bar 0x0
-# CHECK-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# CHECK-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 call bar
 
 .option pop     # Pop relax=false, rvc=false
 # CHECK-INST: .option pop
 # CHECK-INST: call baz
 # CHECK-RELOC: R_RISCV_CALL_PLT baz 0x0
-# CHECK-RELOC-NOT: R_RISCV_RELAX - 0x0
+# CHECK-RELOC-NOT: R_RISCV_RELAX {{.*}} 0x0
 call baz
 
 # CHECK-INST: addi s0, sp, 1020
diff --git a/llvm/test/MC/RISCV/option-relax.s b/llvm/test/MC/RISCV/option-relax.s
index e591c824eb437..8c30b5470060e 100644
--- a/llvm/test/MC/RISCV/option-relax.s
+++ b/llvm/test/MC/RISCV/option-relax.s
@@ -34,7 +34,7 @@ beq s1, s1, .L1
 
 # CHECK-INST: call bar
 # CHECK-RELOC-NEXT: R_RISCV_CALL_PLT bar 0x0
-# CHECK-RELOC-NEXT: R_RISCV_RELAX - 0x0
+# CHECK-RELOC-NEXT: R_RISCV_RELAX {{.*}} 0x0
 call bar
 
 .dword .L2-.L1
@@ -49,7 +49,7 @@ beq s1, s1, .L1
 
 # CHECK-INST: call baz
 # CHECK-RELOC-NEXT: R_RISCV_CALL_PLT baz 0x0
-# CHECK-RELOC-NOT: R_RISCV_RELAX - 0x0
+# CHECK-RELOC-NOT: R_RISCV_RELAX {{.*}} 0x0
 call baz
 
 .dword .L2-.L1
diff --git a/llvm/test/MC/RISCV/option-rvc-relax-mapping-sym.s b/llvm/test/MC/RISCV/option-rvc-relax-mapping-sym.s
new file mode 100644
index 0000000000000..ed3872f01631d
--- /dev/null
+++ b/llvm/test/MC/RISCV/option-rvc-relax-mapping-sym.s
@@ -0,0 +1,106 @@
+# Test .option rvc/.option norvc ISA mapping symbol emission and R_RISCV_RELAX
+# symbol references across four combinations of initial relax/rvc state
+# (RISC-V ELF PSABI PR #393).
+#
+# Each RUN assembles the same source with a different -mattr combination.
+#   RELAX-NORVC   (+relax, no C) - baseline RVC off, relaxation on
+#   RELAX-RVC     (+relax, +c)   - baseline RVC on,  relaxation on
+#   NORELAX-NORVC (no mattr)     - baseline RVC off, relaxation off
+#   NORELAX-RVC   (+c)           - baseline RVC on,  relaxation off
+#
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+relax     %s \
+# RUN:   -o %t.relax_norvc
+# RUN: llvm-readobj -r --symbols %t.relax_norvc \
+# RUN:   | FileCheck --check-prefix=RELAX-NORVC %s
+#
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+relax,+c  %s \
+# RUN:   -o %t.relax_rvc
+# RUN: llvm-readobj -r --symbols %t.relax_rvc \
+# RUN:   | FileCheck --check-prefix=RELAX-RVC %s
+#
+# RUN: llvm-mc -filetype=obj -triple riscv64                    %s \
+# RUN:   -o %t.norelax_norvc
+# RUN: llvm-readobj -r --symbols %t.norelax_norvc \
+# RUN:   | FileCheck --check-prefix=NORELAX-NORVC %s
+#
+# RUN: llvm-mc -filetype=obj -triple riscv64 -mattr=+c          %s \
+# RUN:   -o %t.norelax_rvc
+# RUN: llvm-readobj -r --symbols %t.norelax_rvc \
+# RUN:   | FileCheck --check-prefix=NORELAX-RVC %s
+
+# An initial "$x<ISAString>" symbol is emitted before the very first
+# instruction so R_RISCV_RELAX always has a concrete ISA reference.
+nop
+call before_option
+
+# RELAX-NORVC:      R_RISCV_CALL_PLT before_option
+# RELAX-NORVC-NEXT: R_RISCV_RELAX $xrv64i2p1 0x0
+# RELAX-RVC:        R_RISCV_CALL_PLT before_option
+# RELAX-RVC-NEXT:   R_RISCV_RELAX $xrv64i2p1_c2p0_zca1p0 0x0
+# NORELAX-NORVC:    R_RISCV_CALL_PLT before_option
+# NORELAX-NORVC-NOT: R_RISCV_RELAX
+# NORELAX-RVC:      R_RISCV_CALL_PLT before_option
+# NORELAX-RVC-NOT:  R_RISCV_RELAX
+
+# .option rvc enables RVC; an ISA mapping symbol is emitted (deduplication:
+# only when the ISA actually changes).  When starting with +c the directive
+# is a no-op so the ISA sym and R_RISCV_RELAX target remain unchanged.
+.option rvc
+call after_rvc
+
+# RELAX-NORVC:      R_RISCV_CALL_PLT after_rvc
+# RELAX-NORVC-NEXT: R_RISCV_RELAX $xrv64i2p1_c2p0_zca1p0 0x0
+# RELAX-RVC:        R_RISCV_CALL_PLT after_rvc
+# RELAX-RVC-NEXT:   R_RISCV_RELAX $xrv64i2p1_c2p0_zca1p0 0x0
+# NORELAX-NORVC:    R_RISCV_CALL_PLT after_rvc
+# NORELAX-NORVC-NOT: R_RISCV_RELAX
+# NORELAX-RVC:      R_RISCV_CALL_PLT after_rvc
+# NORELAX-RVC-NOT:  R_RISCV_RELAX
+
+# .option norvc disables RVC; a new ISA mapping symbol without C is emitted.
+.option norvc
+call after_norvc
+
+# RELAX-NORVC:      R_RISCV_CALL_PLT after_norvc
+# RELAX-NORVC-NEXT: R_RISCV_RELAX $xrv64i2p1 0x0
+# RELAX-RVC:        R_RISCV_CALL_PLT after_norvc
+# RELAX-RVC-NEXT:   R_RISCV_RELAX $xrv64i2p1 0x0
+# NORELAX-NORVC:    R_RISCV_CALL_PLT after_norvc
+# NORELAX-NORVC-NOT: R_RISCV_RELAX
+# NORELAX-RVC:      R_RISCV_CALL_PLT after_norvc
+# NORELAX-RVC-NOT:  R_RISCV_RELAX
+
+# Verify ISA mapping symbols at the correct offsets.  In all cases the initial
+# ISA symbol is emitted at 0x0.
+#
+# Layout with RVC off (nop = 4 bytes):
+#   0x00  initial sym + nop,  0x04  call before_option,
+#   0x0C  .option rvc (new sym),  call after_rvc,
+#   0x14  .option norvc (new sym),  call after_norvc
+#
+# RELAX-NORVC:       Name: $xrv64i2p1
+# RELAX-NORVC-NEXT:  Value: 0x0
+# RELAX-NORVC:       Name: $xrv64i2p1_c2p0_zca1p0
+# RELAX-NORVC-NEXT:  Value: 0xC
+# RELAX-NORVC:       Name: $xrv64i2p1
+# RELAX-NORVC-NEXT:  Value: 0x14
+# NORELAX-NORVC:     Name: $xrv64i2p1
+# NORELAX-NORVC-NEXT: Value: 0x0
+# NORELAX-NORVC:     Name: $xrv64i2p1_c2p0_zca1p0
+# NORELAX-NORVC-NEXT: Value: 0xC
+# NORELAX-NORVC:     Name: $xrv64i2p1
+# NORELAX-NORVC-NEXT: Value: 0x14
+#
+# Layout with RVC on (nop = c.nop = 2 bytes):
+#   0x00  initial sym + c.nop,  0x02  call before_option,
+#   0x0A  .option rvc (no-op),  call after_rvc,
+#   0x12  .option norvc (new sym),  call after_norvc
+#
+# RELAX-RVC:        Name: $xrv64i2p1_c2p0_zca1p0
+# RELAX-RVC-NEXT:   Value: 0x0
+# RELAX-RVC:        Name: $xrv64i2p1
+# RELAX-RVC-NEXT:   Value: 0x12
+# NORELAX-RVC:      Name: $xrv64i2p1_c2p0_zca1p0
+# NORELAX-RVC-NEXT: Value: 0x0
+# NORELAX-RVC:      Name: $xrv64i2p1
+# NORELAX-RVC-NEXT: Value: 0x12
diff --git a/llvm/test/MC/RISCV/pcrel-fixups.s b/llvm/test/MC/RISCV/pcrel-fixups.s
index 22e317a7c9690..dbdd802957110 100644
--- a/llvm/test/MC/RISCV/pcrel-fixups.s
+++ b/llvm/test/MC/RISCV/pcrel-fixups.s
@@ -26,10 +26,10 @@ function:
 
 # RELAX: auipc	a0, 0
 # RELAX: R_RISCV_PCREL_HI20	local_function
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 # RELAX: addi	a1, a0, 0
 # RELAX: R_RISCV_PCREL_LO12_I	.Lpcrel_label1
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 
 	.p2align	2   # Cause a new fragment be emitted here
 .Lpcrel_label2:
@@ -42,10 +42,10 @@ function:
 
 # RELAX: auipc	a0, 0
 # RELAX: R_RISCV_PCREL_HI20	local_function
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 # RELAX: addi	a1, a0, 0
 # RELAX: R_RISCV_PCREL_LO12_I	.Lpcrel_label2
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 
 	.type	local_function, at function
 local_function:
@@ -64,10 +64,10 @@ local_function:
 
 # RELAX: auipc	a0, 0
 # RELAX: R_RISCV_PCREL_HI20	local_function
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 # RELAX: addi	a1, a0, 0
 # RELAX: R_RISCV_PCREL_LO12_I	.Lpcrel_label3
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 
 # Check handling of symbol binding.
 
@@ -81,10 +81,10 @@ local_function:
 
 # RELAX: auipc	a0, 0
 # RELAX: R_RISCV_PCREL_HI20	global_function
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 # RELAX: addi	a1, a0, 0
 # RELAX: R_RISCV_PCREL_LO12_I	.Lpcrel_label4
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 
 .Lpcrel_label5:
 	auipc	a0, %pcrel_hi(weak_function)
@@ -96,10 +96,10 @@ local_function:
 
 # RELAX: auipc	a0, 0
 # RELAX: R_RISCV_PCREL_HI20	weak_function
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 # RELAX: addi	a1, a0, 0
 # RELAX: R_RISCV_PCREL_LO12_I	.Lpcrel_label5
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 
 .Lpcrel_label6:
 	auipc	a0, %pcrel_hi(ifunc)
@@ -108,7 +108,7 @@ local_function:
 
 # RELAX: auipc	a0, 0
 # RELAX: R_RISCV_PCREL_HI20	ifunc
-# RELAX: R_RISCV_RELAX	*ABS*
+# RELAX: R_RISCV_RELAX	{{.*}}
 
 	.global	global_function
 	.type	global_function, at function
diff --git a/llvm/test/MC/RISCV/xqcibi-linker-relaxation.s b/llvm/test/MC/RISCV/xqcibi-linker-relaxation.s
index 6122981f82e2b..033a94e6a1138 100644
--- a/llvm/test/MC/RISCV/xqcibi-linker-relaxation.s
+++ b/llvm/test/MC/RISCV/xqcibi-linker-relaxation.s
@@ -16,7 +16,7 @@ branch_over_relaxable:
 # CHECK: qc.e.jal 0x0 <branch_over_relaxable>
 # CHECK-NEXT: R_RISCV_VENDOR QUALCOMM
 # CHECK-NEXT: R_RISCV_QC_E_CALL_PLT foo
-# CHECK-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-NEXT: R_RISCV_RELAX {{.*}}
   bne a0, a1, branch_over_relaxable
 # CHECK-NEXT: bne a0, a1, 0x6 <branch_over_relaxable+0x6>
 # CHECK-NEXT: R_RISCV_BRANCH branch_over_relaxable
@@ -45,7 +45,7 @@ short_jump_over_relaxable:
   call foo
 # CHECK: auipc ra, 0x0
 # CHECK-NEXT: R_RISCV_CALL_PLT foo
-# CHECK-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-NEXT: R_RISCV_RELAX {{.*}}
 # CHECK-NEXT: jalr ra, 0x0(ra) <short_jump_over_relaxable>
   j short_jump_over_relaxable
 # CHECK-NEXT: c.j 0x20 <short_jump_over_relaxable+0x8>
@@ -63,7 +63,7 @@ mid_jump_over_fixed:
   j mid_jump_over_fixed
 # CHECK-NEXT: jal zero, 0x1026 <mid_jump_over_fixed+0x1002>
 # CHECK-NEXT: R_RISCV_JAL mid_jump_over_fixed
-# CHECK-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-NEXT: R_RISCV_RELAX {{.*}}
   ret
 # CHECK-NEXT: c.jr ra
 
@@ -72,7 +72,7 @@ mid_jump_over_relaxable:
   call foo
 # CHECK: auipc ra, 0x0
 # CHECK-NEXT: R_RISCV_CALL_PLT foo
-# CHECK-NEXT: R_RISCV_RELAX *ABS*
+# CHECK-NEXT: R_RISCV_RELAX {{.*}}
 # CHECK-NEXT: jalr ra, 0x0(ra) <mid_jump_over_relaxable>
   .space 0x1000
 # CHECK-NEXT: ...
diff --git a/llvm/test/MC/RISCV/xqcibi-relocations.s b/llvm/test/MC/RISCV/xqcibi-relocations.s
index 0ab860667faeb..07f8e69c83b5a 100644
--- a/llvm/test/MC/RISCV/xqcibi-relocations.s
+++ b/llvm/test/MC/RISCV/xqcibi-relocations.s
@@ -91,14 +91,14 @@ qc.e.bgeui s2, 24, same_section
 # OBJ: qc.beqi t1, 0xa, 0x42 <same_section_extern+0x16>
 # OBJ-NEXT: j 0x3e <same_section_extern+0x12>
 # OBJ-NEXT: R_RISCV_JAL undef{{$}}
-# OBJ-NEXT: R_RISCV_RELAX *ABS*{{$}}
+# OBJ-NEXT: R_RISCV_RELAX {{.*}}{{$}}
 qc.bnei t1, 10, undef
 
 # ASM: qc.e.bgeui s0, 40, undef
 # OBJ-NEXT: qc.e.bltui s0, 0x28, 0x4c <same_section_extern+0x20>
 # OBJ-NEXT: j 0x48 <same_section_extern+0x1c>
 # OBJ-NEXT: R_RISCV_JAL undef{{$}}
-# OBJ-NEXT: R_RISCV_RELAX *ABS*{{$}}
+# OBJ-NEXT: R_RISCV_RELAX {{.*}}{{$}}
 qc.e.bgeui s0, 40, undef
 
 .section .text.second, "ax", @progbits
diff --git a/llvm/test/MC/RISCV/xqcili-linker-relaxation.s b/llvm/test/MC/RISCV/xqcili-linker-relaxation.s
index 41a5f39c17278..33a83974b0f0b 100644
--- a/llvm/test/MC/RISCV/xqcili-linker-relaxation.s
+++ b/llvm/test/MC/RISCV/xqcili-linker-relaxation.s
@@ -15,7 +15,7 @@
 # CHECK-NEXT: qc.e.li a0, 0x0
 # CHECK-NEXT: R_RISCV_VENDOR QUALCOMM{{$}}
 # CHECK-NEXT: R_RISCV_QC_E_32 sym{{$}}
-# CHECK-NEXT: R_RISCV_RELAX *ABS*{{$}}
+# CHECK-NEXT: R_RISCV_RELAX {{.*}}{{$}}
 .L1:
 # CHECK: <.L1>:
   ret
@@ -30,7 +30,7 @@
 # CHECK-NEXT: qc.li a0, 0x0
 # CHECK-NEXT: R_RISCV_VENDOR QUALCOMM{{$}}
 # CHECK-NEXT: R_RISCV_QC_ABS20_U sym{{$}}
-# CHECK-NEXT: R_RISCV_RELAX *ABS*{{$}}
+# CHECK-NEXT: R_RISCV_RELAX {{.*}}{{$}}
 .L2:
 # CHECK: <.L2>:
   ret

>From e0bd87b30304ccbffc414f6013e288602c73d556 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Fri, 5 Jun 2026 17:31:08 +0800
Subject: [PATCH 2/3] !fixup forward declare RISCVELFStreamer instead of inline
 class keyword

---
 llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
index d327bedf844d8..14d5b48d2ae48 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
@@ -20,6 +20,7 @@ namespace llvm {
 class MCAssembler;
 class MCObjectTargetWriter;
 class raw_ostream;
+class RISCVELFStreamer;
 
 class RISCVAsmBackend : public MCAsmBackend {
 protected:
@@ -30,7 +31,7 @@ class RISCVAsmBackend : public MCAsmBackend {
   // Temporary symbol used to check whether a PC-relative fixup is resolved.
   MCSymbol *PCRelTemp = nullptr;
 
-  class RISCVELFStreamer *ELFStreamer = nullptr;
+  RISCVELFStreamer *ELFStreamer = nullptr;
 
   bool isPCRelFixupResolved(const MCSymbol *SymA, const MCFragment &F);
 

>From 83007c1d23051eeafea0712f749aa49993971e47 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Fri, 12 Jun 2026 16:32:51 +0800
Subject: [PATCH 3/3] !fixup note that R_RISCV_RELAX/R_RISCV_VENDOR must stay
 symbol-relative

---
 llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
index 2885e3cca8722..58591cecdebff 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
@@ -29,7 +29,10 @@ class RISCVELFObjectWriter : public MCELFObjectTargetWriter {
   // section plus offset.
   bool needsRelocateWithSymbol(const MCValue &, unsigned Type) const override {
     // TODO: this is very conservative, update once RISC-V psABI requirements
-    //       are clarified.
+    //       are clarified. Note that R_RISCV_RELAX and R_RISCV_VENDOR must keep
+    //       their referenced symbol (not be rewritten to a section symbol plus
+    //       offset): the symbol carries semantics (the ISA mapping symbol and
+    //       the vendor identity respectively), not just an address.
     return true;
   }
 



More information about the llvm-commits mailing list