//===- 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/MCContext.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/SectionKind.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MathExtras.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 = static_cast(S); MCObjectStreamer::emitLabel(Symbol, Loc); const MCSectionWasm &Section = static_cast(*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 = static_cast(S); MCObjectStreamer::emitLabelAtPos(Symbol, Loc, F, Offset); const MCSectionWasm &Section = static_cast(*getCurrentSectionOnly()); if (Section.getSegmentFlags() & wasm::WASM_SEG_FLAG_TLS) Symbol->setTLS(); } void MCWasmStreamer::emitAssignment(MCSymbol *S, const MCExpr *Value) { auto *Symbol = static_cast(S); // wasm has no absolute addressing. A symbol assigned a bare integer constant // ("sym = N" / ".set sym, N", with no symbol reference) cannot be modeled as // an absolute symbol: WasmObjectWriter's alias pass calls getBaseSymbol(), // which is null for a pure constant, and previously aborted MC finalization // with "absolute addressing not supported". glibc emits exactly this for // address-taken linkage markers (e.g. _nl_current_LC_*_used from // _NL_CURRENT_DEFINE in locale/localeinfo.h), where the symbol's *address* is // taken to force a translation unit to be linked, and any read of the symbol // is expected to yield the constant. // // Lower a named (non-temporary) absolute-constant symbol to a real defined // data object that holds the constant in its own data section. It then has a // genuine wasm linear-memory address that can be taken and relocated at link // (resolving an undefined reference in another object), and a load of it // yields the constant. This mirrors what GNU-as effectively provides for the // construct on ELF (a defined symbol with an address). It is never-silent: // the address is real, the value is preserved, and nothing is dropped. // // Assignments that reference other symbols (true aliases, label differences) // are left to the normal assembler-alias machinery (the WasmObjectWriter // alias pass and the prior alias-to-undefined / .symver fixes). Temporary, // assembler-internal absolute constants are also left as plain assignments: // they are folded into immediates at encode time and never need an address. int64_t Const; if (!Symbol->isTemporary() && Value->evaluateAsAbsolute(Const)) { MCContext &Ctx = getContext(); // Store the constant in the target pointer width, widened only if needed so // the stored bytes equal the constant exactly (no silent truncation). unsigned Width = Ctx.getTargetTriple().isArch64Bit() ? 8 : 4; while (Width < 8 && !isIntN(Width * 8, Const) && !isUIntN(Width * 8, static_cast(Const))) Width *= 2; // A dedicated data section so the marker is independently addressable and // garbage-collectable, exactly like any other defined data symbol. MCSection *DataSec = Ctx.getWasmSection(Twine(".data.") + Symbol->getName(), SectionKind::getData()); MCSection *Prev = getCurrentSectionOnly(); switchSection(DataSec); emitValueToAlignment(Align(Width)); emitLabel(Symbol); emitIntValue(static_cast(Const), Width); Symbol->setSize(MCConstantExpr::create(Width, Ctx)); // Record the constant so a repeated, identical "sym = N" (which glibc emits // per-TU and llvm-link concatenates without dedup) is recognized in the // parser as an idempotent no-op rather than colliding as a redefinition of // this now-defined data label, while a repeat to a *different* constant is // still rejected loudly. See MCParserUtils::parseAssignmentExpression. Symbol->setAbsoluteConstant(Const); if (Prev) switchSection(Prev); return; } MCObjectStreamer::emitAssignment(S, Value); } void MCWasmStreamer::changeSection(MCSection *Section, uint32_t Subsection) { MCAssembler &Asm = getAssembler(); auto *SectionWasm = static_cast(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 = static_cast(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) { static_cast(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(); this->MCObjectStreamer::finishImpl(); } MCStreamer *llvm::createWasmStreamer(MCContext &Context, std::unique_ptr &&MAB, std::unique_ptr &&OW, std::unique_ptr &&CE) { MCWasmStreamer *S = new MCWasmStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)); return S; }