llvm-project/llvm/lib/MC/MCWasmStreamer.cpp
Fangrui Song dc3a4c0fcf
MC: Restructure MCFragment as a fixed part and a variable tail
Refactor the fragment representation of `push rax; jmp foo; nop; jmp foo`,
previously encoded as
`MCDataFragment(nop); MCRelaxableFragment(jmp foo); MCDataFragment(nop); MCRelaxableFragment(jmp foo)`,

to

```
MCFragment(fixed: push rax, variable: jmp foo)
MCFragment(fixed: nop, variable: jmp foo)
```

Changes:

* Eliminate MCEncodedFragment, moving content and fixup storage to MCFragment.
* The new MCFragment contains a fixed-size content (similar to previous
  MCDataFragment) and an optional variable-size tail.
* The variable-size tail supports FT_Relaxable, FT_LEB, FT_Dwarf, and
  FT_DwarfFrame, with plans to extend to other fragment types.
  dyn_cast/isa should be avoided for the converted fragment subclasses.
* In `setVarFixups`, source fixup offsets are relative to the variable part's start.
  Stored fixup (in `FixupStorage`) offsets are relative to the fixed part's start.
  A lot of code does `getFragmentOffset(Frag) + Fixup.getOffset()`,
  expecting the fixup offset to be relative to the fixed part's start.
* HexagonAsmBackend::fixupNeedsRelaxationAdvanced needs to know the
  associated instruction for a fixup. We have to add a `const MCFragment &` parameter.
* In MCObjectStreamer, extend `absoluteSymbolDiff` to apply to
  FT_Relaxable as otherwise there would be many more FT_DwarfFrame
  fragments in -g compilations.

https://llvm-compile-time-tracker.com/compare.php?from=28e1473e8e523150914e8c7ea50b44fb0d2a8d65&to=778d68ad1d48e7f111ea853dd249912c601bee89&stat=instructions:u

```
stage2-O0-g instructins:u geomeon (-0.07%)
stage1-ReleaseLTO-g (link only) max-rss geomean (-0.39%)
```

```
% /t/clang-old -g -c sqlite3.i -w -mllvm -debug-only=mc-dump &| awk '/^[0-9]+/{s[$2]++;tot++} END{print "Total",tot; n=asorti(s, si); for(i=1;i<=n;i++) print si[i],s[si[i]]}'
Total 59675
Align 2215
Data 29700
Dwarf 12044
DwarfCallFrame 4216
Fill 92
LEB 12
Relaxable 11396
% /t/clang-new -g -c sqlite3.i -w -mllvm -debug-only=mc-dump &| awk '/^[0-9]+/{s[$2]++;tot++} END{print "Total",tot; n=asorti(s, si); for(i=1;i<=n;i++) print si[i],s[si[i]]}'
Total 32287
Align 2215
Data 2312
Dwarf 12044
DwarfCallFrame 4216
Fill 92
LEB 12
Relaxable 11396
```

Pull Request: https://github.com/llvm/llvm-project/pull/148544
2025-07-15 21:56:55 -07:00

165 lines
4.8 KiB
C++

//===- lib/MC/MCWasmStreamer.cpp - Wasm Object Output ---------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file assembles .s files and emits Wasm .o object files.
//
//===----------------------------------------------------------------------===//
#include "llvm/MC/MCWasmStreamer.h"
#include "llvm/MC/MCAsmBackend.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCCodeEmitter.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCFixup.h"
#include "llvm/MC/MCObjectStreamer.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSectionWasm.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCSymbolWasm.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
namespace llvm {
class MCContext;
class MCStreamer;
class MCSubtargetInfo;
} // namespace llvm
using namespace llvm;
MCWasmStreamer::~MCWasmStreamer() = default; // anchor.
void MCWasmStreamer::emitLabel(MCSymbol *S, SMLoc Loc) {
auto *Symbol = cast<MCSymbolWasm>(S);
MCObjectStreamer::emitLabel(Symbol, Loc);
const MCSectionWasm &Section =
static_cast<const MCSectionWasm &>(*getCurrentSectionOnly());
if (Section.getSegmentFlags() & wasm::WASM_SEG_FLAG_TLS)
Symbol->setTLS();
}
void MCWasmStreamer::emitLabelAtPos(MCSymbol *S, SMLoc Loc, MCFragment &F,
uint64_t Offset) {
auto *Symbol = cast<MCSymbolWasm>(S);
MCObjectStreamer::emitLabelAtPos(Symbol, Loc, F, Offset);
const MCSectionWasm &Section =
static_cast<const MCSectionWasm &>(*getCurrentSectionOnly());
if (Section.getSegmentFlags() & wasm::WASM_SEG_FLAG_TLS)
Symbol->setTLS();
}
void MCWasmStreamer::changeSection(MCSection *Section, uint32_t Subsection) {
MCAssembler &Asm = getAssembler();
auto *SectionWasm = cast<MCSectionWasm>(Section);
const MCSymbol *Grp = SectionWasm->getGroup();
if (Grp)
Asm.registerSymbol(*Grp);
this->MCObjectStreamer::changeSection(Section, Subsection);
Asm.registerSymbol(*Section->getBeginSymbol());
}
bool MCWasmStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) {
assert(Attribute != MCSA_IndirectSymbol && "indirect symbols not supported");
auto *Symbol = cast<MCSymbolWasm>(S);
// Adding a symbol attribute always introduces the symbol; note that an
// important side effect of calling registerSymbol here is to register the
// symbol with the assembler.
getAssembler().registerSymbol(*Symbol);
switch (Attribute) {
case MCSA_LazyReference:
case MCSA_Reference:
case MCSA_SymbolResolver:
case MCSA_PrivateExtern:
case MCSA_WeakDefinition:
case MCSA_WeakDefAutoPrivate:
case MCSA_Invalid:
case MCSA_IndirectSymbol:
case MCSA_Protected:
case MCSA_Exported:
return false;
case MCSA_Hidden:
Symbol->setHidden(true);
break;
case MCSA_Weak:
case MCSA_WeakReference:
Symbol->setWeak(true);
Symbol->setExternal(true);
break;
case MCSA_Global:
Symbol->setExternal(true);
break;
case MCSA_ELF_TypeFunction:
Symbol->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
break;
case MCSA_ELF_TypeTLS:
Symbol->setTLS();
break;
case MCSA_ELF_TypeObject:
case MCSA_Cold:
break;
case MCSA_NoDeadStrip:
Symbol->setNoStrip();
break;
default:
// unrecognized directive
llvm_unreachable("unexpected MCSymbolAttr");
return false;
}
return true;
}
void MCWasmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size,
Align ByteAlignment) {
llvm_unreachable("Common symbols are not yet implemented for Wasm");
}
void MCWasmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) {
cast<MCSymbolWasm>(Symbol)->setSize(Value);
}
void MCWasmStreamer::emitLocalCommonSymbol(MCSymbol *S, uint64_t Size,
Align ByteAlignment) {
llvm_unreachable("Local common symbols are not yet implemented for Wasm");
}
void MCWasmStreamer::emitIdent(StringRef IdentString) {
// TODO(sbc): Add the ident section once we support mergable strings
// sections in the object format
}
void MCWasmStreamer::finishImpl() {
emitFrames(nullptr);
this->MCObjectStreamer::finishImpl();
}
MCStreamer *llvm::createWasmStreamer(MCContext &Context,
std::unique_ptr<MCAsmBackend> &&MAB,
std::unique_ptr<MCObjectWriter> &&OW,
std::unique_ptr<MCCodeEmitter> &&CE) {
MCWasmStreamer *S =
new MCWasmStreamer(Context, std::move(MAB), std::move(OW), std::move(CE));
return S;
}