[llvm-branch-commits] [llvm] [RISC-V][MC] Update ELF streamer ArchString in setFlagsFromFeatures() (PR #225140)

Alexander Richardson via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Sep 26 19:01:49 PDT 2026


https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/225140

>From aa8098518e70ed47f32a0f3f40a01798cb49f3a7 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Tue, 22 Sep 2026 22:52:25 -0700
Subject: [PATCH 1/3] [RISC-V][MC] Update ELF streamer ArchString in
 setFlagsFromFeatures()

During LTO, the TargetMachine subtarget is initialized with the linker's
default CPU (e.g. `generic-rv64`, `rv64i2p1`), while
RISCVAsmPrinter::emitStartOfAsmFile() reconstructs the module's actual ISA from
the `riscv-isa` module flag and calls RISCVTargetStreamer::setFlagsFromFeatures()
and RISCVTargetStreamer::emitTargetAttributes(). Because
RISCVTargetELFStreamer previously only initialized InitialArchString and
ArchString in its constructor rather than in setFlagsFromFeatures(), direct
object emission bypassed the update and tagged `.text` with `$xrv64i2p1`
instead of the module's full architecture string.

Move the InitialArchString and ArchString initialization into
RISCVTargetELFStreamer::setFlagsFromFeatures() and also call setArchString()
alongside emitTextAttribute(RISCVAttrs::ARCH, ...) in
RISCVTargetStreamer::emitTargetAttributes().

This commit was created with the help of AI tools
---
 .../riscv/lto-inline-asm-abi.c                | 19 ++++++++-----------
 .../Target/RISCV/AsmParser/RISCVAsmParser.cpp |  7 ++-----
 .../RISCV/MCTargetDesc/RISCVELFStreamer.cpp   |  6 ++++++
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/cross-project-tests/riscv/lto-inline-asm-abi.c b/cross-project-tests/riscv/lto-inline-asm-abi.c
index 883244f1854f7..897413428500f 100644
--- a/cross-project-tests/riscv/lto-inline-asm-abi.c
+++ b/cross-project-tests/riscv/lto-inline-asm-abi.c
@@ -35,33 +35,30 @@
 // FLAGS-NEXT:   EF_RISCV_RVC (0x1)
 // FLAGS-NEXT: ]
 //
-/// TODO: RISCVTargetELFStreamer::emitTextAttribute does not update the
-/// streamer's ArchString when emitting the module's RISCVAttrs::ARCH attribute
-/// ("rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_...").
 // DISASM-LABEL: Disassembly of section .text:
 // DISASM-EMPTY:
-// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0>:
 // DISASM-NEXT:  [[#%x,]]:      	nop
 // DISASM-EMPTY:
-// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0>:
 // DISASM-NEXT:  [[#%x,]] <symver_fn>:
 // DISASM-NEXT:  [[#%x,]]:      	ret
 // DISASM-EMPTY:
-// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0>:
 // DISASM-NEXT:  [[#%x,]] <fn>:
 // DISASM-NEXT:  [[#%x,]]:      	nop
 // DISASM-NEXT:  [[#%x,]]:      	ret
 // DISASM-EMPTY:
-// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1>:
+// DISASM-NEXT:  [[#%x,]] <$xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0>:
 // DISASM-NEXT:  [[#%x,]] <caller>:
 // DISASM-NEXT:  [[#%x,]]:      	nop
 // DISASM-NEXT:  [[#%x,]]:      	ret
 // DISASM-NOT:   {{.}}
 //
-// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1{{$}}
-// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1{{$}}
-// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1{{$}}
-// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1{{$}}
+// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0{{$}}
+// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0{{$}}
+// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0{{$}}
+// SYMS: [[#%x,]] l       .text	0000000000000000 $xrv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_v1p0_zicsr2p0_zifencei2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0_zcd1p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0{{$}}
 // SYMS: [[#%x,]] g     F .text	0000000000000004 fn{{$}}
 // SYMS: [[#%x,]] g     F .text	0000000000000002 symver_fn{{$}}
 // SYMS: [[#%x,]] g     F .text	0000000000000004 caller{{$}}
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 478a2c785819b..22030f23fb4fe 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -3583,12 +3583,9 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
     if (resetToArch(StringValue, ValueExprLoc, Result, false))
       return true;
 
-    // Then emit the arch string.
+    // Emit the arch string (which also updates the target streamer's active
+    // mapping symbol ISA).
     getTargetStreamer().emitTextAttribute(Tag, Result);
-
-    // And then update the active ISA so the next instruction-run emits
-    // an ISA-specific mapping symbol.
-    getTargetStreamer().setArchString(Result);
   }
 
   return false;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 10702a836de33..704b195cbf0cb 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -20,6 +20,7 @@
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCELFObjectWriter.h"
 #include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/RISCVAttributes.h"
 
 using namespace llvm;
 
@@ -84,6 +85,11 @@ void RISCVTargetELFStreamer::emitAttribute(unsigned Attribute, unsigned Value) {
 void RISCVTargetELFStreamer::emitTextAttribute(unsigned Attribute,
                                                StringRef String) {
   getStreamer().setAttributeItem(Attribute, String, /*OverwriteExisting=*/true);
+  // Keep the active mapping symbol ISA in sync with .attribute arch and
+  // RISCVTargetStreamer::emitTargetAttributes() (e.g. when LTO reconstructs the
+  // module's ISA from the riscv-isa module flag).
+  if (Attribute == RISCVAttrs::ARCH)
+    setArchString(String);
 }
 
 void RISCVTargetELFStreamer::emitIntTextAttribute(unsigned Attribute,

>From 77bb91d35843081ebf8c6f9ffed2dac861aef0da Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Wed, 23 Sep 2026 23:10:38 -0700
Subject: [PATCH 2/3] try to address feedback

---
 llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp         | 7 +++++--
 llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp    | 6 ------
 llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp | 4 +++-
 3 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 22030f23fb4fe..478a2c785819b 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -3583,9 +3583,12 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
     if (resetToArch(StringValue, ValueExprLoc, Result, false))
       return true;
 
-    // Emit the arch string (which also updates the target streamer's active
-    // mapping symbol ISA).
+    // Then emit the arch string.
     getTargetStreamer().emitTextAttribute(Tag, Result);
+
+    // And then update the active ISA so the next instruction-run emits
+    // an ISA-specific mapping symbol.
+    getTargetStreamer().setArchString(Result);
   }
 
   return false;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 704b195cbf0cb..10702a836de33 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -20,7 +20,6 @@
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCELFObjectWriter.h"
 #include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/Support/RISCVAttributes.h"
 
 using namespace llvm;
 
@@ -85,11 +84,6 @@ void RISCVTargetELFStreamer::emitAttribute(unsigned Attribute, unsigned Value) {
 void RISCVTargetELFStreamer::emitTextAttribute(unsigned Attribute,
                                                StringRef String) {
   getStreamer().setAttributeItem(Attribute, String, /*OverwriteExisting=*/true);
-  // Keep the active mapping symbol ISA in sync with .attribute arch and
-  // RISCVTargetStreamer::emitTargetAttributes() (e.g. when LTO reconstructs the
-  // module's ISA from the riscv-isa module flag).
-  if (Attribute == RISCVAttrs::ARCH)
-    setArchString(String);
 }
 
 void RISCVTargetELFStreamer::emitIntTextAttribute(unsigned Attribute,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
index d6523cb42c879..f5f62bc5200f1 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.cpp
@@ -86,7 +86,9 @@ void RISCVTargetStreamer::emitTargetAttributes(const MCSubtargetInfo &STI,
     report_fatal_error(ParseResult.takeError());
   } else {
     auto &ISAInfo = *ParseResult;
-    emitTextAttribute(RISCVAttrs::ARCH, ISAInfo->toString());
+    std::string Arch = ISAInfo->toString();
+    emitTextAttribute(RISCVAttrs::ARCH, Arch);
+    setArchString(Arch);
   }
 
   if (RiscvAbiAttr && STI.hasFeature(RISCV::FeatureStdExtA)) {

>From a115b8e14f3b558253d4b87117dbdcd5aacbf563 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Thu, 24 Sep 2026 10:15:25 -0700
Subject: [PATCH 3/3] use recommended approach

---
 .../RISCV/MCTargetDesc/RISCVELFStreamer.cpp   | 29 +++++++++++--------
 .../RISCV/MCTargetDesc/RISCVELFStreamer.h     |  5 ++--
 .../RISCV/MCTargetDesc/RISCVTargetStreamer.h  |  2 +-
 3 files changed, 21 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index 10702a836de33..11093a700ac27 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -28,18 +28,6 @@ RISCVTargetELFStreamer::RISCVTargetELFStreamer(MCStreamer &S,
                                                const MCSubtargetInfo &STI)
     : RISCVTargetStreamer(S), CurrentVendor("riscv") {
   setFlagsFromFeatures(STI);
-
-  // Compute the initial ISA string.  This serves two purposes:
-  //   1. Deduplication: subsequent .option arch/rvc/norvc directives compare
-  //      against ArchString to avoid propagating redundant ISA updates.
-  //   2. Initial symbol: seed the streamer's active ISA so a "$x<ArchString>"
-  //      mapping symbol is emitted before the first instruction, recording
-  //      the full ISA in the object even when no .option directive is present.
-  if (auto ParseResult = RISCVFeatures::parseFeatureBits(STI)) {
-    InitialArchString = (*ParseResult)->toString();
-    ArchString = InitialArchString;
-    getStreamer().setMappingSymbolArch(ArchString);
-  }
 }
 
 RISCVELFStreamer::RISCVELFStreamer(MCContext &C,
@@ -52,6 +40,23 @@ RISCVELFStreamer &RISCVTargetELFStreamer::getStreamer() {
   return static_cast<RISCVELFStreamer &>(Streamer);
 }
 
+void RISCVTargetELFStreamer::setFlagsFromFeatures(const MCSubtargetInfo &STI) {
+  RISCVTargetStreamer::setFlagsFromFeatures(STI);
+
+  // Compute the initial ISA string.  This serves two purposes:
+  //   1. Deduplication: subsequent .option arch/rvc/norvc directives compare
+  //      against ArchString to avoid propagating redundant ISA updates.
+  //   2. Initial symbol: seed the streamer's active ISA so a "$x<ArchString>"
+  //      mapping symbol is emitted before the first instruction, recording
+  //      the full ISA in the object even when no .option directive is present.
+  if (auto ParseResult = RISCVFeatures::parseFeatureBits(STI)) {
+    InitialArchString = (*ParseResult)->toString();
+    setArchString(InitialArchString);
+  } else {
+    consumeError(ParseResult.takeError());
+  }
+}
+
 void RISCVTargetELFStreamer::setArchString(StringRef Arch) {
   if (Arch == ArchString)
     return;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
index ac738307922d7..e86144ac20c9b 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
@@ -56,8 +56,8 @@ class RISCVTargetELFStreamer : public RISCVTargetStreamer {
 private:
   StringRef CurrentVendor;
 
-  // Initial ISA string derived from the subtarget features in the constructor.
-  // Used to re-establish state on reset().
+  // Initial ISA string derived from the subtarget features in
+  // setFlagsFromFeatures(). Used to re-establish state on reset().
   std::string InitialArchString;
 
   // Current ISA string, kept in sync with each .option arch/rvc/norvc/pop
@@ -81,6 +81,7 @@ class RISCVTargetELFStreamer : public RISCVTargetStreamer {
   RISCVELFStreamer &getStreamer();
   RISCVTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI);
 
+  void setFlagsFromFeatures(const MCSubtargetInfo &STI) override;
   // Update ArchString and propagate the change to the streamer so the next
   // instruction-run emits an ISA-specific mapping symbol. A no-op when
   // Arch == ArchString (deduplication).
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
index cd59089fae4c7..c7b4f752d7264 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
@@ -64,7 +64,7 @@ class RISCVTargetStreamer : public MCTargetStreamer {
   void setTargetABI(RISCVABI::ABI ABI);
   RISCVABI::ABI getTargetABI() const { return TargetABI; }
   bool hasTargetABI() const { return TargetABI != RISCVABI::ABI_Unknown; }
-  void setFlagsFromFeatures(const MCSubtargetInfo &STI);
+  virtual void setFlagsFromFeatures(const MCSubtargetInfo &STI);
   bool hasRVC() const { return HasRVC; }
   bool hasTSO() const { return HasTSO; }
 };



More information about the llvm-branch-commits mailing list