diff --git a/llvm/include/llvm/MC/MCAsmBackend.h b/llvm/include/llvm/MC/MCAsmBackend.h --- a/llvm/include/llvm/MC/MCAsmBackend.h +++ b/llvm/include/llvm/MC/MCAsmBackend.h @@ -41,7 +41,8 @@ /// Generic interface to target specific assembler backends. class MCAsmBackend { protected: // Can only create subclasses. - MCAsmBackend(support::endianness Endian); + MCAsmBackend(support::endianness Endian, + unsigned RelaxFixupKind = MaxFixupKind); public: MCAsmBackend(const MCAsmBackend &) = delete; @@ -50,6 +51,9 @@ const support::endianness Endian; + /// Fixup kind used for linker relaxation. Currently only used by RISC-V. + const unsigned RelaxFixupKind; + /// Return true if this target might automatically pad instructions and thus /// need to emit padding enable/disable directives around sensative code. virtual bool allowAutoPadding() const { return false; } diff --git a/llvm/include/llvm/MC/MCFragment.h b/llvm/include/llvm/MC/MCFragment.h --- a/llvm/include/llvm/MC/MCFragment.h +++ b/llvm/include/llvm/MC/MCFragment.h @@ -75,6 +75,7 @@ protected: bool HasInstructions; + bool LinkerRelaxable = false; MCFragment(FragmentType Kind, bool HasInstructions, MCSection *Parent = nullptr); @@ -246,6 +247,9 @@ static bool classof(const MCFragment *F) { return F->getKind() == MCFragment::FT_Data; } + + bool isLinkerRelaxable() const { return LinkerRelaxable; } + void setLinkerRelaxable() { LinkerRelaxable = true; } }; /// This is a compact (memory-size-wise) fragment for holding an encoded diff --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp --- a/llvm/lib/MC/MCAsmBackend.cpp +++ b/llvm/lib/MC/MCAsmBackend.cpp @@ -22,7 +22,8 @@ using namespace llvm; -MCAsmBackend::MCAsmBackend(support::endianness Endian) : Endian(Endian) {} +MCAsmBackend::MCAsmBackend(support::endianness Endian, unsigned RelaxFixupKind) + : Endian(Endian), RelaxFixupKind(RelaxFixupKind) {} MCAsmBackend::~MCAsmBackend() = default; diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -628,6 +628,9 @@ } DF->setHasInstructions(STI); + if (!Fixups.empty() && Fixups.back().getTargetKind() == + getAssembler().getBackend().RelaxFixupKind) + DF->setLinkerRelaxable(); DF->getContents().append(Code.begin(), Code.end()); if (Assembler.isBundlingEnabled() && Assembler.getRelaxAll()) { diff --git a/llvm/lib/MC/MCExpr.cpp b/llvm/lib/MC/MCExpr.cpp --- a/llvm/lib/MC/MCExpr.cpp +++ b/llvm/lib/MC/MCExpr.cpp @@ -659,7 +659,6 @@ // Try to find a constant displacement from FA to FB, add the displacement // between the offset in FA of SA and the offset in FB of SB. - int64_t Displacement = SA.getOffset() - SB.getOffset(); bool Reverse = false; if (FA == FB) { Reverse = SA.getOffset() < SB.getOffset(); @@ -667,14 +666,31 @@ Reverse = std::find_if(std::next(FA->getIterator()), SecA.end(), [&](auto &I) { return &I == FB; }) != SecA.end(); } + + uint64_t SAOffset = SA.getOffset(), SBOffset = SB.getOffset(); + int64_t Displacement = SA.getOffset() - SB.getOffset(); if (Reverse) { std::swap(FA, FB); + std::swap(SAOffset, SBOffset); Displacement *= -1; } [[maybe_unused]] bool Found = false; + // Track whether B is before a relaxable instruction and whether A is after + // a relaxable instruction. If SA and SB are separated by a linker-relaxable + // instruction, the difference cannot be resolved as it may be changed by + // the linker. + bool BBeforeRelax = false, AAfterRelax = false; for (auto FI = FB->getIterator(), FE = SecA.end(); FI != FE; ++FI) { auto DF = dyn_cast(FI); + if (DF && DF->isLinkerRelaxable()) { + if (&*FI != FB || SBOffset != DF->getContents().size()) + BBeforeRelax = true; + if (&*FI != FA || SAOffset == DF->getContents().size()) + AAfterRelax = true; + if (BBeforeRelax && AAfterRelax) + return; + } if (&*FI == FA) { Found = true; break; diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -211,6 +211,11 @@ const MCSubtargetInfo *STI) { if (!F.hasInstructions()) return true; + // Do not add data after a linker-relaxable instruction. The difference + // between a new label and a label at or before the linker-relaxable + // instruction cannot be resolved at assemble-time. + if (F.isLinkerRelaxable()) + return false; // When bundling is enabled, we don't want to add data to a fragment that // already has instructions (see MCELFStreamer::emitInstToData for details) if (Assembler.isBundlingEnabled()) diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h @@ -31,8 +31,8 @@ public: RISCVAsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI, bool Is64Bit, const MCTargetOptions &Options) - : MCAsmBackend(support::little), STI(STI), OSABI(OSABI), Is64Bit(Is64Bit), - TargetOptions(Options) { + : MCAsmBackend(support::little, RISCV::fixup_riscv_relax), STI(STI), + OSABI(OSABI), Is64Bit(Is64Bit), TargetOptions(Options) { RISCVFeatures::validate(STI.getTargetTriple(), STI.getFeatureBits()); } ~RISCVAsmBackend() override = default; diff --git a/llvm/test/MC/ELF/RISCV/subsection.s b/llvm/test/MC/ELF/RISCV/subsection.s --- a/llvm/test/MC/ELF/RISCV/subsection.s +++ b/llvm/test/MC/ELF/RISCV/subsection.s @@ -1,5 +1,5 @@ -# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error: -# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR --implicit-check-not=error: +# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=-relax %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR,NORELAX --implicit-check-not=error: +# RUN: not llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o /dev/null 2>&1 | FileCheck %s --check-prefixes=ERR,RELAX --implicit-check-not=error: a: nop @@ -11,21 +11,28 @@ .data ## Positive subsection numbers +## With relaxation, report an error as c-b is not an assemble-time constant. +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number .subsection c-b +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number .subsection d-b +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number .subsection c-a .subsection b-a .subsection d-c ## Negative subsection numbers -# ERR: :[[#@LINE+1]]:14: error: subsection number -8 is not within [0,2147483647] +# NORELAX: :[[#@LINE+2]]:14: error: subsection number -8 is not within [0,2147483647] +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number .subsection b-c -# ERR: :[[#@LINE+1]]:14: error: subsection number -12 is not within [0,2147483647] +# NORELAX: :[[#@LINE+2]]:14: error: subsection number -12 is not within [0,2147483647] +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number .subsection b-d -# ERR: :[[#@LINE+1]]:14: error: subsection number -12 is not within [0,2147483647] +# NORELAX: :[[#@LINE+2]]:14: error: subsection number -12 is not within [0,2147483647] +# RELAX: :[[#@LINE+1]]:14: error: cannot evaluate subsection number .subsection a-c -# ERR: :[[#@LINE+1]]:14: error: subsection number -4 is not within [0,2147483647] +# ERR: :[[#@LINE+1]]:14: error: subsection number -4 is not within [0,2147483647] .subsection a-b -# ERR: :[[#@LINE+1]]:14: error: subsection number -4 is not within [0,2147483647] +# ERR: :[[#@LINE+1]]:14: error: subsection number -4 is not within [0,2147483647] .subsection c-d