[llvm] [Mips] Track ISA mode and distinguish ELF data labels (PR #228883)
Jiaxun Yang via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 4 05:41:37 PDT 2026
https://github.com/FlyGoat created https://github.com/llvm/llvm-project/pull/228883
Merge dependency: https://github.com/llvm/llvm-project/pull/228882 must land first so MIPS16 symbol marking can preserve `STO_MIPS_MIPS16` (0xf0) without an assertion or loss of bit 4. This PR contains only the ISA-mode commit.
Represent standard MIPS, microMIPS and MIPS16 with a shared ISA mode in
the ELF target streamer. Initialize it from the subtarget, save and
restore it across .set push/pop, and reset it for .set mips0. Disabling
one compressed ISA must not clear the other ISA's active mode. Keep the
assembler's feature bits consistent when switching between them.
Use the mode to mark function and pending instruction labels, propagate
it to aliases, and set the MIPS16 ELF flag for command-line MIPS16 mode.
Clear pending code labels when emitting byte strings or fill directives,
as already done for integer data, so data does not inherit the following
instruction's mode.
Extend the existing label and .ent tests with command-line microMIPS
mode, nested mode changes, initial-mode resets, and data emission.
Assisted-by: OpenAI Codex # Testcases
Validation: `git diff --check` passes. Tests were not rerun for this cherry-pick.
>From c760ee88878c364d169377c4df62fb1228b93d79 Mon Sep 17 00:00:00 2001
From: Jiaxun Yang <jiaxun.yang at flygoat.com>
Date: Sun, 4 Oct 2026 12:23:42 +0100
Subject: [PATCH] [Mips] Track ISA mode and distinguish ELF data labels
Represent standard MIPS, microMIPS and MIPS16 with a shared ISA mode in
the ELF target streamer. Initialize it from the subtarget, save and
restore it across .set push/pop, and reset it for .set mips0. Disabling
one compressed ISA must not clear the other ISA's active mode. Keep the
assembler's feature bits consistent when switching between them.
Use the mode to mark function and pending instruction labels, propagate
it to aliases, and set the MIPS16 ELF flag for command-line MIPS16 mode.
Clear pending code labels when emitting byte strings or fill directives,
as already done for integer data, so data does not inherit the following
instruction's mode.
Extend the existing label and .ent tests with command-line microMIPS
mode, nested mode changes, initial-mode resets, and data emission.
Assisted-by: OpenAI Codex # Testcases
---
.../Target/Mips/AsmParser/MipsAsmParser.cpp | 2 +
.../Mips/MCTargetDesc/MipsELFStreamer.cpp | 25 ++++-
.../Mips/MCTargetDesc/MipsELFStreamer.h | 6 +
.../Mips/MCTargetDesc/MipsTargetStreamer.cpp | 54 +++++++--
.../Mips/MCTargetDesc/MipsTargetStreamer.h | 14 ++-
llvm/test/MC/Mips/directive-ent.s | 10 +-
llvm/test/MC/Mips/micromips-label-test.s | 103 +++++++++++++++++-
7 files changed, 197 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
index 48163a54f4837..5c8568c605e7b 100644
--- a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
+++ b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
@@ -7163,6 +7163,7 @@ bool MipsAsmParser::parseSetMips16Directive() {
return false;
}
+ clearFeatureBits(Mips::FeatureMicroMips, "micromips");
setFeatureBits(Mips::FeatureMips16, "mips16");
getTargetStreamer().emitDirectiveSetMips16();
Parser.Lex(); // Consume the EndOfStatement.
@@ -7515,6 +7516,7 @@ bool MipsAsmParser::parseSetFeature(uint64_t Feature) {
getTargetStreamer().emitDirectiveSetDspr2();
break;
case Mips::FeatureMicroMips:
+ clearFeatureBits(Mips::FeatureMips16, "mips16");
setFeatureBits(Mips::FeatureMicroMips, "micromips");
getTargetStreamer().emitDirectiveSetMicroMips();
break;
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.cpp b/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.cpp
index a8b7c9e57470d..bd27c2d437c9f 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.cpp
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.cpp
@@ -72,12 +72,14 @@ void MipsELFStreamer::createPendingLabelRelocs() {
MipsTargetELFStreamer *ELFTargetStreamer =
static_cast<MipsTargetELFStreamer *>(getTargetStreamer());
- // FIXME: Also mark labels when in MIPS16 mode.
- if (ELFTargetStreamer->isMicroMipsEnabled()) {
+ if (ELFTargetStreamer->isMicroMipsEnabled() ||
+ ELFTargetStreamer->isMips16Enabled()) {
for (auto *L : Labels) {
auto *Label = static_cast<MCSymbolELF *>(L);
getAssembler().registerSymbol(*Label);
- Label->setOther(ELF::STO_MIPS_MICROMIPS);
+ Label->setOther(ELFTargetStreamer->isMips16Enabled()
+ ? ELF::STO_MIPS_MIPS16
+ : ELF::STO_MIPS_MICROMIPS);
}
}
@@ -105,6 +107,23 @@ void MipsELFStreamer::emitIntValue(uint64_t Value, unsigned Size) {
Labels.clear();
}
+void MipsELFStreamer::emitBytes(StringRef Data) {
+ MCELFStreamer::emitBytes(Data);
+ Labels.clear();
+}
+
+void MipsELFStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue,
+ SMLoc Loc) {
+ MCELFStreamer::emitFill(NumBytes, FillValue, Loc);
+ Labels.clear();
+}
+
+void MipsELFStreamer::emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr,
+ SMLoc Loc) {
+ MCELFStreamer::emitFill(NumValues, Size, Expr, Loc);
+ Labels.clear();
+}
+
void MipsELFStreamer::EmitMipsOptionRecords() {
for (const auto &I : MipsOptionRecords)
I->EmitMipsOptionRecord();
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.h b/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.h
index 1e8042e88c9e3..4a34d455d40aa 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.h
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsELFStreamer.h
@@ -57,6 +57,12 @@ class MipsELFStreamer : public MCELFStreamer {
/// directives are emitted.
void emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) override;
void emitIntValue(uint64_t Value, unsigned Size) override;
+ void emitBytes(StringRef Data) override;
+ using MCELFStreamer::emitFill;
+ void emitFill(const MCExpr &NumBytes, uint64_t FillValue,
+ SMLoc Loc = SMLoc()) override;
+ void emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr,
+ SMLoc Loc = SMLoc()) override;
// Overriding these functions allows us to avoid recording of these labels
// in EmitLabel and later marking them as microMIPS.
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
index aef38feff4736..2c9cfe8656b05 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
@@ -861,9 +861,18 @@ void MipsTargetAsmStreamer::emitDirectiveModuleNoGINV() {
}
// This part is for ELF object output.
+MipsTargetELFStreamer::ISAMode
+MipsTargetELFStreamer::getISAMode(const MCSubtargetInfo &STI) {
+ if (STI.hasFeature(Mips::FeatureMips16))
+ return ISAMode::Mips16;
+ if (STI.hasFeature(Mips::FeatureMicroMips))
+ return ISAMode::MicroMips;
+ return ISAMode::Standard;
+}
+
MipsTargetELFStreamer::MipsTargetELFStreamer(MCStreamer &S,
const MCSubtargetInfo &STI)
- : MipsTargetStreamer(S), MicroMipsEnabled(false), STI(STI) {
+ : MipsTargetStreamer(S), Mode(getISAMode(STI)), STI(STI) {
MCAssembler &MCA = getStreamer().getAssembler();
ELFObjectWriter &W = getStreamer().getWriter();
@@ -937,6 +946,8 @@ MipsTargetELFStreamer::MipsTargetELFStreamer(MCStreamer &S,
EFlags |= ELF::EF_MIPS_MACH_5900;
// Other options.
+ if (isMips16Enabled())
+ EFlags |= ELF::EF_MIPS_ARCH_ASE_M16;
if (Features[Mips::FeatureNaN2008])
EFlags |= ELF::EF_MIPS_NAN2008;
@@ -952,6 +963,8 @@ void MipsTargetELFStreamer::emitLabel(MCSymbol *S) {
if (isMicroMipsEnabled())
Symbol->setOther(ELF::STO_MIPS_MICROMIPS);
+ else if (isMips16Enabled())
+ Symbol->setOther(ELF::STO_MIPS_MIPS16);
}
void MipsTargetELFStreamer::finish() {
@@ -1029,16 +1042,17 @@ void MipsTargetELFStreamer::finish() {
void MipsTargetELFStreamer::emitAssignment(MCSymbol *S, const MCExpr *Value) {
auto *Symbol = static_cast<MCSymbolELF *>(S);
- // If on rhs is micromips symbol then mark Symbol as microMips.
+ // Propagate the ISA mode of a symbol to its alias.
if (Value->getKind() != MCExpr::SymbolRef)
return;
auto &RhsSym = static_cast<const MCSymbolELF &>(
static_cast<const MCSymbolRefExpr *>(Value)->getSymbol());
- if (!(RhsSym.getOther() & ELF::STO_MIPS_MICROMIPS))
- return;
-
- Symbol->setOther(ELF::STO_MIPS_MICROMIPS);
+ unsigned Other = RhsSym.getOther();
+ if ((Other & ELF::STO_MIPS_MIPS16) == ELF::STO_MIPS_MIPS16)
+ Symbol->setOther(ELF::STO_MIPS_MIPS16);
+ else if (Other & ELF::STO_MIPS_MICROMIPS)
+ Symbol->setOther(ELF::STO_MIPS_MICROMIPS);
}
MCELFStreamer &MipsTargetELFStreamer::getStreamer() {
@@ -1089,15 +1103,32 @@ void MipsTargetELFStreamer::emitTPRel64Value(const MCExpr *Value) {
}
void MipsTargetELFStreamer::emitDirectiveSetMicroMips() {
- MicroMipsEnabled = true;
+ Mode = ISAMode::MicroMips;
forbidModuleDirective();
}
void MipsTargetELFStreamer::emitDirectiveSetNoMicroMips() {
- MicroMipsEnabled = false;
+ if (isMicroMipsEnabled())
+ Mode = ISAMode::Standard;
forbidModuleDirective();
}
+void MipsTargetELFStreamer::emitDirectiveSetPush() {
+ ModeStack.push_back(Mode);
+ MipsTargetStreamer::emitDirectiveSetPush();
+}
+
+void MipsTargetELFStreamer::emitDirectiveSetPop() {
+ assert(!ModeStack.empty() && "unmatched .set pop");
+ Mode = ModeStack.pop_back_val();
+ MipsTargetStreamer::emitDirectiveSetPop();
+}
+
+void MipsTargetELFStreamer::emitDirectiveSetMips0() {
+ Mode = getISAMode(STI);
+ MipsTargetStreamer::emitDirectiveSetMips0();
+}
+
void MipsTargetELFStreamer::setUsesMicroMips() {
ELFObjectWriter &W = getStreamer().getWriter();
unsigned Flags = W.getELFHeaderEFlags();
@@ -1106,6 +1137,7 @@ void MipsTargetELFStreamer::setUsesMicroMips() {
}
void MipsTargetELFStreamer::emitDirectiveSetMips16() {
+ Mode = ISAMode::Mips16;
ELFObjectWriter &W = getStreamer().getWriter();
unsigned Flags = W.getELFHeaderEFlags();
Flags |= ELF::EF_MIPS_ARCH_ASE_M16;
@@ -1113,6 +1145,12 @@ void MipsTargetELFStreamer::emitDirectiveSetMips16() {
forbidModuleDirective();
}
+void MipsTargetELFStreamer::emitDirectiveSetNoMips16() {
+ if (isMips16Enabled())
+ Mode = ISAMode::Standard;
+ MipsTargetStreamer::emitDirectiveSetNoMips16();
+}
+
void MipsTargetELFStreamer::emitDirectiveSetNoReorder() {
ELFObjectWriter &W = getStreamer().getWriter();
unsigned Flags = W.getELFHeaderEFlags();
diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.h b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.h
index bb540f3a1da0e..dd57315c721f9 100644
--- a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.h
+++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.h
@@ -12,6 +12,7 @@
#include "MCTargetDesc/MipsABIFlagsSection.h"
#include "MCTargetDesc/MipsABIInfo.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCELFStreamer.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
@@ -331,12 +332,17 @@ class MipsTargetAsmStreamer : public MipsTargetStreamer {
// This part is for ELF object output
class MipsTargetELFStreamer : public MipsTargetStreamer {
- bool MicroMipsEnabled;
+ enum class ISAMode { Standard, MicroMips, Mips16 };
+ static ISAMode getISAMode(const MCSubtargetInfo &STI);
+
+ ISAMode Mode;
+ SmallVector<ISAMode, 4> ModeStack;
const MCSubtargetInfo &STI;
bool Pic;
public:
- bool isMicroMipsEnabled() const { return MicroMipsEnabled; }
+ bool isMicroMipsEnabled() const { return Mode == ISAMode::MicroMips; }
+ bool isMips16Enabled() const { return Mode == ISAMode::Mips16; }
MCELFStreamer &getStreamer();
MipsTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI);
@@ -355,8 +361,12 @@ class MipsTargetELFStreamer : public MipsTargetStreamer {
void emitDirectiveSetMicroMips() override;
void emitDirectiveSetNoMicroMips() override;
+ void emitDirectiveSetPush() override;
+ void emitDirectiveSetPop() override;
+ void emitDirectiveSetMips0() override;
void setUsesMicroMips() override;
void emitDirectiveSetMips16() override;
+ void emitDirectiveSetNoMips16() override;
void emitDirectiveSetNoReorder() override;
void emitDirectiveEnd(StringRef Name) override;
diff --git a/llvm/test/MC/Mips/directive-ent.s b/llvm/test/MC/Mips/directive-ent.s
index c5166882c6d34..7621c4682e034 100644
--- a/llvm/test/MC/Mips/directive-ent.s
+++ b/llvm/test/MC/Mips/directive-ent.s
@@ -25,7 +25,10 @@ a:
# OBJ: Size: 0
# OBJ: Binding: Local
# OBJ: Type: Function
-# OBJ: Other: 0
+# OBJ-32: Other: 0
+# OBJ-MM: Other [ (0x80)
+# OBJ-MM: STO_MIPS_MICROMIPS
+# OBJ-MM: ]
# OBJ: Section: .text
# OBJ: }
@@ -45,6 +48,9 @@ b:
# OBJ-MM: Size: 8
# OBJ: Binding: Local
# OBJ: Type: Function
-# OBJ: Other: 0
+# OBJ-32: Other: 0
+# OBJ-MM: Other [ (0x80)
+# OBJ-MM: STO_MIPS_MICROMIPS
+# OBJ-MM: ]
# OBJ: Section: .text
# OBJ: }
diff --git a/llvm/test/MC/Mips/micromips-label-test.s b/llvm/test/MC/Mips/micromips-label-test.s
index 8224398e67235..11cdd61d65b78 100644
--- a/llvm/test/MC/Mips/micromips-label-test.s
+++ b/llvm/test/MC/Mips/micromips-label-test.s
@@ -1,5 +1,41 @@
-# RUN: llvm-mc %s -triple=mipsel-unknown-linux -mcpu=mips32r2 \
-# RUN: -mattr=+micromips -filetype=obj -o - | llvm-readobj --symbols - | FileCheck %s
+# RUN: llvm-mc %s -triple=mipsel -mcpu=mips32r2 -mattr=+micromips -filetype=obj -o %t.o
+# RUN: llvm-readobj --symbols %t.o | FileCheck %s --check-prefix=CHECK
+# RUN: llvm-readobj --symbols %t.o | FileCheck %s --check-prefix=INITIAL
+# RUN: llvm-readelf -s %t.o | FileCheck %s --check-prefix=MODE
+# RUN: llvm-mc %s -triple=mips -mcpu=mips32r2 -mattr=+micromips -filetype=obj -o %t.o
+# RUN: llvm-readobj --symbols %t.o | FileCheck %s --check-prefix=CHECK
+# RUN: llvm-readobj --symbols %t.o | FileCheck %s --check-prefix=INITIAL
+# RUN: llvm-readelf -s %t.o | FileCheck %s --check-prefix=MODE
+
+## Command-line microMIPS mode must mark code symbols even without a
+## .set micromips directive. Data labels must remain unmarked.
+.globl function, code, data, initial_normal
+.type function, at function
+function:
+ nop
+code:
+ nop
+data:
+ .word 0
+.set nomicromips
+.type initial_normal, at function
+initial_normal:
+ nop
+
+# INITIAL: Name: function
+# INITIAL: Type: Function
+# INITIAL: Other [ (0x80)
+# INITIAL-NEXT: STO_MIPS_MICROMIPS
+# INITIAL: Name: code
+# INITIAL: Type: None
+# INITIAL: Other [ (0x80)
+# INITIAL-NEXT: STO_MIPS_MICROMIPS
+# INITIAL: Name: data
+# INITIAL: Other: 0
+# INITIAL: Name: initial_normal
+# INITIAL: Type: Function
+# INITIAL: Other: 0
+
.text
.set nomicromips
f:
@@ -100,3 +136,66 @@ j:
# CHECK-NEXT: Section: .text
# CHECK-NEXT: }
# CHECK: ]
+
+.set micromips
+.set noreorder
+.text
+.globl initial, normal, nested, restored_normal, restored_micro, reset
+initial:
+ nop
+.set push
+.set nomicromips
+normal:
+ nop
+.set push
+.set micromips
+nested:
+ nop
+.set pop
+restored_normal:
+ nop
+.set pop
+restored_micro:
+ nop
+.set nomicromips
+.set mips0
+reset:
+ nop
+
+# MODE-DAG: NOTYPE GLOBAL DEFAULT [<other: 0x80>] {{[0-9]+}} initial
+# MODE-DAG: NOTYPE GLOBAL DEFAULT {{[0-9]+}} normal
+# MODE-DAG: NOTYPE GLOBAL DEFAULT [<other: 0x80>] {{[0-9]+}} nested
+# MODE-DAG: NOTYPE GLOBAL DEFAULT {{[0-9]+}} restored_normal
+# MODE-DAG: NOTYPE GLOBAL DEFAULT [<other: 0x80>] {{[0-9]+}} restored_micro
+# MODE-DAG: NOTYPE GLOBAL DEFAULT [<other: 0x80>] {{[0-9]+}} reset
+
+# Data in an executable section must not inherit the next instruction's mode.
+.globl bytes, space, fill, word, after_data, before_section, in_section
+bytes:
+ .ascii "data"
+ nop
+space:
+ .space 4
+ nop
+fill:
+ .fill 2, 2, 0
+ nop
+word:
+ .word 0
+ nop
+after_data:
+ nop
+before_section:
+ .pushsection .rodata
+ .word 0
+ .popsection
+in_section:
+ nop
+
+# MODE-DAG: NOTYPE GLOBAL DEFAULT {{[0-9]+}} bytes
+# MODE-DAG: NOTYPE GLOBAL DEFAULT {{[0-9]+}} space
+# MODE-DAG: NOTYPE GLOBAL DEFAULT {{[0-9]+}} fill
+# MODE-DAG: NOTYPE GLOBAL DEFAULT {{[0-9]+}} word
+# MODE-DAG: NOTYPE GLOBAL DEFAULT [<other: 0x80>] {{[0-9]+}} after_data
+# MODE-DAG: NOTYPE GLOBAL DEFAULT {{[0-9]+}} before_section
+# MODE-DAG: NOTYPE GLOBAL DEFAULT [<other: 0x80>] {{[0-9]+}} in_section
More information about the llvm-commits
mailing list