[llvm] [RISCV][MC] Emit ISA mapping symbols on .option arch/rvc/norvc/pop (PR #193123)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Apr 20 17:55:38 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-risc-v
Author: Kito Cheng (kito-cheng)
<details>
<summary>Changes</summary>
When .option arch, .option rvc, .option norvc, or .option pop changes the active ISA, emit a "$x<ISAString>" mapping symbol before the next instruction so that tools can determine the ISA in effect for each code region.
Also emit ISA mapping symbols on begin of first instruction to make sure link with different object still can disassemble correctly.
Based on #<!-- -->67541
---
Patch is 21.49 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/193123.diff
9 Files Affected:
- (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+13)
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp (+74-8)
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h (+34-2)
- (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h (+1)
- (modified) llvm/test/MC/RISCV/mapping-across-sections.s (+10-7)
- (added) llvm/test/MC/RISCV/mapping-isa-attribute.s (+17)
- (added) llvm/test/MC/RISCV/mapping-isa-option-rv32.s (+75)
- (added) llvm/test/MC/RISCV/mapping-isa-option-rv64.s (+75)
- (added) llvm/test/MC/RISCV/mapping-isa-option-rvc-dedup.s (+28)
``````````diff
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index c2f564e559d72..97ea4147eba6d 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -3214,6 +3214,10 @@ bool RISCVAsmParser::parseDirectiveOption() {
return true;
getTargetStreamer().emitDirectiveOptionArch(Args);
+
+ if (auto ParseResult =
+ RISCVFeatures::parseFeatureBits(isRV64(), STI->getFeatureBits()))
+ getTargetStreamer().emitISAMappingSymbol((*ParseResult)->toString());
return false;
}
@@ -3243,6 +3247,9 @@ bool RISCVAsmParser::parseDirectiveOption() {
getTargetStreamer().emitDirectiveOptionRVC();
setFeatureBits(RISCV::FeatureStdExtC, "c");
+ if (auto ParseResult =
+ RISCVFeatures::parseFeatureBits(isRV64(), STI->getFeatureBits()))
+ getTargetStreamer().emitISAMappingSymbol((*ParseResult)->toString());
return false;
}
@@ -3253,6 +3260,9 @@ bool RISCVAsmParser::parseDirectiveOption() {
getTargetStreamer().emitDirectiveOptionNoRVC();
clearFeatureBits(RISCV::FeatureStdExtC, "c");
clearFeatureBits(RISCV::FeatureStdExtZca, "zca");
+ if (auto ParseResult =
+ RISCVFeatures::parseFeatureBits(isRV64(), STI->getFeatureBits()))
+ getTargetStreamer().emitISAMappingSymbol((*ParseResult)->toString());
return false;
}
@@ -3375,6 +3385,9 @@ bool RISCVAsmParser::parseDirectiveAttribute() {
// Then emit the arch string.
getTargetStreamer().emitTextAttribute(Tag, Result);
+
+ // And then emit mapping symbol with arch string.
+ getTargetStreamer().emitISAMappingSymbol(Result);
}
return false;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
index a5bd1245ab856..55f864176c2a8 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.cpp
@@ -34,6 +34,19 @@ RISCVTargetELFStreamer::RISCVTargetELFStreamer(MCStreamer &S,
setTargetABI(RISCVABI::computeTargetABI(STI.getTargetTriple(), Features,
MAB.getTargetOptions().getABIName()));
setFlagsFromFeatures(STI);
+
+ // Compute the initial ISA string. This serves two purposes:
+ // 1. Deduplication: subsequent .option arch/rvc/norvc directives compare
+ // against ISAString to avoid propagating redundant ISA updates.
+ // 2. Initial symbol: seed the streamer's active ISA so a "$x<ISAString>"
+ // 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.hasFeature(RISCV::Feature64Bit), Features)) {
+ InitialISAString = (*ParseResult)->toString();
+ ISAString = InitialISAString;
+ getStreamer().setCurrentISAString(ISAString);
+ }
}
RISCVELFStreamer::RISCVELFStreamer(MCContext &C,
@@ -46,12 +59,30 @@ RISCVELFStreamer &RISCVTargetELFStreamer::getStreamer() {
return static_cast<RISCVELFStreamer &>(Streamer);
}
+void RISCVTargetELFStreamer::setArchString(StringRef Arch) {
+ if (Arch == ISAString)
+ return;
+ ISAString = std::string(Arch);
+ getStreamer().setCurrentISAString(Arch);
+}
+
+void RISCVTargetELFStreamer::emitISAMappingSymbol(StringRef ISAStr) {
+ setArchString(ISAStr);
+}
+
+void RISCVTargetELFStreamer::emitDirectiveOptionPush() {
+ ISAStringStack.push_back(ISAString);
+}
+
+void RISCVTargetELFStreamer::emitDirectiveOptionPop() {
+ if (!ISAStringStack.empty())
+ setArchString(ISAStringStack.pop_back_val());
+}
+
void RISCVTargetELFStreamer::emitDirectiveOptionExact() {}
void RISCVTargetELFStreamer::emitDirectiveOptionNoExact() {}
void RISCVTargetELFStreamer::emitDirectiveOptionPIC() {}
void RISCVTargetELFStreamer::emitDirectiveOptionNoPIC() {}
-void RISCVTargetELFStreamer::emitDirectiveOptionPop() {}
-void RISCVTargetELFStreamer::emitDirectiveOptionPush() {}
void RISCVTargetELFStreamer::emitDirectiveOptionRelax() {}
void RISCVTargetELFStreamer::emitDirectiveOptionNoRelax() {}
void RISCVTargetELFStreamer::emitDirectiveOptionRVC() {}
@@ -119,6 +150,13 @@ void RISCVTargetELFStreamer::finish() {
void RISCVTargetELFStreamer::reset() {
AttributeSection = nullptr;
+ ISAString = InitialISAString;
+ ISAStringStack.clear();
+ // Re-seed the streamer's active ISA so the first instruction after reset
+ // still records the full ISA via "$x<ISA>", matching the behaviour set up
+ // in the constructor.
+ if (!InitialISAString.empty())
+ getStreamer().setCurrentISAString(InitialISAString);
}
void RISCVTargetELFStreamer::emitDirectiveVariantCC(MCSymbol &Symbol) {
@@ -127,10 +165,15 @@ void RISCVTargetELFStreamer::emitDirectiveVariantCC(MCSymbol &Symbol) {
}
void RISCVELFStreamer::reset() {
- static_cast<RISCVTargetStreamer *>(getTargetStreamer())->reset();
MCELFStreamer::reset();
LastMappingSymbols.clear();
LastEMS = EMS_None;
+ CurrentISAString.clear();
+ LastEmittedISA.clear();
+ LastEmittedISAInSection.clear();
+ // Call target streamer reset last: it may call setCurrentISAString to
+ // re-seed the initial ISA after our state has been cleared.
+ static_cast<RISCVTargetStreamer *>(getTargetStreamer())->reset();
}
void RISCVELFStreamer::emitDataMappingSymbol() {
@@ -141,9 +184,22 @@ void RISCVELFStreamer::emitDataMappingSymbol() {
}
void RISCVELFStreamer::emitInstructionsMappingSymbol() {
- if (LastEMS == EMS_Instructions)
- return;
- emitMappingSymbol("$x");
+ // Emit a mapping symbol at the start of each instruction run, and whenever
+ // the active ISA has changed since the last one emitted in this section.
+ // The symbol takes the form "$x<ISA>" when CurrentISAString is known, or
+ // plain "$x" as a fallback. The comparison with LastEmittedISA provides
+ // deduplication: repeating .option arch with the same ISA, or re-entering a
+ // section whose last mapping symbol already matches the active ISA, emits
+ // no redundant symbol.
+ bool NeedSymbol =
+ LastEMS != EMS_Instructions || LastEmittedISA != CurrentISAString;
+ if (NeedSymbol) {
+ if (CurrentISAString.empty())
+ emitMappingSymbol("$x");
+ else
+ emitMappingSymbol("$x" + CurrentISAString);
+ LastEmittedISA = CurrentISAString;
+ }
LastEMS = EMS_Instructions;
}
@@ -155,12 +211,22 @@ void RISCVELFStreamer::emitMappingSymbol(StringRef Name) {
Symbol->setBinding(ELF::STB_LOCAL);
}
+void RISCVELFStreamer::setCurrentISAString(StringRef Arch) {
+ CurrentISAString = std::string(Arch);
+}
+
void RISCVELFStreamer::changeSection(MCSection *Section, uint32_t Subsection) {
// We have to keep track of the mapping symbol state of any sections we
// use. Each one should start off as EMS_None, which is provided as the
- // default constructor by DenseMap::lookup.
- LastMappingSymbols[getPreviousSection().first] = LastEMS;
+ // default constructor by DenseMap::lookup. The last ISA suffix emitted in
+ // each section is also preserved so that re-entering a section only emits a
+ // new "$x<ISA>" symbol when the active ISA has actually changed.
+ const MCSection *Prev = getPreviousSection().first;
+ LastMappingSymbols[Prev] = LastEMS;
+ LastEmittedISAInSection[Prev] = LastEmittedISA;
LastEMS = LastMappingSymbols.lookup(Section);
+ auto It = LastEmittedISAInSection.find(Section);
+ LastEmittedISA = It != LastEmittedISAInSection.end() ? It->second : "";
MCELFStreamer::changeSection(Section, Subsection);
}
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFStreamer.h
index cc693839c9bda..489b744ba8a6d 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/SmallVector.h"
#include "llvm/MC/MCELFStreamer.h"
namespace llvm {
@@ -25,6 +26,18 @@ class RISCVELFStreamer : public MCELFStreamer {
DenseMap<const MCSection *, ElfMappingSymbol> LastMappingSymbols;
ElfMappingSymbol LastEMS = EMS_None;
+ // Active ISA string propagated from RISCVTargetELFStreamer. When non-empty,
+ // it is used as the suffix for "$x<ISA>" mapping symbols.
+ std::string CurrentISAString;
+
+ // ISA suffix last emitted via "$x<ISA>" in the current section, and the
+ // per-section history preserved across changeSection. A new mapping symbol
+ // is emitted whenever LastEMS != EMS_Instructions or
+ // LastEmittedISA != CurrentISAString, so the ISA in effect at each
+ // instruction run is always recorded.
+ std::string LastEmittedISA;
+ DenseMap<const MCSection *, std::string> LastEmittedISAInSection;
+
public:
RISCVELFStreamer(MCContext &C, std::unique_ptr<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> MOW,
@@ -35,12 +48,25 @@ class RISCVELFStreamer : public MCELFStreamer {
void emitBytes(StringRef Data) override;
void emitFill(const MCExpr &NumBytes, uint64_t FillValue, SMLoc Loc) override;
void emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) override;
+
+ void setCurrentISAString(StringRef Arch);
};
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().
+ std::string InitialISAString;
+
+ // Current ISA string, kept in sync with each .option arch/rvc/norvc/pop
+ // and .attribute arch directive. Used to avoid propagating redundant ISA
+ // updates to the streamer when the ISA does not actually change (e.g.,
+ // .option rvc when C is already enabled).
+ std::string ISAString;
+ SmallVector<std::string, 4> ISAStringStack;
+
MCSection *AttributeSection = nullptr;
void emitAttribute(unsigned Attribute, unsigned Value) override;
@@ -51,16 +77,22 @@ class RISCVTargetELFStreamer : public RISCVTargetStreamer {
void reset() override;
+ // Update ISAString and propagate the change to the streamer so the next
+ // instruction-run emits an ISA-specific mapping symbol. A no-op when
+ // Arch == ISAString (deduplication).
+ void setArchString(StringRef Arch);
+
public:
RISCVELFStreamer &getStreamer();
RISCVTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI);
+ void emitISAMappingSymbol(StringRef ISAString) override;
+ void emitDirectiveOptionPush() override;
+ void emitDirectiveOptionPop() override;
void emitDirectiveOptionExact() override;
void emitDirectiveOptionNoExact() override;
void emitDirectiveOptionPIC() override;
void emitDirectiveOptionNoPIC() override;
- void emitDirectiveOptionPop() override;
- void emitDirectiveOptionPush() override;
void emitDirectiveOptionRelax() override;
void emitDirectiveOptionNoRelax() override;
void emitDirectiveOptionRVC() override;
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
index 169cb0f79ba78..51857eb493653 100644
--- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
+++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVTargetStreamer.h
@@ -52,6 +52,7 @@ class RISCVTargetStreamer : public MCTargetStreamer {
virtual void emitDirectiveOptionNoRelax();
virtual void emitDirectiveOptionRVC();
virtual void emitDirectiveOptionNoRVC();
+ virtual void emitISAMappingSymbol(StringRef ISAString) {}
virtual void emitDirectiveVariantCC(MCSymbol &Symbol);
virtual void emitAttribute(unsigned Attribute, unsigned Value);
virtual void finishAttributeSection();
diff --git a/llvm/test/MC/RISCV/mapping-across-sections.s b/llvm/test/MC/RISCV/mapping-across-sections.s
index 11b05a616bdcf..ecb8292dd6664 100644
--- a/llvm/test/MC/RISCV/mapping-across-sections.s
+++ b/llvm/test/MC/RISCV/mapping-across-sections.s
@@ -1,5 +1,5 @@
-# RUN: llvm-mc -triple=riscv32 -filetype=obj %s | llvm-readelf -Ss - | FileCheck %s
-# RUN: llvm-mc -triple=riscv64 -filetype=obj %s | llvm-readelf -Ss - | FileCheck %s
+# RUN: llvm-mc -triple=riscv32 -filetype=obj %s | llvm-readelf -Ss - | FileCheck %s --check-prefix=CHECK,CHECK-RV32
+# RUN: llvm-mc -triple=riscv64 -filetype=obj %s | llvm-readelf -Ss - | FileCheck %s --check-prefix=CHECK,CHECK-RV64
.text
nop
@@ -13,13 +13,14 @@
.section .starts_data
.word 42
-# Changing back to .text should not emit a redundant $x.
+# Changing back to .text should not emit a redundant $x - the active ISA
+# has not changed since the last mapping symbol emitted in .text.
.text
nop
# With all those constraints, we want:
-# + .text to have $x at 0 and no others
-# + .wibble to have $x at 0
+# + .text to have $x<ISA> at 0 and no others
+# + .wibble to have $x<ISA> at 0 (each code section records the active ISA
# + .starts_data to have $d at 0
## Capture section indices.
@@ -28,6 +29,8 @@
# CHECK: [[#STARTS_DATA:]]] .starts_data
# CHECK: Value Size Type Bind Vis Ndx Name
-# CHECK: 00000000 0 NOTYPE LOCAL DEFAULT [[#TEXT]] $x{{$}}
-# CHECK: 00000000 0 NOTYPE LOCAL DEFAULT [[#WIBBLE]] $x{{$}}
+# CHECK-RV32: 00000000 0 NOTYPE LOCAL DEFAULT [[#TEXT]] $xrv32i2p1{{$}}
+# CHECK-RV64: 00000000 0 NOTYPE LOCAL DEFAULT [[#TEXT]] $xrv64i2p1{{$}}
+# CHECK-RV32: 00000000 0 NOTYPE LOCAL DEFAULT [[#WIBBLE]] $xrv32i2p1{{$}}
+# CHECK-RV64: 00000000 0 NOTYPE LOCAL DEFAULT [[#WIBBLE]] $xrv64i2p1{{$}}
# CHECK: 00000000 0 NOTYPE LOCAL DEFAULT [[#STARTS_DATA]] $d{{$}}
diff --git a/llvm/test/MC/RISCV/mapping-isa-attribute.s b/llvm/test/MC/RISCV/mapping-isa-attribute.s
new file mode 100644
index 0000000000000..c75dc1d47e16c
--- /dev/null
+++ b/llvm/test/MC/RISCV/mapping-isa-attribute.s
@@ -0,0 +1,17 @@
+## Test that .attribute 5 (Tag_RISCV_arch) updates the pending ISA mapping
+## symbol. Unlike .option arch, .attribute 5 is usually placed at the top
+## of the file before any instruction is emitted, so it should *replace*
+## (not add to) the initial "$x<base-ISA>" symbol scheduled by the target
+## streamer constructor.
+
+# RUN: llvm-mc -triple=riscv64 -filetype=obj -o %t.o %s
+# RUN: llvm-readobj --symbols %t.o | FileCheck %s
+
+.attribute 5, "rv64i2p1_f2p2_d2p2_v1p0_zicsr2p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0"
+
+.text
+## The initial mapping symbol reflects the arch widened by .attribute, not
+## the triple-derived base.
+vsetvli a3, a2, e8, m8, tu, mu
+# CHECK: Name: $xrv64i2p1_f2p2_d2p2_v1p0_zicsr2p0_zve32f1p0_zve32x1p0_zve64d1p0_zve64f1p0_zve64x1p0_zvl128b1p0_zvl32b1p0_zvl64b1p0
+# CHECK-NEXT: Value: 0x0
diff --git a/llvm/test/MC/RISCV/mapping-isa-option-rv32.s b/llvm/test/MC/RISCV/mapping-isa-option-rv32.s
new file mode 100644
index 0000000000000..c51e11c129ceb
--- /dev/null
+++ b/llvm/test/MC/RISCV/mapping-isa-option-rv32.s
@@ -0,0 +1,75 @@
+## Test ISA mapping symbol emission for .option rvc/norvc/arch/push/pop on rv32.
+## The assembler emits a "$x<ISAString>" symbol when the active ISA changes,
+## using lazy emission (the symbol appears before the first instruction after
+## the change) and deduplication (no symbol when the ISA is unchanged).
+## An initial "$x<ISAString>" symbol is also emitted before the very first
+## instruction to record the starting ISA in the object file.
+
+# RUN: llvm-mc -triple=riscv32 -mattr=+f,+a -filetype=obj -o %t.o %s
+# RUN: llvm-readobj --symbols %t.o | FileCheck %s
+
+.text
+nop
+# The initial ISA mapping symbol is emitted before the first instruction.
+# CHECK: Name: $xrv32i2p1_a2p1_f2p2_zicsr2p0_zaamo1p0_zalrsc1p0
+# CHECK-NEXT: Value: 0x0
+
+# .option rvc enables C; a new symbol is emitted before the next instruction.
+.option rvc
+nop
+# CHECK: Name: $xrv32i2p1_a2p1_f2p2_c2p0_zicsr2p0_zaamo1p0_zalrsc1p0_zca1p0_zcf1p0
+# CHECK-NEXT: Value: 0x4
+
+# .option norvc disables C; a new symbol without C is emitted.
+.option norvc
+nop
+# CHECK: Name: $xrv32i2p1_a2p1_f2p2_zicsr2p0_zaamo1p0_zalrsc1p0
+# CHECK-NEXT: Value: 0x6
+
+# .option push saves the current ISA; .option arch switches to a full ISA;
+# .option pop restores the pre-push ISA and emits a new symbol for it.
+.option push
+.option arch, rv32imac
+nop
+# CHECK: Name: $xrv32i2p1_m2p0_a2p1_c2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0
+# CHECK-NEXT: Value: 0xA
+
+.option pop
+nop
+# CHECK: Name: $xrv32i2p1_a2p1_f2p2_zicsr2p0_zaamo1p0_zalrsc1p0
+# CHECK-NEXT: Value: 0xC
+
+# Deduplication: repeating the same .option arch does not emit a second symbol.
+.option arch, rv32imac
+nop
+# CHECK: Name: $xrv32i2p1_m2p0_a2p1_c2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0
+# CHECK-NEXT: Value: 0x10
+.option arch, rv32imac
+nop
+# No additional symbol expected between the two instructions above.
+# CHECK-NOT: $xrv32i2p1_m2p0_a2p1_c2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0 {{.*}}Value: 0x1[^0]
+
+# Nested push/pop: walk down to an inner ISA via two pushes with distinct
+# .option arch in between, then walk back up with two pops. Each level must
+# emit its own mapping symbol so the stack is observable in the object.
+.option push
+.option arch, rv32im
+nop
+# CHECK: Name: $xrv32i2p1_m2p0_zmmul1p0
+# CHECK-NEXT: Value: 0x14
+
+.option push
+.option arch, rv32imc
+nop
+# CHECK: Name: $xrv32i2p1_m2p0_c2p0_zmmul1p0_zca1p0
+# CHECK-NEXT: Value: 0x18
+
+.option pop
+nop
+# CHECK: Name: $xrv32i2p1_m2p0_zmmul1p0
+# CHECK-NEXT: Value: 0x1A
+
+.option pop
+nop
+# CHECK: Name: $xrv32i2p1_m2p0_a2p1_c2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0
+# CHECK-NEXT: Value: 0x1E
diff --git a/llvm/test/MC/RISCV/mapping-isa-option-rv64.s b/llvm/test/MC/RISCV/mapping-isa-option-rv64.s
new file mode 100644
index 0000000000000..4ef7636c3df7e
--- /dev/null
+++ b/llvm/test/MC/RISCV/mapping-isa-option-rv64.s
@@ -0,0 +1,75 @@
+## Test ISA mapping symbol emission for .option rvc/norvc/arch/push/pop on rv64.
+## The assembler emits a "$x<ISAString>" symbol when the active ISA changes,
+## using lazy emission (the symbol appears before the first instruction after
+## the change) and deduplication (no symbol when the ISA is unchanged).
+## An initial "$x<ISAString>" symbol is also emitted before the very first
+## instruction to record the starting ISA in the object file.
+
+# RUN: llvm-mc -triple=riscv64 -mattr=+f,+a -filetype=obj -o %t.o %s
+# RUN: llvm-readobj --symbols %t.o | FileCheck %s
+
+.text
+nop
+# The initial ISA mapping symbol is emitted before the first instruction.
+# CHECK: Name: $xrv64i2p1_a2p1_f2p2_zicsr2p0_zaamo1p0_zalrsc1p0
+# CHECK-NEXT: Value: 0x0
+
+# .option rvc enables C; a new symbol is emitted before the next instruction.
+.option rvc
+nop
+# CHECK: Name: $xrv64i2p1_a2p1_f2p2_c2p0_zicsr2p0_zaamo1p0_zalrsc1p0_zca1p0
+# CHECK-NEXT: Value: 0x4
+
+# .option norvc disables C; a new symbol without C is emitted.
+.option norvc
+nop
+# CHECK: Name: $xrv64i2p1_a2p1_f2p2_zicsr2p0_zaamo1p0_zalrsc1p0
+# CHECK-NEXT: Value: 0x6
+
+# .option push saves the current ISA; .option arch switches to a full ISA;
+# .option pop restores the pre-push ISA and emits a new symbol for it.
+.option push
+.option arch, rv64imac
+nop
+# CHECK: Name: $xrv64i2p1_m2p0_a2p1_c2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0
+# CHECK-NEXT: Value: 0xA
+
+.option pop
+nop
+# CHECK: Name: $xrv64i2p1_a2p1_f2p2_zicsr2p0_zaamo1p0_zalrsc1p0
+# CHECK-NEXT: Value: 0xC
+
+# Deduplication: repeating the same .option arch does not emit a second symbol.
+.option arch, rv64imac
+nop
+# CHECK: Name: $xrv64i2p1_m2p0_a2p1_c2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0
+# CHECK-NEXT: Value: 0x10
+.option arch, rv64imac
+nop
+# No additional symbol expected between the two instructions above.
+# CHECK-NOT: $xrv64i2p1_m2p0_a2p1_c2p0_zmmul1p0_zaamo1p0_zalrsc1p0_zca1p0 {{.*}}Value: 0x1[^0]
+
+# Nested push/pop: walk down to an inner ISA via two pushes with distinct
+# .option arch in between, then walk back up with two pops. Each level must
+# emit its own mapping symbol so the stack is observable in the object.
+.option push
+.option arch, rv64im
+nop
+# CHECK: Name: $xrv64i2p1_m2p0_zmmul1p0
+# CHECK-NEXT: Value: 0x14
+
+.option push
+.option arch, rv64imc
+nop
+# CHECK: Name: $xrv64i2p1_m2p0_c2p0_zmmu...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/193123
More information about the llvm-commits
mailing list