diff options
| -rw-r--r-- | llvm/include/llvm/MC/MCAsmStreamer.h (renamed from llvm/include/llvm/MC/MCAsmBaseStreamer.h) | 16 | ||||
| -rw-r--r-- | llvm/lib/MC/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | llvm/lib/MC/MCAsmBaseStreamer.cpp | 143 | ||||
| -rw-r--r-- | llvm/lib/MC/MCAsmStreamer.cpp | 2652 | ||||
| -rw-r--r-- | llvm/lib/MC/MCGNUAsmStreamer.cpp | 2605 | ||||
| -rw-r--r-- | llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h | 6 |
6 files changed, 2713 insertions, 2711 deletions
diff --git a/llvm/include/llvm/MC/MCAsmBaseStreamer.h b/llvm/include/llvm/MC/MCAsmStreamer.h index f034c95..1acad32 100644 --- a/llvm/include/llvm/MC/MCAsmBaseStreamer.h +++ b/llvm/include/llvm/MC/MCAsmStreamer.h @@ -1,4 +1,4 @@ -//===- MCAsmBaseStreamer.h - Base Class for Asm Streamers -------*- C++ -*-===// +//===- MCAsmStreamer.h - Base Class for Asm Streamers -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,13 +6,13 @@ // //===----------------------------------------------------------------------===// // -// This file declares the MCAsmBaseStreamer class, a base class for streamers +// This file declares the MCAsmStreamer class, a base class for streamers // which emits assembly text. // //===----------------------------------------------------------------------===// -#ifndef LLVM_MC_MCASMBASESTREAMER_H -#define LLVM_MC_MCASMBASESTREAMER_H +#ifndef LLVM_MC_MCASMSTREAMER_H +#define LLVM_MC_MCASMSTREAMER_H #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" @@ -28,15 +28,15 @@ class MCContext; class MCInst; class MCSubtargetInfo; -class MCAsmBaseStreamer : public MCStreamer { +class MCAsmStreamer : public MCStreamer { protected: std::unique_ptr<MCAssembler> Assembler; SmallString<128> CommentToEmit; raw_svector_ostream CommentStream; raw_null_ostream NullStream; - MCAsmBaseStreamer(MCContext &Context, std::unique_ptr<MCCodeEmitter> Emitter, - std::unique_ptr<MCAsmBackend> AsmBackend); + MCAsmStreamer(MCContext &Context, std::unique_ptr<MCCodeEmitter> Emitter, + std::unique_ptr<MCAsmBackend> AsmBackend); public: /// Return a raw_ostream that comments can be written to. @@ -58,4 +58,4 @@ public: } // end namespace llvm -#endif // LLVM_MC_MCASMBASESTREAMER_H +#endif // LLVM_MC_MCASMSTREAMER_H diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt index d42b735..653962f 100644 --- a/llvm/lib/MC/CMakeLists.txt +++ b/llvm/lib/MC/CMakeLists.txt @@ -5,7 +5,6 @@ add_llvm_component_library(LLVMMC ELFObjectWriter.cpp GOFFObjectWriter.cpp MCAsmBackend.cpp - MCAsmBaseStreamer.cpp MCAsmInfo.cpp MCAsmInfoCOFF.cpp MCAsmInfoDarwin.cpp @@ -26,6 +25,7 @@ add_llvm_component_library(LLVMMC MCELFStreamer.cpp MCExpr.cpp MCFragment.cpp + MCGNUAsmStreamer.cpp MCGOFFStreamer.cpp MCInst.cpp MCInstPrinter.cpp diff --git a/llvm/lib/MC/MCAsmBaseStreamer.cpp b/llvm/lib/MC/MCAsmBaseStreamer.cpp deleted file mode 100644 index c05b80e..0000000 --- a/llvm/lib/MC/MCAsmBaseStreamer.cpp +++ /dev/null @@ -1,143 +0,0 @@ -//===- MCAsmBaseStreamer.cpp - Base Class for Asm Streamers -----*- C++ -*-===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "llvm/MC/MCAsmBaseStreamer.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCAssembler.h" -#include "llvm/MC/MCCodeEmitter.h" -#include "llvm/MC/MCContext.h" -#include "llvm/MC/MCFixup.h" -#include "llvm/MC/MCInst.h" -#include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/Support/Format.h" -#include "llvm/Support/raw_ostream.h" - -using namespace llvm; - -MCAsmBaseStreamer::MCAsmBaseStreamer(MCContext &Context, - std::unique_ptr<MCCodeEmitter> Emitter, - std::unique_ptr<MCAsmBackend> AsmBackend) - : MCStreamer(Context), - Assembler(std::make_unique<MCAssembler>( - Context, std::move(AsmBackend), std::move(Emitter), - (AsmBackend) ? AsmBackend->createObjectWriter(NullStream) : nullptr)), - CommentStream(CommentToEmit) {} - -void MCAsmBaseStreamer::addEncodingComment(const MCInst &Inst, - const MCSubtargetInfo &STI) { - raw_ostream &OS = getCommentOS(); - SmallString<256> Code; - SmallVector<MCFixup, 4> Fixups; - - // If we have no code emitter, don't emit code. - if (!getAssembler().getEmitterPtr()) - return; - - getAssembler().getEmitter().encodeInstruction(Inst, Code, Fixups, STI); - - // RISC-V instructions are always little-endian, even on BE systems. - bool ForceLE = getContext().getTargetTriple().isRISCV(); - - const MCAsmInfo *MAI = getContext().getAsmInfo(); - - // If we are showing fixups, create symbolic markers in the encoded - // representation. We do this by making a per-bit map to the fixup item index, - // then trying to display it as nicely as possible. - SmallVector<uint8_t, 64> FixupMap; - FixupMap.resize(Code.size() * 8); - for (unsigned I = 0, E = Code.size() * 8; I != E; ++I) - FixupMap[I] = 0; - - for (unsigned I = 0, E = Fixups.size(); I != E; ++I) { - MCFixup &F = Fixups[I]; - MCFixupKindInfo Info = - getAssembler().getBackend().getFixupKindInfo(F.getKind()); - for (unsigned J = 0; J != Info.TargetSize; ++J) { - unsigned Index = F.getOffset() * 8 + Info.TargetOffset + J; - assert(Index < Code.size() * 8 && "Invalid offset in fixup!"); - FixupMap[Index] = 1 + I; - } - } - - // FIXME: Note the fixup comments for Thumb2 are completely bogus since the - // high order halfword of a 32-bit Thumb2 instruction is emitted first. - OS << "encoding: ["; - for (unsigned I = 0, E = Code.size(); I != E; ++I) { - if (I) - OS << ','; - - // See if all bits are the same map entry. - uint8_t MapEntry = FixupMap[I * 8 + 0]; - for (unsigned J = 1; J != 8; ++J) { - if (FixupMap[I * 8 + J] == MapEntry) - continue; - - MapEntry = uint8_t(~0U); - break; - } - - if (MapEntry != uint8_t(~0U)) { - if (MapEntry == 0) { - OS << format("0x%02x", uint8_t(Code[I])); - } else { - if (Code[I]) { - // FIXME: Some of the 8 bits require fix up. - OS << format("0x%02x", uint8_t(Code[I])) << '\'' - << char('A' + MapEntry - 1) << '\''; - } else - OS << char('A' + MapEntry - 1); - } - } else { - // Otherwise, write out in binary. - OS << "0b"; - for (unsigned J = 8; J--;) { - unsigned Bit = (Code[I] >> J) & 1; - - unsigned FixupBit; - // RISC-V instructions are always little-endian. - // The FixupMap is indexed by actual bit positions in the LE - // instruction. - if (MAI->isLittleEndian() || ForceLE) - FixupBit = I * 8 + J; - else - FixupBit = I * 8 + (7 - J); - - if (uint8_t MapEntry = FixupMap[FixupBit]) { - assert(Bit == 0 && "Encoder wrote into fixed up bit!"); - OS << char('A' + MapEntry - 1); - } else - OS << Bit; - } - } - } - OS << "]\n"; - - for (unsigned I = 0, E = Fixups.size(); I != E; ++I) { - MCFixup &F = Fixups[I]; - OS << " fixup " << char('A' + I) << " - " - << "offset: " << F.getOffset() << ", value: "; - MAI->printExpr(OS, *F.getValue()); - auto Kind = F.getKind(); - if (mc::isRelocation(Kind)) - OS << ", relocation type: " << Kind; - else { - OS << ", kind: "; - auto Info = getAssembler().getBackend().getFixupKindInfo(Kind); - if (F.isPCRel() && StringRef(Info.Name).starts_with("FK_Data_")) - OS << "FK_PCRel_" << (Info.TargetSize / 8); - else - OS << Info.Name; - } - OS << '\n'; - } -} diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index b451abb..f3ab3f0 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -1,4 +1,4 @@ -//===- lib/MC/MCAsmStreamer.cpp - Text Assembly Output ----------*- C++ -*-===// +//===- MCAsmStreamer.cpp - Base Class for Asm Streamers ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,2598 +6,138 @@ // //===----------------------------------------------------------------------===// +#include "llvm/MC/MCAsmStreamer.h" #include "llvm/ADT/SmallString.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/Twine.h" -#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCAsmBaseStreamer.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" -#include "llvm/MC/MCCodeView.h" #include "llvm/MC/MCContext.h" -#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixup.h" #include "llvm/MC/MCInst.h" -#include "llvm/MC/MCInstPrinter.h" -#include "llvm/MC/MCLFIRewriter.h" -#include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" -#include "llvm/MC/MCPseudoProbe.h" -#include "llvm/MC/MCRegister.h" -#include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCSectionMachO.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSymbolXCOFF.h" -#include "llvm/MC/TargetRegistry.h" -#include "llvm/Support/ErrorHandling.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/Format.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Support/LEB128.h" -#include "llvm/Support/MathExtras.h" -#include "llvm/Support/Path.h" -#include <algorithm> -#include <optional> +#include "llvm/Support/raw_ostream.h" using namespace llvm; -namespace { +MCAsmStreamer::MCAsmStreamer(MCContext &Context, + std::unique_ptr<MCCodeEmitter> Emitter, + std::unique_ptr<MCAsmBackend> AsmBackend) + : MCStreamer(Context), + Assembler(std::make_unique<MCAssembler>( + Context, std::move(AsmBackend), std::move(Emitter), + (AsmBackend) ? AsmBackend->createObjectWriter(NullStream) : nullptr)), + CommentStream(CommentToEmit) {} -class MCAsmStreamer final : public MCAsmBaseStreamer { - std::unique_ptr<formatted_raw_ostream> OSOwner; - formatted_raw_ostream &OS; - const MCAsmInfo *MAI; - std::unique_ptr<MCInstPrinter> InstPrinter; +void MCAsmStreamer::addEncodingComment(const MCInst &Inst, + const MCSubtargetInfo &STI) { + raw_ostream &OS = getCommentOS(); + SmallString<256> Code; + SmallVector<MCFixup, 4> Fixups; - SmallString<128> ExplicitCommentToEmit; - - bool EmittedSectionDirective = false; - - bool IsVerboseAsm = false; - bool ShowInst = false; - bool UseDwarfDirectory = false; - - void EmitRegisterName(int64_t Register); - void PrintQuotedString(StringRef Data, raw_ostream &OS) const; - void printDwarfFileDirective(unsigned FileNo, StringRef Directory, - StringRef Filename, - std::optional<MD5::MD5Result> Checksum, - std::optional<StringRef> Source, - bool UseDwarfDirectory, - raw_svector_ostream &OS) const; - void emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) override; - void emitCFIEndProcImpl(MCDwarfFrameInfo &Frame) override; - - /// Helper to emit common .loc directive flags, isa, and discriminator. - void emitDwarfLocDirectiveFlags(unsigned Flags, unsigned Isa, - unsigned Discriminator); - - /// Helper to emit the common suffix of .loc directives (flags, comment, EOL, - /// parent call). - void emitDwarfLocDirectiveSuffix(unsigned FileNo, unsigned Line, - unsigned Column, unsigned Flags, - unsigned Isa, unsigned Discriminator, - StringRef FileName, StringRef Comment); - -public: - MCAsmStreamer(MCContext &Context, std::unique_ptr<formatted_raw_ostream> OS, - std::unique_ptr<MCInstPrinter> Printer, - std::unique_ptr<MCCodeEmitter> Emitter, - std::unique_ptr<MCAsmBackend> AsmBackend) - : MCAsmBaseStreamer(Context, std::move(Emitter), std::move(AsmBackend)), - OSOwner(std::move(OS)), OS(*OSOwner), MAI(Context.getAsmInfo()), - InstPrinter(std::move(Printer)) { - assert(InstPrinter); - if (Assembler->getBackendPtr()) - setAllowAutoPadding(Assembler->getBackend().allowAutoPadding()); - - Context.setUseNamesOnTempLabels(true); - - auto *TO = Context.getTargetOptions(); - if (!TO) - return; - IsVerboseAsm = TO->AsmVerbose; - if (IsVerboseAsm) - InstPrinter->setCommentStream(CommentStream); - ShowInst = TO->ShowMCInst; - switch (TO->MCUseDwarfDirectory) { - case MCTargetOptions::DisableDwarfDirectory: - UseDwarfDirectory = false; - break; - case MCTargetOptions::EnableDwarfDirectory: - UseDwarfDirectory = true; - break; - case MCTargetOptions::DefaultDwarfDirectory: - UseDwarfDirectory = - Context.getAsmInfo()->enableDwarfFileDirectoryDefault(); - break; - } - } - - inline void EmitEOL() { - // Dump Explicit Comments here. - emitExplicitComments(); - // If we don't have any comments, just emit a \n. - if (!IsVerboseAsm) { - OS << '\n'; - return; - } - EmitCommentsAndEOL(); - } - - void emitSyntaxDirective(StringRef Syntax, StringRef Options) override; - - void EmitCommentsAndEOL(); - - /// Return true if this streamer supports verbose assembly at all. - bool isVerboseAsm() const override { return IsVerboseAsm; } - - /// Do we support EmitRawText? - bool hasRawTextSupport() const override { return true; } - - /// Add a comment that can be emitted to the generated .s file to make the - /// output of the compiler more readable. This only affects the MCAsmStreamer - /// and only when verbose assembly output is enabled. - void AddComment(const Twine &T, bool EOL = true) override; - - void emitRawComment(const Twine &T, bool TabPrefix = true) override; - - void addExplicitComment(const Twine &T) override; - void emitExplicitComments() override; - - /// Emit a blank line to a .s file to pretty it up. - void addBlankLine() override { EmitEOL(); } - - /// @name MCStreamer Interface - /// @{ - - void switchSection(MCSection *Section, uint32_t Subsection) override; - bool popSection() override; - - void emitELFSymverDirective(const MCSymbol *OriginalSym, StringRef Name, - bool KeepOriginalSym) override; - - void emitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) override; - - void emitGNUAttribute(unsigned Tag, unsigned Value) override; - - StringRef getMnemonic(const MCInst &MI) const override { - auto [Ptr, Bits] = InstPrinter->getMnemonic(MI); - assert((Bits != 0 || Ptr == nullptr) && - "Invalid char pointer for instruction with no mnemonic"); - return Ptr; - } - - void emitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; - - void emitSubsectionsViaSymbols() override; - void emitLinkerOptions(ArrayRef<std::string> Options) override; - void emitDataRegion(MCDataRegionType Kind) override; - void emitVersionMin(MCVersionMinType Kind, unsigned Major, unsigned Minor, - unsigned Update, VersionTuple SDKVersion) override; - void emitBuildVersion(unsigned Platform, unsigned Major, unsigned Minor, - unsigned Update, VersionTuple SDKVersion) override; - void emitDarwinTargetVariantBuildVersion(unsigned Platform, unsigned Major, - unsigned Minor, unsigned Update, - VersionTuple SDKVersion) override; - - void emitAssignment(MCSymbol *Symbol, const MCExpr *Value) override; - void emitConditionalAssignment(MCSymbol *Symbol, - const MCExpr *Value) override; - void emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) override; - bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; - - void emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) override; - void beginCOFFSymbolDef(const MCSymbol *Symbol) override; - void emitCOFFSymbolStorageClass(int StorageClass) override; - void emitCOFFSymbolType(int Type) override; - void endCOFFSymbolDef() override; - void emitCOFFSafeSEH(MCSymbol const *Symbol) override; - void emitCOFFSymbolIndex(MCSymbol const *Symbol) override; - void emitCOFFSectionIndex(MCSymbol const *Symbol) override; - void emitCOFFSecRel32(MCSymbol const *Symbol, uint64_t Offset) override; - void emitCOFFImgRel32(MCSymbol const *Symbol, int64_t Offset) override; - void emitCOFFSecNumber(MCSymbol const *Symbol) override; - void emitCOFFSecOffset(MCSymbol const *Symbol) override; - void emitXCOFFLocalCommonSymbol(MCSymbol *LabelSym, uint64_t Size, - MCSymbol *CsectSym, Align Alignment) override; - void emitXCOFFSymbolLinkageWithVisibility(MCSymbol *Symbol, - MCSymbolAttr Linkage, - MCSymbolAttr Visibility) override; - void emitXCOFFRenameDirective(const MCSymbol *Name, - StringRef Rename) override; - - void emitXCOFFRefDirective(const MCSymbol *Symbol) override; - - void emitXCOFFExceptDirective(const MCSymbol *Symbol, - const MCSymbol *Trap, - unsigned Lang, unsigned Reason, - unsigned FunctionSize, bool hasDebug) override; - void emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) override; - - void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; - void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, - Align ByteAlignment) override; - - /// Emit a local common (.lcomm) symbol. - /// - /// @param Symbol - The common symbol to emit. - /// @param Size - The size of the common symbol. - /// @param ByteAlignment - The alignment of the common symbol in bytes. - void emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, - Align ByteAlignment) override; - - void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, - uint64_t Size = 0, Align ByteAlignment = Align(1), - SMLoc Loc = SMLoc()) override; - - void emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, uint64_t Size, - Align ByteAlignment = Align(1)) override; - - void emitBinaryData(StringRef Data) override; - - void emitBytes(StringRef Data) override; - - void emitValueImpl(const MCExpr *Value, unsigned Size, - SMLoc Loc = SMLoc()) override; - void emitIntValue(uint64_t Value, unsigned Size) override; - void emitIntValueInHex(uint64_t Value, unsigned Size) override; - void emitIntValueInHexWithPadding(uint64_t Value, unsigned Size) override; - - void emitULEB128Value(const MCExpr *Value) override; - - void emitSLEB128Value(const MCExpr *Value) override; - - 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; - - void emitAlignmentDirective(uint64_t ByteAlignment, - std::optional<int64_t> Value, unsigned ValueSize, - unsigned MaxBytesToEmit); - - void emitValueToAlignment(Align Alignment, int64_t Fill = 0, - uint8_t FillLen = 1, - unsigned MaxBytesToEmit = 0) override; - - void emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI, - unsigned MaxBytesToEmit = 0) override; - void emitPrefAlign(Align Alignment) override; - - void emitValueToOffset(const MCExpr *Offset, - unsigned char Value, - SMLoc Loc) override; - - void emitFileDirective(StringRef Filename) override; - void emitFileDirective(StringRef Filename, StringRef CompilerVersion, - StringRef TimeStamp, StringRef Description) override; - Expected<unsigned> tryEmitDwarfFileDirective( - unsigned FileNo, StringRef Directory, StringRef Filename, - std::optional<MD5::MD5Result> Checksum = std::nullopt, - std::optional<StringRef> Source = std::nullopt, - unsigned CUID = 0) override; - void emitDwarfFile0Directive(StringRef Directory, StringRef Filename, - std::optional<MD5::MD5Result> Checksum, - std::optional<StringRef> Source, - unsigned CUID = 0) override; - void emitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, - unsigned Flags, unsigned Isa, - unsigned Discriminator, StringRef FileName, - StringRef Location = {}) override; - void emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name) override; - - /// This is same as emitDwarfLocDirective, except also emits inlined function - /// and inlined callsite information. - void emitDwarfLocDirectiveWithInlinedAt(unsigned FileNo, unsigned Line, - unsigned Column, unsigned FileIA, - unsigned LineIA, unsigned ColIA, - const MCSymbol *Sym, unsigned Flags, - unsigned Isa, unsigned Discriminator, - StringRef FileName, - StringRef Comment = {}) override; - - MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override; - - bool emitCVFileDirective(unsigned FileNo, StringRef Filename, - ArrayRef<uint8_t> Checksum, - unsigned ChecksumKind) override; - bool emitCVFuncIdDirective(unsigned FuncId) override; - bool emitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc, - unsigned IAFile, unsigned IALine, - unsigned IACol, SMLoc Loc) override; - void emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, - unsigned Column, bool PrologueEnd, bool IsStmt, - StringRef FileName, SMLoc Loc) override; - void emitCVLinetableDirective(unsigned FunctionId, const MCSymbol *FnStart, - const MCSymbol *FnEnd) override; - void emitCVInlineLinetableDirective(unsigned PrimaryFunctionId, - unsigned SourceFileId, - unsigned SourceLineNum, - const MCSymbol *FnStartSym, - const MCSymbol *FnEndSym) override; - - void PrintCVDefRangePrefix( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges); - - void emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeRegisterRelHeader DRHdr) override; - - void emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeSubfieldRegisterHeader DRHdr) override; - - void emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeRegisterHeader DRHdr) override; - - void emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeFramePointerRelHeader DRHdr) override; - - void emitCVStringTableDirective() override; - void emitCVFileChecksumsDirective() override; - void emitCVFileChecksumOffsetDirective(unsigned FileNo) override; - void emitCVFPOData(const MCSymbol *ProcSym, SMLoc L) override; - - void emitIdent(StringRef IdentString) override; - void emitCFIBKeyFrame() override; - void emitCFIMTETaggedFrame() override; - void emitCFISections(bool EH, bool Debug, bool SFrame) override; - void emitCFIDefCfa(int64_t Register, int64_t Offset, SMLoc Loc) override; - void emitCFIDefCfaOffset(int64_t Offset, SMLoc Loc) override; - void emitCFIDefCfaRegister(int64_t Register, SMLoc Loc) override; - void emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset, - int64_t AddressSpace, SMLoc Loc) override; - void emitCFIOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; - void emitCFIPersonality(const MCSymbol *Sym, unsigned Encoding) override; - void emitCFILsda(const MCSymbol *Sym, unsigned Encoding) override; - void emitCFIRememberState(SMLoc Loc) override; - void emitCFIRestoreState(SMLoc Loc) override; - void emitCFIRestore(int64_t Register, SMLoc Loc) override; - void emitCFISameValue(int64_t Register, SMLoc Loc) override; - void emitCFIRelOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; - void emitCFIAdjustCfaOffset(int64_t Adjustment, SMLoc Loc) override; - void emitCFIEscape(StringRef Values, SMLoc Loc) override; - void emitCFIGnuArgsSize(int64_t Size, SMLoc Loc) override; - void emitCFISignalFrame() override; - void emitCFIUndefined(int64_t Register, SMLoc Loc) override; - void emitCFIRegister(int64_t Register1, int64_t Register2, - SMLoc Loc) override; - void emitCFIWindowSave(SMLoc Loc) override; - void emitCFINegateRAState(SMLoc Loc) override; - void emitCFINegateRAStateWithPC(SMLoc Loc) override; - void emitCFIReturnColumn(int64_t Register) override; - void emitCFILabelDirective(SMLoc Loc, StringRef Name) override; - void emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; - - void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override; - void emitWinCFIEndProc(SMLoc Loc) override; - void emitWinCFIFuncletOrFuncEnd(SMLoc Loc) override; - void emitWinCFISplitChained(SMLoc Loc) override; - void emitWinCFIPushReg(MCRegister Register, SMLoc Loc) override; - void emitWinCFISetFrame(MCRegister Register, unsigned Offset, - SMLoc Loc) override; - void emitWinCFIAllocStack(unsigned Size, SMLoc Loc) override; - void emitWinCFISaveReg(MCRegister Register, unsigned Offset, - SMLoc Loc) override; - void emitWinCFISaveXMM(MCRegister Register, unsigned Offset, - SMLoc Loc) override; - void emitWinCFIPushFrame(bool Code, SMLoc Loc) override; - void emitWinCFIEndProlog(SMLoc Loc) override; - void emitWinCFIBeginEpilogue(SMLoc Loc) override; - void emitWinCFIEndEpilogue(SMLoc Loc) override; - void emitWinCFIUnwindV2Start(SMLoc Loc) override; - void emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) override; - - void emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except, - SMLoc Loc) override; - void emitWinEHHandlerData(SMLoc Loc) override; - - void emitCGProfileEntry(const MCSymbolRefExpr *From, - const MCSymbolRefExpr *To, uint64_t Count) override; - - void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; - - void emitPseudoProbe(uint64_t Guid, uint64_t Index, uint64_t Type, - uint64_t Attr, uint64_t Discriminator, - const MCPseudoProbeInlineStack &InlineStack, - MCSymbol *FnSym) override; - - void emitRelocDirective(const MCExpr &Offset, StringRef Name, - const MCExpr *Expr, SMLoc Loc) override; - - void emitAddrsig() override; - void emitAddrsigSym(const MCSymbol *Sym) override; - - /// If this file is backed by an assembly streamer, this dumps the specified - /// string in the output .s file. This capability is indicated by the - /// hasRawTextSupport() predicate. - void emitRawTextImpl(StringRef String) override; - - void finishImpl() override; - - void emitDwarfUnitLength(uint64_t Length, const Twine &Comment) override; - - MCSymbol *emitDwarfUnitLength(const Twine &Prefix, - const Twine &Comment) override; - - void emitDwarfLineStartLabel(MCSymbol *StartSym) override; - - void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel, - MCSymbol *EndLabel = nullptr) override; - - void emitDwarfAdvanceLineAddr(int64_t LineDelta, const MCSymbol *LastLabel, - const MCSymbol *Label, - unsigned PointerSize) override; -}; - -} // end anonymous namespace. - -void MCAsmStreamer::AddComment(const Twine &T, bool EOL) { - if (!IsVerboseAsm) return; - - T.toVector(CommentToEmit); - - if (EOL) - CommentToEmit.push_back('\n'); // Place comment in a new line. -} - -void MCAsmStreamer::EmitCommentsAndEOL() { - if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0) { - OS << '\n'; - return; - } - - StringRef Comments = CommentToEmit; - - assert(Comments.back() == '\n' && - "Comment array not newline terminated"); - do { - // Emit a line of comments. - OS.PadToColumn(MAI->getCommentColumn()); - size_t Position = Comments.find('\n'); - OS << MAI->getCommentString() << ' ' << Comments.substr(0, Position) <<'\n'; - - Comments = Comments.substr(Position+1); - } while (!Comments.empty()); - - CommentToEmit.clear(); -} - -static inline int64_t truncateToSize(int64_t Value, unsigned Bytes) { - assert(Bytes > 0 && Bytes <= 8 && "Invalid size!"); - return Value & ((uint64_t) (int64_t) -1 >> (64 - Bytes * 8)); -} - -void MCAsmStreamer::emitRawComment(const Twine &T, bool TabPrefix) { - if (TabPrefix) - OS << '\t'; - OS << MAI->getCommentString() << T; - EmitEOL(); -} - -void MCAsmStreamer::addExplicitComment(const Twine &T) { - StringRef c = T.getSingleStringRef(); - if (c == MAI->getSeparatorString()) - return; - if (c.starts_with(StringRef("//"))) { - ExplicitCommentToEmit.append("\t"); - ExplicitCommentToEmit.append(MAI->getCommentString()); - // drop // - ExplicitCommentToEmit.append(c.substr(2).str()); - } else if (c.starts_with(StringRef("/*"))) { - size_t p = 2, len = c.size() - 2; - // emit each line in comment as separate newline. - do { - size_t newp = std::min(len, c.find_first_of("\r\n", p)); - ExplicitCommentToEmit.append("\t"); - ExplicitCommentToEmit.append(MAI->getCommentString()); - ExplicitCommentToEmit.append(c.slice(p, newp).str()); - // If we have another line in this comment add line - if (newp < len) - ExplicitCommentToEmit.append("\n"); - p = newp + 1; - } while (p < len); - } else if (c.starts_with(StringRef(MAI->getCommentString()))) { - ExplicitCommentToEmit.append("\t"); - ExplicitCommentToEmit.append(c.str()); - } else if (c.front() == '#') { - - ExplicitCommentToEmit.append("\t"); - ExplicitCommentToEmit.append(MAI->getCommentString()); - ExplicitCommentToEmit.append(c.substr(1).str()); - } else - assert(false && "Unexpected Assembly Comment"); - // full line comments immediately output - if (c.back() == '\n') - emitExplicitComments(); -} - -void MCAsmStreamer::emitExplicitComments() { - StringRef Comments = ExplicitCommentToEmit; - if (!Comments.empty()) - OS << Comments; - ExplicitCommentToEmit.clear(); -} - -void MCAsmStreamer::switchSection(MCSection *Section, uint32_t Subsection) { - MCSectionSubPair Cur = getCurrentSection(); - if (!EmittedSectionDirective || - MCSectionSubPair(Section, Subsection) != Cur) { - EmittedSectionDirective = true; - if (MCTargetStreamer *TS = getTargetStreamer()) { - TS->changeSection(Cur.first, Section, Subsection, OS); - } else { - MAI->printSwitchToSection(*Section, Subsection, - getContext().getTargetTriple(), OS); - } - } - MCStreamer::switchSection(Section, Subsection); -} - -bool MCAsmStreamer::popSection() { - if (!MCStreamer::popSection()) - return false; - auto [Sec, Subsec] = getCurrentSection(); - MAI->printSwitchToSection(*Sec, Subsec, getContext().getTargetTriple(), OS); - return true; -} - -void MCAsmStreamer::emitELFSymverDirective(const MCSymbol *OriginalSym, - StringRef Name, - bool KeepOriginalSym) { - OS << ".symver "; - OriginalSym->print(OS, MAI); - OS << ", " << Name; - if (!KeepOriginalSym && !Name.contains("@@@")) - OS << ", remove"; - EmitEOL(); -} - -void MCAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { - MCStreamer::emitLabel(Symbol, Loc); - // FIXME: Fix CodeGen/AArch64/arm64ec-varargs.ll. emitLabel is followed by - // setVariableValue, leading to an assertion failure if setOffset(0) is - // called. - if (!Symbol->isVariable() && - getContext().getObjectFileType() != MCContext::IsCOFF) - Symbol->setOffset(0); - - Symbol->print(OS, MAI); - OS << MAI->getLabelSuffix(); - - EmitEOL(); -} - -void MCAsmStreamer::emitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) { - StringRef str = MCLOHIdToName(Kind); - -#ifndef NDEBUG - int NbArgs = MCLOHIdToNbArgs(Kind); - assert(NbArgs != -1 && ((size_t)NbArgs) == Args.size() && "Malformed LOH!"); - assert(str != "" && "Invalid LOH name"); -#endif - - OS << "\t" << MCLOHDirectiveName() << " " << str << "\t"; - bool IsFirst = true; - for (const MCSymbol *Arg : Args) { - if (!IsFirst) - OS << ", "; - IsFirst = false; - Arg->print(OS, MAI); - } - EmitEOL(); -} - -void MCAsmStreamer::emitGNUAttribute(unsigned Tag, unsigned Value) { - OS << "\t.gnu_attribute " << Tag << ", " << Value << "\n"; -} - -void MCAsmStreamer::emitSubsectionsViaSymbols() { - OS << ".subsections_via_symbols\n"; -} - -void MCAsmStreamer::emitLinkerOptions(ArrayRef<std::string> Options) { - assert(!Options.empty() && "At least one option is required!"); - OS << "\t.linker_option \"" << Options[0] << '"'; - for (const std::string &Opt : llvm::drop_begin(Options)) - OS << ", " << '"' << Opt << '"'; - EmitEOL(); -} - -void MCAsmStreamer::emitDataRegion(MCDataRegionType Kind) { - if (!MAI->doesSupportDataRegionDirectives()) - return; - switch (Kind) { - case MCDR_DataRegion: OS << "\t.data_region"; break; - case MCDR_DataRegionJT8: OS << "\t.data_region jt8"; break; - case MCDR_DataRegionJT16: OS << "\t.data_region jt16"; break; - case MCDR_DataRegionJT32: OS << "\t.data_region jt32"; break; - case MCDR_DataRegionEnd: OS << "\t.end_data_region"; break; - } - EmitEOL(); -} - -static const char *getVersionMinDirective(MCVersionMinType Type) { - switch (Type) { - case MCVM_WatchOSVersionMin: return ".watchos_version_min"; - case MCVM_TvOSVersionMin: return ".tvos_version_min"; - case MCVM_IOSVersionMin: return ".ios_version_min"; - case MCVM_OSXVersionMin: return ".macosx_version_min"; - } - llvm_unreachable("Invalid MC version min type"); -} - -static void EmitSDKVersionSuffix(raw_ostream &OS, - const VersionTuple &SDKVersion) { - if (SDKVersion.empty()) - return; - OS << '\t' << "sdk_version " << SDKVersion.getMajor(); - if (auto Minor = SDKVersion.getMinor()) { - OS << ", " << *Minor; - if (auto Subminor = SDKVersion.getSubminor()) { - OS << ", " << *Subminor; - } - } -} - -void MCAsmStreamer::emitVersionMin(MCVersionMinType Type, unsigned Major, - unsigned Minor, unsigned Update, - VersionTuple SDKVersion) { - OS << '\t' << getVersionMinDirective(Type) << ' ' << Major << ", " << Minor; - if (Update) - OS << ", " << Update; - EmitSDKVersionSuffix(OS, SDKVersion); - EmitEOL(); -} - -static const char *getPlatformName(MachO::PlatformType Type) { - switch (Type) { -#define PLATFORM(platform, id, name, build_name, target, tapi_target, \ - marketing) \ - case MachO::PLATFORM_##platform: \ - return #build_name; -#include "llvm/BinaryFormat/MachO.def" - } - llvm_unreachable("Invalid Mach-O platform type"); -} - -void MCAsmStreamer::emitBuildVersion(unsigned Platform, unsigned Major, - unsigned Minor, unsigned Update, - VersionTuple SDKVersion) { - const char *PlatformName = getPlatformName((MachO::PlatformType)Platform); - OS << "\t.build_version " << PlatformName << ", " << Major << ", " << Minor; - if (Update) - OS << ", " << Update; - EmitSDKVersionSuffix(OS, SDKVersion); - EmitEOL(); -} - -void MCAsmStreamer::emitDarwinTargetVariantBuildVersion( - unsigned Platform, unsigned Major, unsigned Minor, unsigned Update, - VersionTuple SDKVersion) { - emitBuildVersion(Platform, Major, Minor, Update, SDKVersion); -} - -void MCAsmStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) { - bool UseSet = MAI->usesSetToEquateSymbol(); - if (UseSet) - OS << ".set "; - Symbol->print(OS, MAI); - OS << (UseSet ? ", " : " = "); - MAI->printExpr(OS, *Value); - - EmitEOL(); - MCStreamer::emitAssignment(Symbol, Value); -} - -void MCAsmStreamer::emitConditionalAssignment(MCSymbol *Symbol, - const MCExpr *Value) { - OS << ".lto_set_conditional "; - Symbol->print(OS, MAI); - OS << ", "; - MAI->printExpr(OS, *Value); - EmitEOL(); -} - -void MCAsmStreamer::emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) { - OS << ".weakref "; - Alias->print(OS, MAI); - OS << ", "; - Symbol->print(OS, MAI); - EmitEOL(); -} - -bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol, - MCSymbolAttr Attribute) { - switch (Attribute) { - case MCSA_Invalid: llvm_unreachable("Invalid symbol attribute"); - case MCSA_ELF_TypeFunction: /// .type _foo, STT_FUNC # aka @function - case MCSA_ELF_TypeIndFunction: /// .type _foo, STT_GNU_IFUNC - case MCSA_ELF_TypeObject: /// .type _foo, STT_OBJECT # aka @object - case MCSA_ELF_TypeTLS: /// .type _foo, STT_TLS # aka @tls_object - case MCSA_ELF_TypeCommon: /// .type _foo, STT_COMMON # aka @common - case MCSA_ELF_TypeNoType: /// .type _foo, STT_NOTYPE # aka @notype - case MCSA_ELF_TypeGnuUniqueObject: /// .type _foo, @gnu_unique_object - if (!MAI->hasDotTypeDotSizeDirective()) - return false; // Symbol attribute not supported - OS << "\t.type\t"; - Symbol->print(OS, MAI); - OS << ',' << ((MAI->getCommentString()[0] != '@') ? '@' : '%'); - switch (Attribute) { - default: return false; - case MCSA_ELF_TypeFunction: OS << "function"; break; - case MCSA_ELF_TypeIndFunction: OS << "gnu_indirect_function"; break; - case MCSA_ELF_TypeObject: OS << "object"; break; - case MCSA_ELF_TypeTLS: OS << "tls_object"; break; - case MCSA_ELF_TypeCommon: OS << "common"; break; - case MCSA_ELF_TypeNoType: OS << "notype"; break; - case MCSA_ELF_TypeGnuUniqueObject: OS << "gnu_unique_object"; break; - } - EmitEOL(); - return true; - case MCSA_Global: // .globl/.global - OS << MAI->getGlobalDirective(); - break; - case MCSA_LGlobal: OS << "\t.lglobl\t"; break; - case MCSA_Hidden: OS << "\t.hidden\t"; break; - case MCSA_IndirectSymbol: OS << "\t.indirect_symbol\t"; break; - case MCSA_Internal: OS << "\t.internal\t"; break; - case MCSA_LazyReference: OS << "\t.lazy_reference\t"; break; - case MCSA_Local: OS << "\t.local\t"; break; - case MCSA_NoDeadStrip: - if (!MAI->hasNoDeadStrip()) - return false; - OS << "\t.no_dead_strip\t"; - break; - case MCSA_SymbolResolver: OS << "\t.symbol_resolver\t"; break; - case MCSA_AltEntry: OS << "\t.alt_entry\t"; break; - case MCSA_PrivateExtern: - OS << "\t.private_extern\t"; - break; - case MCSA_Protected: OS << "\t.protected\t"; break; - case MCSA_Reference: OS << "\t.reference\t"; break; - case MCSA_Extern: - OS << "\t.extern\t"; - break; - case MCSA_Weak: OS << MAI->getWeakDirective(); break; - case MCSA_WeakDefinition: - OS << "\t.weak_definition\t"; - break; - // .weak_reference - case MCSA_WeakReference: OS << MAI->getWeakRefDirective(); break; - case MCSA_WeakDefAutoPrivate: OS << "\t.weak_def_can_be_hidden\t"; break; - case MCSA_Cold: - // Assemblers currently do not support a .cold directive. - case MCSA_Exported: - // Non-AIX assemblers currently do not support exported visibility. - case MCSA_OSLinkage: - case MCSA_XPLinkage: - // Only for HLASM. - return false; - case MCSA_Memtag: - OS << "\t.memtag\t"; - break; - case MCSA_WeakAntiDep: - OS << "\t.weak_anti_dep\t"; - break; - } - - Symbol->print(OS, MAI); - EmitEOL(); - - return true; -} - -void MCAsmStreamer::emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) { - OS << ".desc" << ' '; - Symbol->print(OS, MAI); - OS << ',' << DescValue; - EmitEOL(); -} - -void MCAsmStreamer::emitSyntaxDirective(StringRef Syntax, StringRef Options) { - OS << "\t." << Syntax << "_syntax"; - if (!Options.empty()) - OS << " " << Options; - EmitEOL(); -} - -void MCAsmStreamer::beginCOFFSymbolDef(const MCSymbol *Symbol) { - OS << "\t.def\t"; - Symbol->print(OS, MAI); - OS << ';'; - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSymbolStorageClass(int StorageClass) { - OS << "\t.scl\t" << StorageClass << ';'; - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSymbolType(int Type) { - OS << "\t.type\t" << Type << ';'; - EmitEOL(); -} - -void MCAsmStreamer::endCOFFSymbolDef() { - OS << "\t.endef"; - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSafeSEH(MCSymbol const *Symbol) { - OS << "\t.safeseh\t"; - Symbol->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSymbolIndex(MCSymbol const *Symbol) { - OS << "\t.symidx\t"; - Symbol->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSectionIndex(MCSymbol const *Symbol) { - OS << "\t.secidx\t"; - Symbol->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSecRel32(MCSymbol const *Symbol, uint64_t Offset) { - OS << "\t.secrel32\t"; - Symbol->print(OS, MAI); - if (Offset != 0) - OS << '+' << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFImgRel32(MCSymbol const *Symbol, int64_t Offset) { - OS << "\t.rva\t"; - Symbol->print(OS, MAI); - if (Offset > 0) - OS << '+' << Offset; - else if (Offset < 0) - OS << '-' << -Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSecNumber(MCSymbol const *Symbol) { - OS << "\t.secnum\t"; - Symbol->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitCOFFSecOffset(MCSymbol const *Symbol) { - OS << "\t.secoffset\t"; - Symbol->print(OS, MAI); - EmitEOL(); -} - -// We need an XCOFF-specific version of this directive as the AIX syntax -// requires a QualName argument identifying the csect name and storage mapping -// class to appear before the alignment if we are specifying it. -void MCAsmStreamer::emitXCOFFLocalCommonSymbol(MCSymbol *LabelSym, - uint64_t Size, - MCSymbol *CsectSym, - Align Alignment) { - assert(MAI->getLCOMMDirectiveAlignmentType() == LCOMM::Log2Alignment && - "We only support writing log base-2 alignment format with XCOFF."); - - OS << "\t.lcomm\t"; - LabelSym->print(OS, MAI); - OS << ',' << Size << ','; - CsectSym->print(OS, MAI); - OS << ',' << Log2(Alignment); - - EmitEOL(); - - // Print symbol's rename (original name contains invalid character(s)) if - // there is one. - auto *XSym = static_cast<MCSymbolXCOFF *>(CsectSym); - if (XSym->hasRename()) - emitXCOFFRenameDirective(XSym, XSym->getSymbolTableName()); -} - -void MCAsmStreamer::emitXCOFFSymbolLinkageWithVisibility( - MCSymbol *Symbol, MCSymbolAttr Linkage, MCSymbolAttr Visibility) { - auto &Sym = static_cast<MCSymbolXCOFF &>(*Symbol); - switch (Linkage) { - case MCSA_Global: - OS << MAI->getGlobalDirective(); - break; - case MCSA_Weak: - OS << MAI->getWeakDirective(); - break; - case MCSA_Extern: - OS << "\t.extern\t"; - break; - case MCSA_LGlobal: - OS << "\t.lglobl\t"; - break; - default: - report_fatal_error("unhandled linkage type"); - } - - Symbol->print(OS, MAI); - - switch (Visibility) { - case MCSA_Invalid: - // Nothing to do. - break; - case MCSA_Hidden: - OS << ",hidden"; - break; - case MCSA_Protected: - OS << ",protected"; - break; - case MCSA_Exported: - OS << ",exported"; - break; - default: - report_fatal_error("unexpected value for Visibility type"); - } - EmitEOL(); - - // Print symbol's rename (original name contains invalid character(s)) if - // there is one. - if (Sym.hasRename()) - emitXCOFFRenameDirective(&Sym, Sym.getSymbolTableName()); -} - -void MCAsmStreamer::emitXCOFFRenameDirective(const MCSymbol *Name, - StringRef Rename) { - OS << "\t.rename\t"; - Name->print(OS, MAI); - const char DQ = '"'; - OS << ',' << DQ; - for (char C : Rename) { - // To escape a double quote character, the character should be doubled. - if (C == DQ) - OS << DQ; - OS << C; - } - OS << DQ; - EmitEOL(); -} - -void MCAsmStreamer::emitXCOFFRefDirective(const MCSymbol *Symbol) { - OS << "\t.ref "; - Symbol->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitXCOFFExceptDirective(const MCSymbol *Symbol, - const MCSymbol *Trap, - unsigned Lang, - unsigned Reason, - unsigned FunctionSize, - bool hasDebug) { - OS << "\t.except\t"; - Symbol->print(OS, MAI); - OS << ", " << Lang << ", " << Reason; - EmitEOL(); -} - -void MCAsmStreamer::emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) { - const char InfoDirective[] = "\t.info "; - const char *Separator = ", "; - constexpr int WordSize = sizeof(uint32_t); - - // Start by emitting the .info pseudo-op and C_INFO symbol name. - OS << InfoDirective; - PrintQuotedString(Name, OS); - OS << Separator; - - size_t MetadataSize = Metadata.size(); - - // Emit the 4-byte length of the metadata. - OS << format_hex(MetadataSize, 10) << Separator; - - // Nothing left to do if there's no metadata. - if (MetadataSize == 0) { - EmitEOL(); + // If we have no code emitter, don't emit code. + if (!getAssembler().getEmitterPtr()) return; - } - // Metadata needs to be padded out to an even word size when generating - // assembly because the .info pseudo-op can only generate words of data. We - // apply the same restriction to the object case for consistency, however the - // linker doesn't require padding, so it will only save bytes specified by the - // length and discard any padding. - uint32_t PaddedSize = alignTo(MetadataSize, WordSize); - uint32_t PaddingSize = PaddedSize - MetadataSize; + getAssembler().getEmitter().encodeInstruction(Inst, Code, Fixups, STI); - // Write out the payload a word at a time. - // - // The assembler has a limit on the number of operands in an expression, - // so we need multiple .info pseudo-ops. We choose a small number of words - // per pseudo-op to keep the assembly readable. - constexpr int WordsPerDirective = 5; - // Force emitting a new directive to keep the first directive purely about the - // name and size of the note. - int WordsBeforeNextDirective = 0; - auto PrintWord = [&](const uint8_t *WordPtr) { - if (WordsBeforeNextDirective-- == 0) { - EmitEOL(); - OS << InfoDirective; - WordsBeforeNextDirective = WordsPerDirective; - } - OS << Separator; - uint32_t Word = llvm::support::endian::read32be(WordPtr); - OS << format_hex(Word, 10); - }; + // RISC-V instructions are always little-endian, even on BE systems. + bool ForceLE = getContext().getTargetTriple().isRISCV(); - size_t Index = 0; - for (; Index + WordSize <= MetadataSize; Index += WordSize) - PrintWord(reinterpret_cast<const uint8_t *>(Metadata.data()) + Index); + const MCAsmInfo *MAI = getContext().getAsmInfo(); - // If there is padding, then we have at least one byte of payload left - // to emit. - if (PaddingSize) { - assert(PaddedSize - Index == WordSize); - std::array<uint8_t, WordSize> LastWord = {0}; - ::memcpy(LastWord.data(), Metadata.data() + Index, MetadataSize - Index); - PrintWord(LastWord.data()); - } - EmitEOL(); -} + // If we are showing fixups, create symbolic markers in the encoded + // representation. We do this by making a per-bit map to the fixup item index, + // then trying to display it as nicely as possible. + SmallVector<uint8_t, 64> FixupMap; + FixupMap.resize(Code.size() * 8); + for (unsigned I = 0, E = Code.size() * 8; I != E; ++I) + FixupMap[I] = 0; -void MCAsmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) { - assert(MAI->hasDotTypeDotSizeDirective()); - OS << "\t.size\t"; - Symbol->print(OS, MAI); - OS << ", "; - MAI->printExpr(OS, *Value); - EmitEOL(); -} - -void MCAsmStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, - Align ByteAlignment) { - OS << "\t.comm\t"; - Symbol->print(OS, MAI); - OS << ',' << Size; - - if (MAI->getCOMMDirectiveAlignmentIsInBytes()) - OS << ',' << ByteAlignment.value(); - else - OS << ',' << Log2(ByteAlignment); - EmitEOL(); - - // Print symbol's rename (original name contains invalid character(s)) if - // there is one. - if (getContext().isXCOFF()) { - auto *XSym = static_cast<MCSymbolXCOFF *>(Symbol); - if (XSym && XSym->hasRename()) - emitXCOFFRenameDirective(XSym, XSym->getSymbolTableName()); - } -} - -void MCAsmStreamer::emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, - Align ByteAlign) { - OS << "\t.lcomm\t"; - Symbol->print(OS, MAI); - OS << ',' << Size; - - if (ByteAlign > 1) { - switch (MAI->getLCOMMDirectiveAlignmentType()) { - case LCOMM::NoAlignment: - llvm_unreachable("alignment not supported on .lcomm!"); - case LCOMM::ByteAlignment: - OS << ',' << ByteAlign.value(); - break; - case LCOMM::Log2Alignment: - OS << ',' << Log2(ByteAlign); - break; + for (unsigned I = 0, E = Fixups.size(); I != E; ++I) { + MCFixup &F = Fixups[I]; + MCFixupKindInfo Info = + getAssembler().getBackend().getFixupKindInfo(F.getKind()); + for (unsigned J = 0; J != Info.TargetSize; ++J) { + unsigned Index = F.getOffset() * 8 + Info.TargetOffset + J; + assert(Index < Code.size() * 8 && "Invalid offset in fixup!"); + FixupMap[Index] = 1 + I; } } - EmitEOL(); -} - -void MCAsmStreamer::emitZerofill(MCSection *Section, MCSymbol *Symbol, - uint64_t Size, Align ByteAlignment, - SMLoc Loc) { - if (Symbol) - Symbol->setFragment(&Section->getDummyFragment()); - - // Note: a .zerofill directive does not switch sections. - OS << ".zerofill "; - - assert(getContext().getObjectFileType() == MCContext::IsMachO && - ".zerofill is a Mach-O specific directive"); - // This is a mach-o specific directive. - - const MCSectionMachO *MOSection = ((const MCSectionMachO*)Section); - OS << MOSection->getSegmentName() << "," << MOSection->getName(); - - if (Symbol) { - OS << ','; - Symbol->print(OS, MAI); - OS << ',' << Size; - OS << ',' << Log2(ByteAlignment); - } - EmitEOL(); -} - -// .tbss sym, size, align -// This depends that the symbol has already been mangled from the original, -// e.g. _a. -void MCAsmStreamer::emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, - uint64_t Size, Align ByteAlignment) { - Symbol->setFragment(&Section->getDummyFragment()); - - // Instead of using the Section we'll just use the shortcut. - assert(getContext().getObjectFileType() == MCContext::IsMachO && - ".zerofill is a Mach-O specific directive"); - // This is a mach-o specific directive and section. - - OS << ".tbss "; - Symbol->print(OS, MAI); - OS << ", " << Size; - - // Output align if we have it. We default to 1 so don't bother printing - // that. - if (ByteAlignment > 1) - OS << ", " << Log2(ByteAlignment); - - EmitEOL(); -} - -static inline bool isPrintableString(StringRef Data) { - const auto BeginPtr = Data.begin(), EndPtr = Data.end(); - for (const unsigned char C : make_range(BeginPtr, EndPtr - 1)) { - if (!isPrint(C)) - return false; - } - return isPrint(Data.back()) || Data.back() == 0; -} - -static inline char toOctal(int X) { return (X&7)+'0'; } - -static void PrintByteList(StringRef Data, raw_ostream &OS, - MCAsmInfo::AsmCharLiteralSyntax ACLS) { - assert(!Data.empty() && "Cannot generate an empty list."); - const auto printCharacterInOctal = [&OS](unsigned char C) { - OS << '0'; - OS << toOctal(C >> 6); - OS << toOctal(C >> 3); - OS << toOctal(C >> 0); - }; - const auto printOneCharacterFor = [printCharacterInOctal]( - auto printOnePrintingCharacter) { - return [printCharacterInOctal, printOnePrintingCharacter](unsigned char C) { - if (isPrint(C)) { - printOnePrintingCharacter(static_cast<char>(C)); - return; - } - printCharacterInOctal(C); - }; - }; - const auto printCharacterList = [Data, &OS](const auto &printOneCharacter) { - const auto BeginPtr = Data.begin(), EndPtr = Data.end(); - for (const unsigned char C : make_range(BeginPtr, EndPtr - 1)) { - printOneCharacter(C); + // FIXME: Note the fixup comments for Thumb2 are completely bogus since the + // high order halfword of a 32-bit Thumb2 instruction is emitted first. + OS << "encoding: ["; + for (unsigned I = 0, E = Code.size(); I != E; ++I) { + if (I) OS << ','; - } - printOneCharacter(*(EndPtr - 1)); - }; - switch (ACLS) { - case MCAsmInfo::ACLS_Unknown: - printCharacterList(printCharacterInOctal); - return; - case MCAsmInfo::ACLS_SingleQuotePrefix: - printCharacterList(printOneCharacterFor([&OS](char C) { - const char AsmCharLitBuf[2] = {'\'', C}; - OS << StringRef(AsmCharLitBuf, sizeof(AsmCharLitBuf)); - })); - return; - } - llvm_unreachable("Invalid AsmCharLiteralSyntax value!"); -} -void MCAsmStreamer::PrintQuotedString(StringRef Data, raw_ostream &OS) const { - OS << '"'; - - if (MAI->isAIX()) { - for (unsigned char C : Data) { - if (C == '"') - OS << "\"\""; - else - OS << (char)C; - } - } else { - for (unsigned char C : Data) { - if (C == '"' || C == '\\') { - OS << '\\' << (char)C; + // See if all bits are the same map entry. + uint8_t MapEntry = FixupMap[I * 8 + 0]; + for (unsigned J = 1; J != 8; ++J) { + if (FixupMap[I * 8 + J] == MapEntry) continue; - } - - if (isPrint(C)) { - OS << (char)C; - continue; - } - - switch (C) { - case '\b': - OS << "\\b"; - break; - case '\f': - OS << "\\f"; - break; - case '\n': - OS << "\\n"; - break; - case '\r': - OS << "\\r"; - break; - case '\t': - OS << "\\t"; - break; - default: - OS << '\\'; - OS << toOctal(C >> 6); - OS << toOctal(C >> 3); - OS << toOctal(C >> 0); - break; - } - } - } - - OS << '"'; -} - -void MCAsmStreamer::emitBytes(StringRef Data) { - assert(getCurrentSectionOnly() && - "Cannot emit contents before setting section!"); - if (Data.empty()) return; - - const auto emitAsString = [this](StringRef Data) { - if (MAI->isAIX()) { - if (isPrintableString(Data)) { - // For target with DoubleQuoteString constants, .string and .byte are - // used as replacement of .asciz and .ascii. - if (Data.back() == 0) { - OS << "\t.string\t"; - Data = Data.substr(0, Data.size() - 1); - } else { - OS << "\t.byte\t"; - } - PrintQuotedString(Data, OS); - } else { - OS << "\t.byte\t"; - PrintByteList(Data, OS, MAI->characterLiteralSyntax()); - } - EmitEOL(); - return true; - } - - // If the data ends with 0 and the target supports .asciz, use it, otherwise - // use .ascii or a byte-list directive - if (MAI->getAscizDirective() && Data.back() == 0) { - OS << MAI->getAscizDirective(); - Data = Data.substr(0, Data.size() - 1); - } else if (LLVM_LIKELY(MAI->getAsciiDirective())) { - OS << MAI->getAsciiDirective(); - } else { - return false; - } - - PrintQuotedString(Data, OS); - EmitEOL(); - return true; - }; - - if (Data.size() != 1 && emitAsString(Data)) - return; - - // Only single byte is provided or no ascii, asciz, or byte-list directives - // are applicable. Emit as vector of individual 8bits data elements. - if (MCTargetStreamer *TS = getTargetStreamer()) { - TS->emitRawBytes(Data); - return; - } - const char *Directive = MAI->getData8bitsDirective(); - for (const unsigned char C : Data.bytes()) { - OS << Directive << (unsigned)C; - EmitEOL(); - } -} - -void MCAsmStreamer::emitBinaryData(StringRef Data) { - // This is binary data. Print it in a grid of hex bytes for readability. - const size_t Cols = 4; - for (size_t I = 0, EI = alignTo(Data.size(), Cols); I < EI; I += Cols) { - size_t J = I, EJ = std::min(I + Cols, Data.size()); - assert(EJ > 0); - OS << MAI->getData8bitsDirective(); - for (; J < EJ - 1; ++J) - OS << format("0x%02x", uint8_t(Data[J])) << ", "; - OS << format("0x%02x", uint8_t(Data[J])); - EmitEOL(); - } -} - -void MCAsmStreamer::emitIntValue(uint64_t Value, unsigned Size) { - emitValue(MCConstantExpr::create(Value, getContext()), Size); -} - -void MCAsmStreamer::emitIntValueInHex(uint64_t Value, unsigned Size) { - emitValue(MCConstantExpr::create(Value, getContext(), true), Size); -} - -void MCAsmStreamer::emitIntValueInHexWithPadding(uint64_t Value, - unsigned Size) { - emitValue(MCConstantExpr::create(Value, getContext(), true, Size), Size); -} - -void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, - SMLoc Loc) { - assert(Size <= 8 && "Invalid size"); - assert(getCurrentSectionOnly() && - "Cannot emit contents before setting section!"); - const char *Directive = nullptr; - switch (Size) { - default: break; - case 1: Directive = MAI->getData8bitsDirective(); break; - case 2: Directive = MAI->getData16bitsDirective(); break; - case 4: Directive = MAI->getData32bitsDirective(); break; - case 8: Directive = MAI->getData64bitsDirective(); break; - } - - if (!Directive) { - int64_t IntValue; - if (!Value->evaluateAsAbsolute(IntValue)) - report_fatal_error("Don't know how to emit this value."); - - // We couldn't handle the requested integer size so we fallback by breaking - // the request down into several, smaller, integers. - // Since sizes greater or equal to "Size" are invalid, we use the greatest - // power of 2 that is less than "Size" as our largest piece of granularity. - bool IsLittleEndian = MAI->isLittleEndian(); - for (unsigned Emitted = 0; Emitted != Size;) { - unsigned Remaining = Size - Emitted; - // The size of our partial emission must be a power of two less than - // Size. - unsigned EmissionSize = llvm::bit_floor(std::min(Remaining, Size - 1)); - // Calculate the byte offset of our partial emission taking into account - // the endianness of the target. - unsigned ByteOffset = - IsLittleEndian ? Emitted : (Remaining - EmissionSize); - uint64_t ValueToEmit = IntValue >> (ByteOffset * 8); - // We truncate our partial emission to fit within the bounds of the - // emission domain. This produces nicer output and silences potential - // truncation warnings when round tripping through another assembler. - uint64_t Shift = 64 - EmissionSize * 8; - assert(Shift < static_cast<uint64_t>( - std::numeric_limits<unsigned long long>::digits) && - "undefined behavior"); - ValueToEmit &= ~0ULL >> Shift; - emitIntValue(ValueToEmit, EmissionSize); - Emitted += EmissionSize; - } - return; - } - - assert(Directive && "Invalid size for machine code value!"); - OS << Directive; - if (MCTargetStreamer *TS = getTargetStreamer()) { - TS->emitValue(Value); - } else { - MAI->printExpr(OS, *Value); - EmitEOL(); - } -} - -void MCAsmStreamer::emitULEB128Value(const MCExpr *Value) { - int64_t IntValue; - if (Value->evaluateAsAbsolute(IntValue)) { - emitULEB128IntValue(IntValue); - return; - } - OS << "\t.uleb128 "; - MAI->printExpr(OS, *Value); - EmitEOL(); -} - -void MCAsmStreamer::emitSLEB128Value(const MCExpr *Value) { - int64_t IntValue; - if (Value->evaluateAsAbsolute(IntValue)) { - emitSLEB128IntValue(IntValue); - return; - } - OS << "\t.sleb128 "; - MAI->printExpr(OS, *Value); - EmitEOL(); -} - -void MCAsmStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, - SMLoc Loc) { - int64_t IntNumBytes; - const bool IsAbsolute = NumBytes.evaluateAsAbsolute(IntNumBytes); - if (IsAbsolute && IntNumBytes == 0) - return; - - if (const char *ZeroDirective = MAI->getZeroDirective()) { - if (!MAI->isAIX() || FillValue == 0) { - // FIXME: Emit location directives - OS << ZeroDirective; - MAI->printExpr(OS, NumBytes); - if (FillValue != 0) - OS << ',' << (int)FillValue; - EmitEOL(); - } else { - if (!IsAbsolute) - report_fatal_error( - "Cannot emit non-absolute expression lengths of fill."); - for (int i = 0; i < IntNumBytes; ++i) { - OS << MAI->getData8bitsDirective() << (int)FillValue; - EmitEOL(); - } - } - return; - } - - MCStreamer::emitFill(NumBytes, FillValue); -} - -void MCAsmStreamer::emitFill(const MCExpr &NumValues, int64_t Size, - int64_t Expr, SMLoc Loc) { - // FIXME: Emit location directives - OS << "\t.fill\t"; - MAI->printExpr(OS, NumValues); - OS << ", " << Size << ", 0x"; - OS.write_hex(truncateToSize(Expr, 4)); - EmitEOL(); -} -void MCAsmStreamer::emitAlignmentDirective(uint64_t ByteAlignment, - std::optional<int64_t> Value, - unsigned ValueSize, - unsigned MaxBytesToEmit) { - if (MAI->isAIX()) { - if (!isPowerOf2_64(ByteAlignment)) - report_fatal_error("Only power-of-two alignments are supported " - "with .align."); - OS << "\t.align\t"; - OS << Log2_64(ByteAlignment); - EmitEOL(); - return; - } - - // Some assemblers don't support non-power of two alignments, so we always - // emit alignments as a power of two if possible. - if (isPowerOf2_64(ByteAlignment)) { - switch (ValueSize) { - default: - llvm_unreachable("Invalid size for machine code value!"); - case 1: - OS << "\t.p2align\t"; - break; - case 2: - OS << ".p2alignw "; + MapEntry = uint8_t(~0U); break; - case 4: - OS << ".p2alignl "; - break; - case 8: - llvm_unreachable("Unsupported alignment size!"); } - OS << Log2_64(ByteAlignment); - - if (Value.has_value() || MaxBytesToEmit) { - if (Value.has_value()) { - OS << ", 0x"; - OS.write_hex(truncateToSize(*Value, ValueSize)); + if (MapEntry != uint8_t(~0U)) { + if (MapEntry == 0) { + OS << format("0x%02x", uint8_t(Code[I])); } else { - OS << ", "; + if (Code[I]) { + // FIXME: Some of the 8 bits require fix up. + OS << format("0x%02x", uint8_t(Code[I])) << '\'' + << char('A' + MapEntry - 1) << '\''; + } else + OS << char('A' + MapEntry - 1); } - - if (MaxBytesToEmit) - OS << ", " << MaxBytesToEmit; - } - EmitEOL(); - return; - } - - // Non-power of two alignment. This is not widely supported by assemblers. - // FIXME: Parameterize this based on MAI. - switch (ValueSize) { - default: llvm_unreachable("Invalid size for machine code value!"); - case 1: OS << ".balign"; break; - case 2: OS << ".balignw"; break; - case 4: OS << ".balignl"; break; - case 8: llvm_unreachable("Unsupported alignment size!"); - } - - OS << ' ' << ByteAlignment; - if (Value.has_value()) - OS << ", " << truncateToSize(*Value, ValueSize); - else if (MaxBytesToEmit) - OS << ", "; - if (MaxBytesToEmit) - OS << ", " << MaxBytesToEmit; - EmitEOL(); -} - -void MCAsmStreamer::emitValueToAlignment(Align Alignment, int64_t Fill, - uint8_t FillLen, - unsigned MaxBytesToEmit) { - emitAlignmentDirective(Alignment.value(), Fill, FillLen, MaxBytesToEmit); -} - -void MCAsmStreamer::emitCodeAlignment(Align Alignment, - const MCSubtargetInfo *STI, - unsigned MaxBytesToEmit) { - // Emit with a text fill value. - if (MAI->getTextAlignFillValue()) - emitAlignmentDirective(Alignment.value(), MAI->getTextAlignFillValue(), 1, - MaxBytesToEmit); - else - emitAlignmentDirective(Alignment.value(), std::nullopt, 1, MaxBytesToEmit); -} - -void MCAsmStreamer::emitPrefAlign(Align Alignment) { - OS << "\t.prefalign\t" << Alignment.value(); - EmitEOL(); -} - -void MCAsmStreamer::emitValueToOffset(const MCExpr *Offset, - unsigned char Value, - SMLoc Loc) { - // FIXME: Verify that Offset is associated with the current section. - OS << ".org "; - MAI->printExpr(OS, *Offset); - OS << ", " << (unsigned)Value; - EmitEOL(); -} - -void MCAsmStreamer::emitFileDirective(StringRef Filename) { - assert(MAI->hasSingleParameterDotFile()); - OS << "\t.file\t"; - PrintQuotedString(Filename, OS); - EmitEOL(); -} - -void MCAsmStreamer::emitFileDirective(StringRef Filename, - StringRef CompilerVersion, - StringRef TimeStamp, - StringRef Description) { - assert(MAI->isAIX()); - OS << "\t.file\t"; - PrintQuotedString(Filename, OS); - bool useTimeStamp = !TimeStamp.empty(); - bool useCompilerVersion = !CompilerVersion.empty(); - bool useDescription = !Description.empty(); - if (useTimeStamp || useCompilerVersion || useDescription) { - OS << ","; - if (useTimeStamp) - PrintQuotedString(TimeStamp, OS); - if (useCompilerVersion || useDescription) { - OS << ","; - if (useCompilerVersion) - PrintQuotedString(CompilerVersion, OS); - if (useDescription) { - OS << ","; - PrintQuotedString(Description, OS); + } else { + // Otherwise, write out in binary. + OS << "0b"; + for (unsigned J = 8; J--;) { + unsigned Bit = (Code[I] >> J) & 1; + + unsigned FixupBit; + // RISC-V instructions are always little-endian. + // The FixupMap is indexed by actual bit positions in the LE + // instruction. + if (MAI->isLittleEndian() || ForceLE) + FixupBit = I * 8 + J; + else + FixupBit = I * 8 + (7 - J); + + if (uint8_t MapEntry = FixupMap[FixupBit]) { + assert(Bit == 0 && "Encoder wrote into fixed up bit!"); + OS << char('A' + MapEntry - 1); + } else + OS << Bit; } } } - EmitEOL(); -} + OS << "]\n"; -void MCAsmStreamer::printDwarfFileDirective( - unsigned FileNo, StringRef Directory, StringRef Filename, - std::optional<MD5::MD5Result> Checksum, std::optional<StringRef> Source, - bool UseDwarfDirectory, raw_svector_ostream &OS) const { - SmallString<128> FullPathName; - - if (!UseDwarfDirectory && !Directory.empty()) { - if (sys::path::is_absolute(Filename)) - Directory = ""; + for (unsigned I = 0, E = Fixups.size(); I != E; ++I) { + MCFixup &F = Fixups[I]; + OS << " fixup " << char('A' + I) << " - " + << "offset: " << F.getOffset() << ", value: "; + MAI->printExpr(OS, *F.getValue()); + auto Kind = F.getKind(); + if (mc::isRelocation(Kind)) + OS << ", relocation type: " << Kind; else { - FullPathName = Directory; - sys::path::append(FullPathName, Filename); - Directory = ""; - Filename = FullPathName; - } - } - - OS << "\t.file\t" << FileNo << ' '; - if (!Directory.empty()) { - PrintQuotedString(Directory, OS); - OS << ' '; - } - PrintQuotedString(Filename, OS); - if (Checksum) - OS << " md5 0x" << Checksum->digest(); - if (Source) { - OS << " source "; - PrintQuotedString(*Source, OS); - } -} - -Expected<unsigned> MCAsmStreamer::tryEmitDwarfFileDirective( - unsigned FileNo, StringRef Directory, StringRef Filename, - std::optional<MD5::MD5Result> Checksum, std::optional<StringRef> Source, - unsigned CUID) { - assert(CUID == 0 && "multiple CUs not supported by MCAsmStreamer"); - - MCDwarfLineTable &Table = getContext().getMCDwarfLineTable(CUID); - unsigned NumFiles = Table.getMCDwarfFiles().size(); - Expected<unsigned> FileNoOrErr = - Table.tryGetFile(Directory, Filename, Checksum, Source, - getContext().getDwarfVersion(), FileNo); - if (!FileNoOrErr) - return FileNoOrErr.takeError(); - FileNo = FileNoOrErr.get(); - - // Return early if this file is already emitted before or if target doesn't - // support .file directive. - if (NumFiles == Table.getMCDwarfFiles().size() || MAI->isAIX()) - return FileNo; - - SmallString<128> Str; - raw_svector_ostream OS1(Str); - printDwarfFileDirective(FileNo, Directory, Filename, Checksum, Source, - UseDwarfDirectory, OS1); - - if (MCTargetStreamer *TS = getTargetStreamer()) - TS->emitDwarfFileDirective(OS1.str()); - else - emitRawText(OS1.str()); - - return FileNo; -} - -void MCAsmStreamer::emitDwarfFile0Directive( - StringRef Directory, StringRef Filename, - std::optional<MD5::MD5Result> Checksum, std::optional<StringRef> Source, - unsigned CUID) { - assert(CUID == 0); - // .file 0 is new for DWARF v5. - if (getContext().getDwarfVersion() < 5) - return; - // Inform MCDwarf about the root file. - getContext().setMCLineTableRootFile(CUID, Directory, Filename, Checksum, - Source); - - // Target doesn't support .loc/.file directives, return early. - if (MAI->isAIX()) - return; - - SmallString<128> Str; - raw_svector_ostream OS1(Str); - printDwarfFileDirective(0, Directory, Filename, Checksum, Source, - UseDwarfDirectory, OS1); - - if (MCTargetStreamer *TS = getTargetStreamer()) - TS->emitDwarfFileDirective(OS1.str()); - else - emitRawText(OS1.str()); -} - -/// Helper to emit common .loc directive flags, isa, and discriminator. -void MCAsmStreamer::emitDwarfLocDirectiveFlags(unsigned Flags, unsigned Isa, - unsigned Discriminator) { - if (!MAI->supportsExtendedDwarfLocDirective()) - return; - - if (Flags & DWARF2_FLAG_BASIC_BLOCK) - OS << " basic_block"; - if (Flags & DWARF2_FLAG_PROLOGUE_END) - OS << " prologue_end"; - if (Flags & DWARF2_FLAG_EPILOGUE_BEGIN) - OS << " epilogue_begin"; - - const unsigned OldFlags = getContext().getCurrentDwarfLoc().getFlags(); - if ((Flags & DWARF2_FLAG_IS_STMT) != (OldFlags & DWARF2_FLAG_IS_STMT)) { - OS << " is_stmt "; - OS << ((Flags & DWARF2_FLAG_IS_STMT) ? "1" : "0"); - } - - if (Isa) - OS << " isa " << Isa; - if (Discriminator) - OS << " discriminator " << Discriminator; -} - -/// Helper to emit the common suffix of .loc directives. -void MCAsmStreamer::emitDwarfLocDirectiveSuffix(unsigned FileNo, unsigned Line, - unsigned Column, unsigned Flags, - unsigned Isa, - unsigned Discriminator, - StringRef FileName, - StringRef Comment) { - // Emit flags, isa, and discriminator. - emitDwarfLocDirectiveFlags(Flags, Isa, Discriminator); - - // Emit verbose comment if enabled. - if (IsVerboseAsm) { - OS.PadToColumn(MAI->getCommentColumn()); - OS << MAI->getCommentString() << ' '; - if (Comment.empty()) - OS << FileName << ':' << Line << ':' << Column; - else - OS << Comment; - } - - // Emit end of line and update the baseclass state. - EmitEOL(); - MCStreamer::emitDwarfLocDirective(FileNo, Line, Column, Flags, Isa, - Discriminator, FileName, Comment); -} - -void MCAsmStreamer::emitDwarfLocDirective(unsigned FileNo, unsigned Line, - unsigned Column, unsigned Flags, - unsigned Isa, unsigned Discriminator, - StringRef FileName, - StringRef Comment) { - // If target doesn't support .loc/.file directive, we need to record the lines - // same way like we do in object mode. - if (MAI->isAIX()) { - // In case we see two .loc directives in a row, make sure the - // first one gets a line entry. - MCDwarfLineEntry::make(this, getCurrentSectionOnly()); - this->MCStreamer::emitDwarfLocDirective(FileNo, Line, Column, Flags, Isa, - Discriminator, FileName, Comment); - return; - } - - // Emit the basic .loc directive. - OS << "\t.loc\t" << FileNo << " " << Line << " " << Column; - - // Emit common suffix (flags, comment, EOL, parent call). - emitDwarfLocDirectiveSuffix(FileNo, Line, Column, Flags, Isa, Discriminator, - FileName, Comment); -} - -/// This is same as emitDwarfLocDirective, except also emits inlined function -/// and inlined callsite information. -void MCAsmStreamer::emitDwarfLocDirectiveWithInlinedAt( - unsigned FileNo, unsigned Line, unsigned Column, unsigned FileIA, - unsigned LineIA, unsigned ColIA, const MCSymbol *Sym, unsigned Flags, - unsigned Isa, unsigned Discriminator, StringRef FileName, - StringRef Comment) { - // Emit the basic .loc directive with NVPTX-specific extensions. - OS << "\t.loc\t" << FileNo << " " << Line << " " << Column; - OS << ", function_name " << *Sym; - OS << ", inlined_at " << FileIA << " " << LineIA << " " << ColIA; - - // Emit common suffix (flags, comment, EOL, parent call). - emitDwarfLocDirectiveSuffix(FileNo, Line, Column, Flags, Isa, Discriminator, - FileName, Comment); -} - -void MCAsmStreamer::emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name) { - MCStreamer::emitDwarfLocLabelDirective(Loc, Name); - OS << ".loc_label\t" << Name; - EmitEOL(); -} - -MCSymbol *MCAsmStreamer::getDwarfLineTableSymbol(unsigned CUID) { - // Always use the zeroth line table, since asm syntax only supports one line - // table for now. - return MCStreamer::getDwarfLineTableSymbol(0); -} - -bool MCAsmStreamer::emitCVFileDirective(unsigned FileNo, StringRef Filename, - ArrayRef<uint8_t> Checksum, - unsigned ChecksumKind) { - if (!getContext().getCVContext().addFile(*this, FileNo, Filename, Checksum, - ChecksumKind)) - return false; - - OS << "\t.cv_file\t" << FileNo << ' '; - PrintQuotedString(Filename, OS); - - if (!ChecksumKind) { - EmitEOL(); - return true; - } - - OS << ' '; - PrintQuotedString(toHex(Checksum), OS); - OS << ' ' << ChecksumKind; - - EmitEOL(); - return true; -} - -bool MCAsmStreamer::emitCVFuncIdDirective(unsigned FuncId) { - OS << "\t.cv_func_id " << FuncId << '\n'; - return MCStreamer::emitCVFuncIdDirective(FuncId); -} - -bool MCAsmStreamer::emitCVInlineSiteIdDirective(unsigned FunctionId, - unsigned IAFunc, - unsigned IAFile, - unsigned IALine, unsigned IACol, - SMLoc Loc) { - OS << "\t.cv_inline_site_id " << FunctionId << " within " << IAFunc - << " inlined_at " << IAFile << ' ' << IALine << ' ' << IACol << '\n'; - return MCStreamer::emitCVInlineSiteIdDirective(FunctionId, IAFunc, IAFile, - IALine, IACol, Loc); -} - -void MCAsmStreamer::emitCVLocDirective(unsigned FunctionId, unsigned FileNo, - unsigned Line, unsigned Column, - bool PrologueEnd, bool IsStmt, - StringRef FileName, SMLoc Loc) { - // Validate the directive. - if (!checkCVLocSection(FunctionId, FileNo, Loc)) - return; - - OS << "\t.cv_loc\t" << FunctionId << " " << FileNo << " " << Line << " " - << Column; - if (PrologueEnd) - OS << " prologue_end"; - - if (IsStmt) - OS << " is_stmt 1"; - - if (IsVerboseAsm) { - OS.PadToColumn(MAI->getCommentColumn()); - OS << MAI->getCommentString() << ' ' << FileName << ':' << Line << ':' - << Column; - } - EmitEOL(); -} - -void MCAsmStreamer::emitCVLinetableDirective(unsigned FunctionId, - const MCSymbol *FnStart, - const MCSymbol *FnEnd) { - OS << "\t.cv_linetable\t" << FunctionId << ", "; - FnStart->print(OS, MAI); - OS << ", "; - FnEnd->print(OS, MAI); - EmitEOL(); - this->MCStreamer::emitCVLinetableDirective(FunctionId, FnStart, FnEnd); -} - -void MCAsmStreamer::emitCVInlineLinetableDirective(unsigned PrimaryFunctionId, - unsigned SourceFileId, - unsigned SourceLineNum, - const MCSymbol *FnStartSym, - const MCSymbol *FnEndSym) { - OS << "\t.cv_inline_linetable\t" << PrimaryFunctionId << ' ' << SourceFileId - << ' ' << SourceLineNum << ' '; - FnStartSym->print(OS, MAI); - OS << ' '; - FnEndSym->print(OS, MAI); - EmitEOL(); - this->MCStreamer::emitCVInlineLinetableDirective( - PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); -} - -void MCAsmStreamer::PrintCVDefRangePrefix( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges) { - OS << "\t.cv_def_range\t"; - for (std::pair<const MCSymbol *, const MCSymbol *> Range : Ranges) { - OS << ' '; - Range.first->print(OS, MAI); - OS << ' '; - Range.second->print(OS, MAI); - } -} - -void MCAsmStreamer::emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeRegisterRelHeader DRHdr) { - PrintCVDefRangePrefix(Ranges); - OS << ", reg_rel, "; - OS << DRHdr.Register << ", " << DRHdr.Flags << ", " - << DRHdr.BasePointerOffset; - EmitEOL(); -} - -void MCAsmStreamer::emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeSubfieldRegisterHeader DRHdr) { - PrintCVDefRangePrefix(Ranges); - OS << ", subfield_reg, "; - OS << DRHdr.Register << ", " << DRHdr.OffsetInParent; - EmitEOL(); -} - -void MCAsmStreamer::emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeRegisterHeader DRHdr) { - PrintCVDefRangePrefix(Ranges); - OS << ", reg, "; - OS << DRHdr.Register; - EmitEOL(); -} - -void MCAsmStreamer::emitCVDefRangeDirective( - ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, - codeview::DefRangeFramePointerRelHeader DRHdr) { - PrintCVDefRangePrefix(Ranges); - OS << ", frame_ptr_rel, "; - OS << DRHdr.Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitCVStringTableDirective() { - OS << "\t.cv_stringtable"; - EmitEOL(); -} - -void MCAsmStreamer::emitCVFileChecksumsDirective() { - OS << "\t.cv_filechecksums"; - EmitEOL(); -} - -void MCAsmStreamer::emitCVFileChecksumOffsetDirective(unsigned FileNo) { - OS << "\t.cv_filechecksumoffset\t" << FileNo; - EmitEOL(); -} - -void MCAsmStreamer::emitCVFPOData(const MCSymbol *ProcSym, SMLoc L) { - OS << "\t.cv_fpo_data\t"; - ProcSym->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitIdent(StringRef IdentString) { - assert(MAI->hasIdentDirective() && ".ident directive not supported"); - OS << "\t.ident\t"; - PrintQuotedString(IdentString, OS); - EmitEOL(); -} - -void MCAsmStreamer::emitCFISections(bool EH, bool Debug, bool SFrame) { - MCStreamer::emitCFISections(EH, Debug, SFrame); - OS << "\t.cfi_sections "; - bool C = false; - if (EH) { - OS << ".eh_frame"; - C = true; - } - if (Debug) { - if (C) - OS << ", "; - OS << ".debug_frame"; - C = true; - } - if (SFrame) { - if (C) - OS << ", "; - OS << ".sframe"; - } - - EmitEOL(); -} - -void MCAsmStreamer::emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) { - OS << "\t.cfi_startproc"; - if (Frame.IsSimple) - OS << " simple"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIEndProcImpl(MCDwarfFrameInfo &Frame) { - MCStreamer::emitCFIEndProcImpl(Frame); - OS << "\t.cfi_endproc"; - EmitEOL(); -} - -void MCAsmStreamer::EmitRegisterName(int64_t Register) { - if (!MAI->useDwarfRegNumForCFI()) { - // User .cfi_* directives can use arbitrary DWARF register numbers, not - // just ones that map to LLVM register numbers and have known names. - // Fall back to using the original number directly if no name is known. - const MCRegisterInfo *MRI = getContext().getRegisterInfo(); - if (std::optional<MCRegister> LLVMRegister = - MRI->getLLVMRegNum(Register, true)) { - InstPrinter->printRegName(OS, *LLVMRegister); - return; - } - } - OS << Register; -} - -void MCAsmStreamer::emitCFIDefCfa(int64_t Register, int64_t Offset, SMLoc Loc) { - MCStreamer::emitCFIDefCfa(Register, Offset, Loc); - OS << "\t.cfi_def_cfa "; - EmitRegisterName(Register); - OS << ", " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIDefCfaOffset(int64_t Offset, SMLoc Loc) { - MCStreamer::emitCFIDefCfaOffset(Offset, Loc); - OS << "\t.cfi_def_cfa_offset " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset, - int64_t AddressSpace, SMLoc Loc) { - MCStreamer::emitCFILLVMDefAspaceCfa(Register, Offset, AddressSpace, Loc); - OS << "\t.cfi_llvm_def_aspace_cfa "; - EmitRegisterName(Register); - OS << ", " << Offset; - OS << ", " << AddressSpace; - EmitEOL(); -} - -static void PrintCFIEscape(llvm::formatted_raw_ostream &OS, StringRef Values) { - OS << "\t.cfi_escape "; - if (!Values.empty()) { - size_t e = Values.size() - 1; - for (size_t i = 0; i < e; ++i) - OS << format("0x%02x", uint8_t(Values[i])) << ", "; - OS << format("0x%02x", uint8_t(Values[e])); - } -} - -void MCAsmStreamer::emitCFIEscape(StringRef Values, SMLoc Loc) { - MCStreamer::emitCFIEscape(Values, Loc); - PrintCFIEscape(OS, Values); - EmitEOL(); -} - -void MCAsmStreamer::emitCFIGnuArgsSize(int64_t Size, SMLoc Loc) { - MCStreamer::emitCFIGnuArgsSize(Size, Loc); - - uint8_t Buffer[16] = { dwarf::DW_CFA_GNU_args_size }; - unsigned Len = encodeULEB128(Size, Buffer + 1) + 1; - - PrintCFIEscape(OS, StringRef((const char *)&Buffer[0], Len)); - EmitEOL(); -} - -void MCAsmStreamer::emitCFIDefCfaRegister(int64_t Register, SMLoc Loc) { - MCStreamer::emitCFIDefCfaRegister(Register, Loc); - OS << "\t.cfi_def_cfa_register "; - EmitRegisterName(Register); - EmitEOL(); -} - -void MCAsmStreamer::emitCFIOffset(int64_t Register, int64_t Offset, SMLoc Loc) { - MCStreamer::emitCFIOffset(Register, Offset, Loc); - OS << "\t.cfi_offset "; - EmitRegisterName(Register); - OS << ", " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIPersonality(const MCSymbol *Sym, - unsigned Encoding) { - MCStreamer::emitCFIPersonality(Sym, Encoding); - OS << "\t.cfi_personality " << Encoding << ", "; - Sym->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitCFILsda(const MCSymbol *Sym, unsigned Encoding) { - MCStreamer::emitCFILsda(Sym, Encoding); - OS << "\t.cfi_lsda " << Encoding << ", "; - Sym->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitCFIRememberState(SMLoc Loc) { - MCStreamer::emitCFIRememberState(Loc); - OS << "\t.cfi_remember_state"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIRestoreState(SMLoc Loc) { - MCStreamer::emitCFIRestoreState(Loc); - OS << "\t.cfi_restore_state"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIRestore(int64_t Register, SMLoc Loc) { - MCStreamer::emitCFIRestore(Register, Loc); - OS << "\t.cfi_restore "; - EmitRegisterName(Register); - EmitEOL(); -} - -void MCAsmStreamer::emitCFISameValue(int64_t Register, SMLoc Loc) { - MCStreamer::emitCFISameValue(Register, Loc); - OS << "\t.cfi_same_value "; - EmitRegisterName(Register); - EmitEOL(); -} - -void MCAsmStreamer::emitCFIRelOffset(int64_t Register, int64_t Offset, - SMLoc Loc) { - MCStreamer::emitCFIRelOffset(Register, Offset, Loc); - OS << "\t.cfi_rel_offset "; - EmitRegisterName(Register); - OS << ", " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIAdjustCfaOffset(int64_t Adjustment, SMLoc Loc) { - MCStreamer::emitCFIAdjustCfaOffset(Adjustment, Loc); - OS << "\t.cfi_adjust_cfa_offset " << Adjustment; - EmitEOL(); -} - -void MCAsmStreamer::emitCFISignalFrame() { - MCStreamer::emitCFISignalFrame(); - OS << "\t.cfi_signal_frame"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIUndefined(int64_t Register, SMLoc Loc) { - MCStreamer::emitCFIUndefined(Register, Loc); - OS << "\t.cfi_undefined "; - EmitRegisterName(Register); - EmitEOL(); -} - -void MCAsmStreamer::emitCFIRegister(int64_t Register1, int64_t Register2, - SMLoc Loc) { - MCStreamer::emitCFIRegister(Register1, Register2, Loc); - OS << "\t.cfi_register "; - EmitRegisterName(Register1); - OS << ", "; - EmitRegisterName(Register2); - EmitEOL(); -} - -void MCAsmStreamer::emitCFIWindowSave(SMLoc Loc) { - MCStreamer::emitCFIWindowSave(Loc); - OS << "\t.cfi_window_save"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFINegateRAState(SMLoc Loc) { - MCStreamer::emitCFINegateRAState(Loc); - OS << "\t.cfi_negate_ra_state"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFINegateRAStateWithPC(SMLoc Loc) { - MCStreamer::emitCFINegateRAStateWithPC(Loc); - OS << "\t.cfi_negate_ra_state_with_pc"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIReturnColumn(int64_t Register) { - MCStreamer::emitCFIReturnColumn(Register); - OS << "\t.cfi_return_column "; - EmitRegisterName(Register); - EmitEOL(); -} - -void MCAsmStreamer::emitCFILabelDirective(SMLoc Loc, StringRef Name) { - MCStreamer::emitCFILabelDirective(Loc, Name); - OS << "\t.cfi_label " << Name; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIBKeyFrame() { - MCStreamer::emitCFIBKeyFrame(); - OS << "\t.cfi_b_key_frame"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIMTETaggedFrame() { - MCStreamer::emitCFIMTETaggedFrame(); - OS << "\t.cfi_mte_tagged_frame"; - EmitEOL(); -} - -void MCAsmStreamer::emitCFIValOffset(int64_t Register, int64_t Offset, - SMLoc Loc) { - MCStreamer::emitCFIValOffset(Register, Offset, Loc); - OS << "\t.cfi_val_offset "; - EmitRegisterName(Register); - OS << ", " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) { - MCStreamer::emitWinCFIStartProc(Symbol, Loc); - - OS << ".seh_proc "; - Symbol->print(OS, MAI); - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIEndProc(SMLoc Loc) { - MCStreamer::emitWinCFIEndProc(Loc); - - OS << "\t.seh_endproc"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIFuncletOrFuncEnd(SMLoc Loc) { - MCStreamer::emitWinCFIFuncletOrFuncEnd(Loc); - - OS << "\t.seh_endfunclet"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFISplitChained(SMLoc Loc) { - MCStreamer::emitWinCFISplitChained(Loc); - - OS << "\t.seh_splitchained"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinEHHandler(const MCSymbol *Sym, bool Unwind, - bool Except, SMLoc Loc) { - MCStreamer::emitWinEHHandler(Sym, Unwind, Except, Loc); - - OS << "\t.seh_handler "; - Sym->print(OS, MAI); - char Marker = '@'; - const Triple &T = getContext().getTargetTriple(); - if (T.getArch() == Triple::arm || T.getArch() == Triple::thumb) - Marker = '%'; - if (Unwind) - OS << ", " << Marker << "unwind"; - if (Except) - OS << ", " << Marker << "except"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinEHHandlerData(SMLoc Loc) { - MCStreamer::emitWinEHHandlerData(Loc); - - // Switch sections. Don't call switchSection directly, because that will - // cause the section switch to be visible in the emitted assembly. - // We only do this so the section switch that terminates the handler - // data block is visible. - WinEH::FrameInfo *CurFrame = getCurrentWinFrameInfo(); - - // Do nothing if no frame is open. MCStreamer should've already reported an - // error. - if (!CurFrame) - return; - - MCSection *TextSec = &CurFrame->Function->getSection(); - MCSection *XData = getAssociatedXDataSection(TextSec); - switchSectionNoPrint(XData); - - OS << "\t.seh_handlerdata"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIPushReg(MCRegister Register, SMLoc Loc) { - MCStreamer::emitWinCFIPushReg(Register, Loc); - - OS << "\t.seh_pushreg "; - InstPrinter->printRegName(OS, Register); - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFISetFrame(MCRegister Register, unsigned Offset, - SMLoc Loc) { - MCStreamer::emitWinCFISetFrame(Register, Offset, Loc); - - OS << "\t.seh_setframe "; - InstPrinter->printRegName(OS, Register); - OS << ", " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIAllocStack(unsigned Size, SMLoc Loc) { - MCStreamer::emitWinCFIAllocStack(Size, Loc); - - OS << "\t.seh_stackalloc " << Size; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFISaveReg(MCRegister Register, unsigned Offset, - SMLoc Loc) { - MCStreamer::emitWinCFISaveReg(Register, Offset, Loc); - - OS << "\t.seh_savereg "; - InstPrinter->printRegName(OS, Register); - OS << ", " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFISaveXMM(MCRegister Register, unsigned Offset, - SMLoc Loc) { - MCStreamer::emitWinCFISaveXMM(Register, Offset, Loc); - - OS << "\t.seh_savexmm "; - InstPrinter->printRegName(OS, Register); - OS << ", " << Offset; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIPushFrame(bool Code, SMLoc Loc) { - MCStreamer::emitWinCFIPushFrame(Code, Loc); - - OS << "\t.seh_pushframe"; - if (Code) - OS << " @code"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIEndProlog(SMLoc Loc) { - MCStreamer::emitWinCFIEndProlog(Loc); - - OS << "\t.seh_endprologue"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIBeginEpilogue(SMLoc Loc) { - MCStreamer::emitWinCFIBeginEpilogue(Loc); - - OS << "\t.seh_startepilogue"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIEndEpilogue(SMLoc Loc) { - MCStreamer::emitWinCFIEndEpilogue(Loc); - - OS << "\t.seh_endepilogue"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIUnwindV2Start(SMLoc Loc) { - MCStreamer::emitWinCFIUnwindV2Start(Loc); - - OS << "\t.seh_unwindv2start"; - EmitEOL(); -} - -void MCAsmStreamer::emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) { - MCStreamer::emitWinCFIUnwindVersion(Version, Loc); - - OS << "\t.seh_unwindversion " << (unsigned)Version; - EmitEOL(); -} - -void MCAsmStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, - const MCSymbolRefExpr *To, - uint64_t Count) { - OS << "\t.cg_profile "; - From->getSymbol().print(OS, MAI); - OS << ", "; - To->getSymbol().print(OS, MAI); - OS << ", " << Count; - EmitEOL(); -} - -void MCAsmStreamer::emitInstruction(const MCInst &Inst, - const MCSubtargetInfo &STI) { - if (LFIRewriter && LFIRewriter->rewriteInst(Inst, *this, STI)) - return; - - if (CurFrag) { - MCSection *Sec = getCurrentSectionOnly(); - Sec->setHasInstructions(true); - } - - if (MAI->isAIX() && CurFrag) - // Now that a machine instruction has been assembled into this section, make - // a line entry for any .loc directive that has been seen. - MCDwarfLineEntry::make(this, getCurrentSectionOnly()); - - // Show the encoding in a comment if we have a code emitter. - addEncodingComment(Inst, STI); - - // Show the MCInst if enabled. - if (ShowInst) { - Inst.dump_pretty(getCommentOS(), InstPrinter.get(), "\n ", &getContext()); - getCommentOS() << "\n"; - } - - if(getTargetStreamer()) - getTargetStreamer()->prettyPrintAsm(*InstPrinter, 0, Inst, STI, OS); - else - InstPrinter->printInst(&Inst, 0, "", STI, OS); - - StringRef Comments = CommentToEmit; - if (Comments.size() && Comments.back() != '\n') - getCommentOS() << "\n"; - - EmitEOL(); -} - -void MCAsmStreamer::emitPseudoProbe(uint64_t Guid, uint64_t Index, - uint64_t Type, uint64_t Attr, - uint64_t Discriminator, - const MCPseudoProbeInlineStack &InlineStack, - MCSymbol *FnSym) { - OS << "\t.pseudoprobe\t" << Guid << " " << Index << " " << Type << " " << Attr; - if (Discriminator) - OS << " " << Discriminator; - // Emit inline stack like - // @ GUIDmain:3 @ GUIDCaller:1 @ GUIDDirectCaller:11 - for (const auto &Site : InlineStack) - OS << " @ " << std::get<0>(Site) << ":" << std::get<1>(Site); - - OS << " "; - FnSym->print(OS, MAI); - - EmitEOL(); -} - -void MCAsmStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, - const MCExpr *Expr, SMLoc) { - OS << "\t.reloc "; - MAI->printExpr(OS, Offset); - OS << ", " << Name; - if (Expr) { - OS << ", "; - MAI->printExpr(OS, *Expr); - } - EmitEOL(); -} - -void MCAsmStreamer::emitAddrsig() { - OS << "\t.addrsig"; - EmitEOL(); -} - -void MCAsmStreamer::emitAddrsigSym(const MCSymbol *Sym) { - OS << "\t.addrsig_sym "; - Sym->print(OS, MAI); - EmitEOL(); -} - -/// EmitRawText - If this file is backed by an assembly streamer, this dumps -/// the specified string in the output .s file. This capability is -/// indicated by the hasRawTextSupport() predicate. -void MCAsmStreamer::emitRawTextImpl(StringRef String) { - String.consume_back("\n"); - OS << String; - EmitEOL(); -} - -void MCAsmStreamer::finishImpl() { - // If we are generating dwarf for assembly source files dump out the sections. - if (getContext().getGenDwarfForAssembly()) - MCGenDwarfInfo::Emit(this); - - // Now it is time to emit debug line sections if target doesn't support .loc - // and .line directives. - if (MAI->isAIX()) { - MCDwarfLineTable::emit(this, getAssembler().getDWARFLinetableParams()); - return; - } - - // Emit the label for the line table, if requested - since the rest of the - // line table will be defined by .loc/.file directives, and not emitted - // directly, the label is the only work required here. - const auto &Tables = getContext().getMCDwarfLineTables(); - if (!Tables.empty()) { - assert(Tables.size() == 1 && "asm output only supports one line table"); - if (auto *Label = Tables.begin()->second.getLabel()) { - switchSection(getContext().getObjectFileInfo()->getDwarfLineSection(), 0); - emitLabel(Label); + OS << ", kind: "; + auto Info = getAssembler().getBackend().getFixupKindInfo(Kind); + if (F.isPCRel() && StringRef(Info.Name).starts_with("FK_Data_")) + OS << "FK_PCRel_" << (Info.TargetSize / 8); + else + OS << Info.Name; } + OS << '\n'; } } - -void MCAsmStreamer::emitDwarfUnitLength(uint64_t Length, const Twine &Comment) { - // If the assembler on some target fills in the DWARF unit length, we - // don't want to emit the length in the compiler. For example, the AIX - // assembler requires the assembly file with the unit length omitted from - // the debug section headers. In such cases, any label we placed occurs - // after the implied length field. We need to adjust the reference here - // to account for the offset introduced by the inserted length field. - if (MAI->isAIX()) - return; - MCStreamer::emitDwarfUnitLength(Length, Comment); -} - -MCSymbol *MCAsmStreamer::emitDwarfUnitLength(const Twine &Prefix, - const Twine &Comment) { - // If the assembler on some target fills in the DWARF unit length, we - // don't want to emit the length in the compiler. For example, the AIX - // assembler requires the assembly file with the unit length omitted from - // the debug section headers. In such cases, any label we placed occurs - // after the implied length field. We need to adjust the reference here - // to account for the offset introduced by the inserted length field. - if (MAI->isAIX()) - return getContext().createTempSymbol(Prefix + "_end"); - return MCStreamer::emitDwarfUnitLength(Prefix, Comment); -} - -void MCAsmStreamer::emitDwarfLineStartLabel(MCSymbol *StartSym) { - // If the assembler on some target fills in the DWARF unit length, we - // don't want to emit the length in the compiler. For example, the AIX - // assembler requires the assembly file with the unit length omitted from - // the debug section headers. In such cases, any label we placed occurs - // after the implied length field. We need to adjust the reference here - // to account for the offset introduced by the inserted length field. - MCContext &Ctx = getContext(); - if (MAI->isAIX()) { - MCSymbol *DebugLineSymTmp = Ctx.createTempSymbol("debug_line_"); - // Emit the symbol which does not contain the unit length field. - emitLabel(DebugLineSymTmp); - - // Adjust the outer reference to account for the offset introduced by the - // inserted length field. - unsigned LengthFieldSize = - dwarf::getUnitLengthFieldByteSize(Ctx.getDwarfFormat()); - const MCExpr *EntrySize = MCConstantExpr::create(LengthFieldSize, Ctx); - const MCExpr *OuterSym = MCBinaryExpr::createSub( - MCSymbolRefExpr::create(DebugLineSymTmp, Ctx), EntrySize, Ctx); - - emitAssignment(StartSym, OuterSym); - return; - } - MCStreamer::emitDwarfLineStartLabel(StartSym); -} - -void MCAsmStreamer::emitDwarfLineEndEntry(MCSection *Section, - MCSymbol *LastLabel, - MCSymbol *EndLabel) { - // If the targets write the raw debug line data for assembly output (We can - // not switch to Section and add the end symbol there for assembly output) - // we currently use the .text end label as any section end. This will not - // impact the debugability as we will jump to the caller of the last function - // in the section before we come into the .text end address. - assert(MAI->isAIX() && - ".loc should not be generated together with raw data!"); - - MCContext &Ctx = getContext(); - - // FIXME: use section end symbol as end of the Section. We need to consider - // the explicit sections and -ffunction-sections when we try to generate or - // find section end symbol for the Section. - MCSection *TextSection = Ctx.getObjectFileInfo()->getTextSection(); - assert(TextSection->hasEnded() && ".text section is not end!"); - - if (!EndLabel) - EndLabel = TextSection->getEndSymbol(Ctx); - const MCAsmInfo *AsmInfo = Ctx.getAsmInfo(); - emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, EndLabel, - AsmInfo->getCodePointerSize()); -} - -// Generate DWARF line sections for assembly mode without .loc/.file -void MCAsmStreamer::emitDwarfAdvanceLineAddr(int64_t LineDelta, - const MCSymbol *LastLabel, - const MCSymbol *Label, - unsigned PointerSize) { - assert(MAI->isAIX() && - ".loc/.file don't need raw data in debug line section!"); - - // Set to new address. - AddComment("Set address to " + Label->getName()); - emitIntValue(dwarf::DW_LNS_extended_op, 1); - emitULEB128IntValue(PointerSize + 1); - emitIntValue(dwarf::DW_LNE_set_address, 1); - emitSymbolValue(Label, PointerSize); - - if (!LastLabel) { - // Emit the sequence for the LineDelta (from 1) and a zero address delta. - AddComment("Start sequence"); - MCDwarfLineAddr::Emit(this, MCDwarfLineTableParams(), LineDelta, 0); - return; - } - - // INT64_MAX is a signal of the end of the section. Emit DW_LNE_end_sequence - // for the end of the section. - if (LineDelta == INT64_MAX) { - AddComment("End sequence"); - emitIntValue(dwarf::DW_LNS_extended_op, 1); - emitULEB128IntValue(1); - emitIntValue(dwarf::DW_LNE_end_sequence, 1); - return; - } - - // Advance line. - AddComment("Advance line " + Twine(LineDelta)); - emitIntValue(dwarf::DW_LNS_advance_line, 1); - emitSLEB128IntValue(LineDelta); - emitIntValue(dwarf::DW_LNS_copy, 1); -} - -MCStreamer *llvm::createAsmStreamer(MCContext &Context, - std::unique_ptr<formatted_raw_ostream> OS, - std::unique_ptr<MCInstPrinter> IP, - std::unique_ptr<MCCodeEmitter> CE, - std::unique_ptr<MCAsmBackend> MAB) { - return new MCAsmStreamer(Context, std::move(OS), std::move(IP), std::move(CE), - std::move(MAB)); -} diff --git a/llvm/lib/MC/MCGNUAsmStreamer.cpp b/llvm/lib/MC/MCGNUAsmStreamer.cpp new file mode 100644 index 0000000..c1dd1ef --- /dev/null +++ b/llvm/lib/MC/MCGNUAsmStreamer.cpp @@ -0,0 +1,2605 @@ +//===- lib/MC/MCGNUAsmStreamer.cpp - Text Assembly Output -------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/DebugInfo/CodeView/SymbolRecord.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCAsmStreamer.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCCodeView.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCLFIRewriter.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCPseudoProbe.h" +#include "llvm/MC/MCRegister.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbolXCOFF.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/Path.h" +#include <algorithm> +#include <optional> + +using namespace llvm; + +namespace { + +class MCGNUAsmStreamer final : public MCAsmStreamer { + std::unique_ptr<formatted_raw_ostream> OSOwner; + formatted_raw_ostream &OS; + const MCAsmInfo *MAI; + std::unique_ptr<MCInstPrinter> InstPrinter; + + SmallString<128> ExplicitCommentToEmit; + + bool EmittedSectionDirective = false; + + bool IsVerboseAsm = false; + bool ShowInst = false; + bool UseDwarfDirectory = false; + + void EmitRegisterName(int64_t Register); + void PrintQuotedString(StringRef Data, raw_ostream &OS) const; + void printDwarfFileDirective(unsigned FileNo, StringRef Directory, + StringRef Filename, + std::optional<MD5::MD5Result> Checksum, + std::optional<StringRef> Source, + bool UseDwarfDirectory, + raw_svector_ostream &OS) const; + void emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) override; + void emitCFIEndProcImpl(MCDwarfFrameInfo &Frame) override; + + /// Helper to emit common .loc directive flags, isa, and discriminator. + void emitDwarfLocDirectiveFlags(unsigned Flags, unsigned Isa, + unsigned Discriminator); + + /// Helper to emit the common suffix of .loc directives (flags, comment, EOL, + /// parent call). + void emitDwarfLocDirectiveSuffix(unsigned FileNo, unsigned Line, + unsigned Column, unsigned Flags, + unsigned Isa, unsigned Discriminator, + StringRef FileName, StringRef Comment); + +public: + MCGNUAsmStreamer(MCContext &Context, + std::unique_ptr<formatted_raw_ostream> OS, + std::unique_ptr<MCInstPrinter> Printer, + std::unique_ptr<MCCodeEmitter> Emitter, + std::unique_ptr<MCAsmBackend> AsmBackend) + : MCAsmStreamer(Context, std::move(Emitter), std::move(AsmBackend)), + OSOwner(std::move(OS)), OS(*OSOwner), MAI(Context.getAsmInfo()), + InstPrinter(std::move(Printer)) { + assert(InstPrinter); + if (Assembler->getBackendPtr()) + setAllowAutoPadding(Assembler->getBackend().allowAutoPadding()); + + Context.setUseNamesOnTempLabels(true); + + auto *TO = Context.getTargetOptions(); + if (!TO) + return; + IsVerboseAsm = TO->AsmVerbose; + if (IsVerboseAsm) + InstPrinter->setCommentStream(CommentStream); + ShowInst = TO->ShowMCInst; + switch (TO->MCUseDwarfDirectory) { + case MCTargetOptions::DisableDwarfDirectory: + UseDwarfDirectory = false; + break; + case MCTargetOptions::EnableDwarfDirectory: + UseDwarfDirectory = true; + break; + case MCTargetOptions::DefaultDwarfDirectory: + UseDwarfDirectory = + Context.getAsmInfo()->enableDwarfFileDirectoryDefault(); + break; + } + } + + inline void EmitEOL() { + // Dump Explicit Comments here. + emitExplicitComments(); + // If we don't have any comments, just emit a \n. + if (!IsVerboseAsm) { + OS << '\n'; + return; + } + EmitCommentsAndEOL(); + } + + void emitSyntaxDirective(StringRef Syntax, StringRef Options) override; + + void EmitCommentsAndEOL(); + + /// Return true if this streamer supports verbose assembly at all. + bool isVerboseAsm() const override { return IsVerboseAsm; } + + /// Do we support EmitRawText? + bool hasRawTextSupport() const override { return true; } + + /// Add a comment that can be emitted to the generated .s file to make the + /// output of the compiler more readable. This only affects the + /// MCGNUAsmStreamer and only when verbose assembly output is enabled. + void AddComment(const Twine &T, bool EOL = true) override; + + void emitRawComment(const Twine &T, bool TabPrefix = true) override; + + void addExplicitComment(const Twine &T) override; + void emitExplicitComments() override; + + /// Emit a blank line to a .s file to pretty it up. + void addBlankLine() override { EmitEOL(); } + + /// @name MCStreamer Interface + /// @{ + + void switchSection(MCSection *Section, uint32_t Subsection) override; + bool popSection() override; + + void emitELFSymverDirective(const MCSymbol *OriginalSym, StringRef Name, + bool KeepOriginalSym) override; + + void emitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) override; + + void emitGNUAttribute(unsigned Tag, unsigned Value) override; + + StringRef getMnemonic(const MCInst &MI) const override { + auto [Ptr, Bits] = InstPrinter->getMnemonic(MI); + assert((Bits != 0 || Ptr == nullptr) && + "Invalid char pointer for instruction with no mnemonic"); + return Ptr; + } + + void emitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; + + void emitSubsectionsViaSymbols() override; + void emitLinkerOptions(ArrayRef<std::string> Options) override; + void emitDataRegion(MCDataRegionType Kind) override; + void emitVersionMin(MCVersionMinType Kind, unsigned Major, unsigned Minor, + unsigned Update, VersionTuple SDKVersion) override; + void emitBuildVersion(unsigned Platform, unsigned Major, unsigned Minor, + unsigned Update, VersionTuple SDKVersion) override; + void emitDarwinTargetVariantBuildVersion(unsigned Platform, unsigned Major, + unsigned Minor, unsigned Update, + VersionTuple SDKVersion) override; + + void emitAssignment(MCSymbol *Symbol, const MCExpr *Value) override; + void emitConditionalAssignment(MCSymbol *Symbol, + const MCExpr *Value) override; + void emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) override; + bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; + + void emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) override; + void beginCOFFSymbolDef(const MCSymbol *Symbol) override; + void emitCOFFSymbolStorageClass(int StorageClass) override; + void emitCOFFSymbolType(int Type) override; + void endCOFFSymbolDef() override; + void emitCOFFSafeSEH(MCSymbol const *Symbol) override; + void emitCOFFSymbolIndex(MCSymbol const *Symbol) override; + void emitCOFFSectionIndex(MCSymbol const *Symbol) override; + void emitCOFFSecRel32(MCSymbol const *Symbol, uint64_t Offset) override; + void emitCOFFImgRel32(MCSymbol const *Symbol, int64_t Offset) override; + void emitCOFFSecNumber(MCSymbol const *Symbol) override; + void emitCOFFSecOffset(MCSymbol const *Symbol) override; + void emitXCOFFLocalCommonSymbol(MCSymbol *LabelSym, uint64_t Size, + MCSymbol *CsectSym, Align Alignment) override; + void emitXCOFFSymbolLinkageWithVisibility(MCSymbol *Symbol, + MCSymbolAttr Linkage, + MCSymbolAttr Visibility) override; + void emitXCOFFRenameDirective(const MCSymbol *Name, + StringRef Rename) override; + + void emitXCOFFRefDirective(const MCSymbol *Symbol) override; + + void emitXCOFFExceptDirective(const MCSymbol *Symbol, const MCSymbol *Trap, + unsigned Lang, unsigned Reason, + unsigned FunctionSize, bool hasDebug) override; + void emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) override; + + void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; + void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment) override; + + /// Emit a local common (.lcomm) symbol. + /// + /// @param Symbol - The common symbol to emit. + /// @param Size - The size of the common symbol. + /// @param ByteAlignment - The alignment of the common symbol in bytes. + void emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment) override; + + void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, + uint64_t Size = 0, Align ByteAlignment = Align(1), + SMLoc Loc = SMLoc()) override; + + void emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment = Align(1)) override; + + void emitBinaryData(StringRef Data) override; + + void emitBytes(StringRef Data) override; + + void emitValueImpl(const MCExpr *Value, unsigned Size, + SMLoc Loc = SMLoc()) override; + void emitIntValue(uint64_t Value, unsigned Size) override; + void emitIntValueInHex(uint64_t Value, unsigned Size) override; + void emitIntValueInHexWithPadding(uint64_t Value, unsigned Size) override; + + void emitULEB128Value(const MCExpr *Value) override; + + void emitSLEB128Value(const MCExpr *Value) override; + + 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; + + void emitAlignmentDirective(uint64_t ByteAlignment, + std::optional<int64_t> Value, unsigned ValueSize, + unsigned MaxBytesToEmit); + + void emitValueToAlignment(Align Alignment, int64_t Fill = 0, + uint8_t FillLen = 1, + unsigned MaxBytesToEmit = 0) override; + + void emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI, + unsigned MaxBytesToEmit = 0) override; + void emitPrefAlign(Align Alignment) override; + + void emitValueToOffset(const MCExpr *Offset, unsigned char Value, + SMLoc Loc) override; + + void emitFileDirective(StringRef Filename) override; + void emitFileDirective(StringRef Filename, StringRef CompilerVersion, + StringRef TimeStamp, StringRef Description) override; + Expected<unsigned> tryEmitDwarfFileDirective( + unsigned FileNo, StringRef Directory, StringRef Filename, + std::optional<MD5::MD5Result> Checksum = std::nullopt, + std::optional<StringRef> Source = std::nullopt, + unsigned CUID = 0) override; + void emitDwarfFile0Directive(StringRef Directory, StringRef Filename, + std::optional<MD5::MD5Result> Checksum, + std::optional<StringRef> Source, + unsigned CUID = 0) override; + void emitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, + unsigned Flags, unsigned Isa, + unsigned Discriminator, StringRef FileName, + StringRef Location = {}) override; + void emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name) override; + + /// This is same as emitDwarfLocDirective, except also emits inlined function + /// and inlined callsite information. + void emitDwarfLocDirectiveWithInlinedAt(unsigned FileNo, unsigned Line, + unsigned Column, unsigned FileIA, + unsigned LineIA, unsigned ColIA, + const MCSymbol *Sym, unsigned Flags, + unsigned Isa, unsigned Discriminator, + StringRef FileName, + StringRef Comment = {}) override; + + MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override; + + bool emitCVFileDirective(unsigned FileNo, StringRef Filename, + ArrayRef<uint8_t> Checksum, + unsigned ChecksumKind) override; + bool emitCVFuncIdDirective(unsigned FuncId) override; + bool emitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc, + unsigned IAFile, unsigned IALine, + unsigned IACol, SMLoc Loc) override; + void emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, + unsigned Column, bool PrologueEnd, bool IsStmt, + StringRef FileName, SMLoc Loc) override; + void emitCVLinetableDirective(unsigned FunctionId, const MCSymbol *FnStart, + const MCSymbol *FnEnd) override; + void emitCVInlineLinetableDirective(unsigned PrimaryFunctionId, + unsigned SourceFileId, + unsigned SourceLineNum, + const MCSymbol *FnStartSym, + const MCSymbol *FnEndSym) override; + + void PrintCVDefRangePrefix( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges); + + void emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeRegisterRelHeader DRHdr) override; + + void emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeSubfieldRegisterHeader DRHdr) override; + + void emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeRegisterHeader DRHdr) override; + + void emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeFramePointerRelHeader DRHdr) override; + + void emitCVStringTableDirective() override; + void emitCVFileChecksumsDirective() override; + void emitCVFileChecksumOffsetDirective(unsigned FileNo) override; + void emitCVFPOData(const MCSymbol *ProcSym, SMLoc L) override; + + void emitIdent(StringRef IdentString) override; + void emitCFIBKeyFrame() override; + void emitCFIMTETaggedFrame() override; + void emitCFISections(bool EH, bool Debug, bool SFrame) override; + void emitCFIDefCfa(int64_t Register, int64_t Offset, SMLoc Loc) override; + void emitCFIDefCfaOffset(int64_t Offset, SMLoc Loc) override; + void emitCFIDefCfaRegister(int64_t Register, SMLoc Loc) override; + void emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset, + int64_t AddressSpace, SMLoc Loc) override; + void emitCFIOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; + void emitCFIPersonality(const MCSymbol *Sym, unsigned Encoding) override; + void emitCFILsda(const MCSymbol *Sym, unsigned Encoding) override; + void emitCFIRememberState(SMLoc Loc) override; + void emitCFIRestoreState(SMLoc Loc) override; + void emitCFIRestore(int64_t Register, SMLoc Loc) override; + void emitCFISameValue(int64_t Register, SMLoc Loc) override; + void emitCFIRelOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; + void emitCFIAdjustCfaOffset(int64_t Adjustment, SMLoc Loc) override; + void emitCFIEscape(StringRef Values, SMLoc Loc) override; + void emitCFIGnuArgsSize(int64_t Size, SMLoc Loc) override; + void emitCFISignalFrame() override; + void emitCFIUndefined(int64_t Register, SMLoc Loc) override; + void emitCFIRegister(int64_t Register1, int64_t Register2, + SMLoc Loc) override; + void emitCFIWindowSave(SMLoc Loc) override; + void emitCFINegateRAState(SMLoc Loc) override; + void emitCFINegateRAStateWithPC(SMLoc Loc) override; + void emitCFIReturnColumn(int64_t Register) override; + void emitCFILabelDirective(SMLoc Loc, StringRef Name) override; + void emitCFIValOffset(int64_t Register, int64_t Offset, SMLoc Loc) override; + + void emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override; + void emitWinCFIEndProc(SMLoc Loc) override; + void emitWinCFIFuncletOrFuncEnd(SMLoc Loc) override; + void emitWinCFISplitChained(SMLoc Loc) override; + void emitWinCFIPushReg(MCRegister Register, SMLoc Loc) override; + void emitWinCFISetFrame(MCRegister Register, unsigned Offset, + SMLoc Loc) override; + void emitWinCFIAllocStack(unsigned Size, SMLoc Loc) override; + void emitWinCFISaveReg(MCRegister Register, unsigned Offset, + SMLoc Loc) override; + void emitWinCFISaveXMM(MCRegister Register, unsigned Offset, + SMLoc Loc) override; + void emitWinCFIPushFrame(bool Code, SMLoc Loc) override; + void emitWinCFIEndProlog(SMLoc Loc) override; + void emitWinCFIBeginEpilogue(SMLoc Loc) override; + void emitWinCFIEndEpilogue(SMLoc Loc) override; + void emitWinCFIUnwindV2Start(SMLoc Loc) override; + void emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) override; + + void emitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except, + SMLoc Loc) override; + void emitWinEHHandlerData(SMLoc Loc) override; + + void emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, uint64_t Count) override; + + void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; + + void emitPseudoProbe(uint64_t Guid, uint64_t Index, uint64_t Type, + uint64_t Attr, uint64_t Discriminator, + const MCPseudoProbeInlineStack &InlineStack, + MCSymbol *FnSym) override; + + void emitRelocDirective(const MCExpr &Offset, StringRef Name, + const MCExpr *Expr, SMLoc Loc) override; + + void emitAddrsig() override; + void emitAddrsigSym(const MCSymbol *Sym) override; + + /// If this file is backed by an assembly streamer, this dumps the specified + /// string in the output .s file. This capability is indicated by the + /// hasRawTextSupport() predicate. + void emitRawTextImpl(StringRef String) override; + + void finishImpl() override; + + void emitDwarfUnitLength(uint64_t Length, const Twine &Comment) override; + + MCSymbol *emitDwarfUnitLength(const Twine &Prefix, + const Twine &Comment) override; + + void emitDwarfLineStartLabel(MCSymbol *StartSym) override; + + void emitDwarfLineEndEntry(MCSection *Section, MCSymbol *LastLabel, + MCSymbol *EndLabel = nullptr) override; + + void emitDwarfAdvanceLineAddr(int64_t LineDelta, const MCSymbol *LastLabel, + const MCSymbol *Label, + unsigned PointerSize) override; +}; + +} // end anonymous namespace. + +void MCGNUAsmStreamer::AddComment(const Twine &T, bool EOL) { + if (!IsVerboseAsm) return; + + T.toVector(CommentToEmit); + + if (EOL) + CommentToEmit.push_back('\n'); // Place comment in a new line. +} + +void MCGNUAsmStreamer::EmitCommentsAndEOL() { + if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0) { + OS << '\n'; + return; + } + + StringRef Comments = CommentToEmit; + + assert(Comments.back() == '\n' && + "Comment array not newline terminated"); + do { + // Emit a line of comments. + OS.PadToColumn(MAI->getCommentColumn()); + size_t Position = Comments.find('\n'); + OS << MAI->getCommentString() << ' ' << Comments.substr(0, Position) <<'\n'; + + Comments = Comments.substr(Position+1); + } while (!Comments.empty()); + + CommentToEmit.clear(); +} + +static inline int64_t truncateToSize(int64_t Value, unsigned Bytes) { + assert(Bytes > 0 && Bytes <= 8 && "Invalid size!"); + return Value & ((uint64_t) (int64_t) -1 >> (64 - Bytes * 8)); +} + +void MCGNUAsmStreamer::emitRawComment(const Twine &T, bool TabPrefix) { + if (TabPrefix) + OS << '\t'; + OS << MAI->getCommentString() << T; + EmitEOL(); +} + +void MCGNUAsmStreamer::addExplicitComment(const Twine &T) { + StringRef c = T.getSingleStringRef(); + if (c == MAI->getSeparatorString()) + return; + if (c.starts_with(StringRef("//"))) { + ExplicitCommentToEmit.append("\t"); + ExplicitCommentToEmit.append(MAI->getCommentString()); + // drop // + ExplicitCommentToEmit.append(c.substr(2).str()); + } else if (c.starts_with(StringRef("/*"))) { + size_t p = 2, len = c.size() - 2; + // emit each line in comment as separate newline. + do { + size_t newp = std::min(len, c.find_first_of("\r\n", p)); + ExplicitCommentToEmit.append("\t"); + ExplicitCommentToEmit.append(MAI->getCommentString()); + ExplicitCommentToEmit.append(c.slice(p, newp).str()); + // If we have another line in this comment add line + if (newp < len) + ExplicitCommentToEmit.append("\n"); + p = newp + 1; + } while (p < len); + } else if (c.starts_with(StringRef(MAI->getCommentString()))) { + ExplicitCommentToEmit.append("\t"); + ExplicitCommentToEmit.append(c.str()); + } else if (c.front() == '#') { + + ExplicitCommentToEmit.append("\t"); + ExplicitCommentToEmit.append(MAI->getCommentString()); + ExplicitCommentToEmit.append(c.substr(1).str()); + } else + assert(false && "Unexpected Assembly Comment"); + // full line comments immediately output + if (c.back() == '\n') + emitExplicitComments(); +} + +void MCGNUAsmStreamer::emitExplicitComments() { + StringRef Comments = ExplicitCommentToEmit; + if (!Comments.empty()) + OS << Comments; + ExplicitCommentToEmit.clear(); +} + +void MCGNUAsmStreamer::switchSection(MCSection *Section, uint32_t Subsection) { + MCSectionSubPair Cur = getCurrentSection(); + if (!EmittedSectionDirective || + MCSectionSubPair(Section, Subsection) != Cur) { + EmittedSectionDirective = true; + if (MCTargetStreamer *TS = getTargetStreamer()) { + TS->changeSection(Cur.first, Section, Subsection, OS); + } else { + MAI->printSwitchToSection(*Section, Subsection, + getContext().getTargetTriple(), OS); + } + } + MCStreamer::switchSection(Section, Subsection); +} + +bool MCGNUAsmStreamer::popSection() { + if (!MCStreamer::popSection()) + return false; + auto [Sec, Subsec] = getCurrentSection(); + MAI->printSwitchToSection(*Sec, Subsec, getContext().getTargetTriple(), OS); + return true; +} + +void MCGNUAsmStreamer::emitELFSymverDirective(const MCSymbol *OriginalSym, + StringRef Name, + bool KeepOriginalSym) { + OS << ".symver "; + OriginalSym->print(OS, MAI); + OS << ", " << Name; + if (!KeepOriginalSym && !Name.contains("@@@")) + OS << ", remove"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { + MCStreamer::emitLabel(Symbol, Loc); + // FIXME: Fix CodeGen/AArch64/arm64ec-varargs.ll. emitLabel is followed by + // setVariableValue, leading to an assertion failure if setOffset(0) is + // called. + if (!Symbol->isVariable() && + getContext().getObjectFileType() != MCContext::IsCOFF) + Symbol->setOffset(0); + + Symbol->print(OS, MAI); + OS << MAI->getLabelSuffix(); + + EmitEOL(); +} + +void MCGNUAsmStreamer::emitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) { + StringRef str = MCLOHIdToName(Kind); + +#ifndef NDEBUG + int NbArgs = MCLOHIdToNbArgs(Kind); + assert(NbArgs != -1 && ((size_t)NbArgs) == Args.size() && "Malformed LOH!"); + assert(str != "" && "Invalid LOH name"); +#endif + + OS << "\t" << MCLOHDirectiveName() << " " << str << "\t"; + bool IsFirst = true; + for (const MCSymbol *Arg : Args) { + if (!IsFirst) + OS << ", "; + IsFirst = false; + Arg->print(OS, MAI); + } + EmitEOL(); +} + +void MCGNUAsmStreamer::emitGNUAttribute(unsigned Tag, unsigned Value) { + OS << "\t.gnu_attribute " << Tag << ", " << Value << "\n"; +} + +void MCGNUAsmStreamer::emitSubsectionsViaSymbols() { + OS << ".subsections_via_symbols\n"; +} + +void MCGNUAsmStreamer::emitLinkerOptions(ArrayRef<std::string> Options) { + assert(!Options.empty() && "At least one option is required!"); + OS << "\t.linker_option \"" << Options[0] << '"'; + for (const std::string &Opt : llvm::drop_begin(Options)) + OS << ", " << '"' << Opt << '"'; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitDataRegion(MCDataRegionType Kind) { + if (!MAI->doesSupportDataRegionDirectives()) + return; + switch (Kind) { + case MCDR_DataRegion: OS << "\t.data_region"; break; + case MCDR_DataRegionJT8: OS << "\t.data_region jt8"; break; + case MCDR_DataRegionJT16: OS << "\t.data_region jt16"; break; + case MCDR_DataRegionJT32: OS << "\t.data_region jt32"; break; + case MCDR_DataRegionEnd: OS << "\t.end_data_region"; break; + } + EmitEOL(); +} + +static const char *getVersionMinDirective(MCVersionMinType Type) { + switch (Type) { + case MCVM_WatchOSVersionMin: return ".watchos_version_min"; + case MCVM_TvOSVersionMin: return ".tvos_version_min"; + case MCVM_IOSVersionMin: return ".ios_version_min"; + case MCVM_OSXVersionMin: return ".macosx_version_min"; + } + llvm_unreachable("Invalid MC version min type"); +} + +static void EmitSDKVersionSuffix(raw_ostream &OS, + const VersionTuple &SDKVersion) { + if (SDKVersion.empty()) + return; + OS << '\t' << "sdk_version " << SDKVersion.getMajor(); + if (auto Minor = SDKVersion.getMinor()) { + OS << ", " << *Minor; + if (auto Subminor = SDKVersion.getSubminor()) { + OS << ", " << *Subminor; + } + } +} + +void MCGNUAsmStreamer::emitVersionMin(MCVersionMinType Type, unsigned Major, + unsigned Minor, unsigned Update, + VersionTuple SDKVersion) { + OS << '\t' << getVersionMinDirective(Type) << ' ' << Major << ", " << Minor; + if (Update) + OS << ", " << Update; + EmitSDKVersionSuffix(OS, SDKVersion); + EmitEOL(); +} + +static const char *getPlatformName(MachO::PlatformType Type) { + switch (Type) { +#define PLATFORM(platform, id, name, build_name, target, tapi_target, \ + marketing) \ + case MachO::PLATFORM_##platform: \ + return #build_name; +#include "llvm/BinaryFormat/MachO.def" + } + llvm_unreachable("Invalid Mach-O platform type"); +} + +void MCGNUAsmStreamer::emitBuildVersion(unsigned Platform, unsigned Major, + unsigned Minor, unsigned Update, + VersionTuple SDKVersion) { + const char *PlatformName = getPlatformName((MachO::PlatformType)Platform); + OS << "\t.build_version " << PlatformName << ", " << Major << ", " << Minor; + if (Update) + OS << ", " << Update; + EmitSDKVersionSuffix(OS, SDKVersion); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitDarwinTargetVariantBuildVersion( + unsigned Platform, unsigned Major, unsigned Minor, unsigned Update, + VersionTuple SDKVersion) { + emitBuildVersion(Platform, Major, Minor, Update, SDKVersion); +} + +void MCGNUAsmStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) { + bool UseSet = MAI->usesSetToEquateSymbol(); + if (UseSet) + OS << ".set "; + Symbol->print(OS, MAI); + OS << (UseSet ? ", " : " = "); + MAI->printExpr(OS, *Value); + + EmitEOL(); + MCStreamer::emitAssignment(Symbol, Value); +} + +void MCGNUAsmStreamer::emitConditionalAssignment(MCSymbol *Symbol, + const MCExpr *Value) { + OS << ".lto_set_conditional "; + Symbol->print(OS, MAI); + OS << ", "; + MAI->printExpr(OS, *Value); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWeakReference(MCSymbol *Alias, + const MCSymbol *Symbol) { + OS << ".weakref "; + Alias->print(OS, MAI); + OS << ", "; + Symbol->print(OS, MAI); + EmitEOL(); +} + +bool MCGNUAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol, + MCSymbolAttr Attribute) { + switch (Attribute) { + case MCSA_Invalid: llvm_unreachable("Invalid symbol attribute"); + case MCSA_ELF_TypeFunction: /// .type _foo, STT_FUNC # aka @function + case MCSA_ELF_TypeIndFunction: /// .type _foo, STT_GNU_IFUNC + case MCSA_ELF_TypeObject: /// .type _foo, STT_OBJECT # aka @object + case MCSA_ELF_TypeTLS: /// .type _foo, STT_TLS # aka @tls_object + case MCSA_ELF_TypeCommon: /// .type _foo, STT_COMMON # aka @common + case MCSA_ELF_TypeNoType: /// .type _foo, STT_NOTYPE # aka @notype + case MCSA_ELF_TypeGnuUniqueObject: /// .type _foo, @gnu_unique_object + if (!MAI->hasDotTypeDotSizeDirective()) + return false; // Symbol attribute not supported + OS << "\t.type\t"; + Symbol->print(OS, MAI); + OS << ',' << ((MAI->getCommentString()[0] != '@') ? '@' : '%'); + switch (Attribute) { + default: return false; + case MCSA_ELF_TypeFunction: OS << "function"; break; + case MCSA_ELF_TypeIndFunction: OS << "gnu_indirect_function"; break; + case MCSA_ELF_TypeObject: OS << "object"; break; + case MCSA_ELF_TypeTLS: OS << "tls_object"; break; + case MCSA_ELF_TypeCommon: OS << "common"; break; + case MCSA_ELF_TypeNoType: OS << "notype"; break; + case MCSA_ELF_TypeGnuUniqueObject: OS << "gnu_unique_object"; break; + } + EmitEOL(); + return true; + case MCSA_Global: // .globl/.global + OS << MAI->getGlobalDirective(); + break; + case MCSA_LGlobal: OS << "\t.lglobl\t"; break; + case MCSA_Hidden: OS << "\t.hidden\t"; break; + case MCSA_IndirectSymbol: OS << "\t.indirect_symbol\t"; break; + case MCSA_Internal: OS << "\t.internal\t"; break; + case MCSA_LazyReference: OS << "\t.lazy_reference\t"; break; + case MCSA_Local: OS << "\t.local\t"; break; + case MCSA_NoDeadStrip: + if (!MAI->hasNoDeadStrip()) + return false; + OS << "\t.no_dead_strip\t"; + break; + case MCSA_SymbolResolver: OS << "\t.symbol_resolver\t"; break; + case MCSA_AltEntry: OS << "\t.alt_entry\t"; break; + case MCSA_PrivateExtern: + OS << "\t.private_extern\t"; + break; + case MCSA_Protected: OS << "\t.protected\t"; break; + case MCSA_Reference: OS << "\t.reference\t"; break; + case MCSA_Extern: + OS << "\t.extern\t"; + break; + case MCSA_Weak: OS << MAI->getWeakDirective(); break; + case MCSA_WeakDefinition: + OS << "\t.weak_definition\t"; + break; + // .weak_reference + case MCSA_WeakReference: OS << MAI->getWeakRefDirective(); break; + case MCSA_WeakDefAutoPrivate: OS << "\t.weak_def_can_be_hidden\t"; break; + case MCSA_Cold: + // Assemblers currently do not support a .cold directive. + case MCSA_Exported: + // Non-AIX assemblers currently do not support exported visibility. + case MCSA_OSLinkage: + case MCSA_XPLinkage: + // Only for HLASM. + return false; + case MCSA_Memtag: + OS << "\t.memtag\t"; + break; + case MCSA_WeakAntiDep: + OS << "\t.weak_anti_dep\t"; + break; + } + + Symbol->print(OS, MAI); + EmitEOL(); + + return true; +} + +void MCGNUAsmStreamer::emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) { + OS << ".desc" << ' '; + Symbol->print(OS, MAI); + OS << ',' << DescValue; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitSyntaxDirective(StringRef Syntax, + StringRef Options) { + OS << "\t." << Syntax << "_syntax"; + if (!Options.empty()) + OS << " " << Options; + EmitEOL(); +} + +void MCGNUAsmStreamer::beginCOFFSymbolDef(const MCSymbol *Symbol) { + OS << "\t.def\t"; + Symbol->print(OS, MAI); + OS << ';'; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSymbolStorageClass(int StorageClass) { + OS << "\t.scl\t" << StorageClass << ';'; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSymbolType(int Type) { + OS << "\t.type\t" << Type << ';'; + EmitEOL(); +} + +void MCGNUAsmStreamer::endCOFFSymbolDef() { + OS << "\t.endef"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSafeSEH(MCSymbol const *Symbol) { + OS << "\t.safeseh\t"; + Symbol->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSymbolIndex(MCSymbol const *Symbol) { + OS << "\t.symidx\t"; + Symbol->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSectionIndex(MCSymbol const *Symbol) { + OS << "\t.secidx\t"; + Symbol->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSecRel32(MCSymbol const *Symbol, + uint64_t Offset) { + OS << "\t.secrel32\t"; + Symbol->print(OS, MAI); + if (Offset != 0) + OS << '+' << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFImgRel32(MCSymbol const *Symbol, + int64_t Offset) { + OS << "\t.rva\t"; + Symbol->print(OS, MAI); + if (Offset > 0) + OS << '+' << Offset; + else if (Offset < 0) + OS << '-' << -Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSecNumber(MCSymbol const *Symbol) { + OS << "\t.secnum\t"; + Symbol->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCOFFSecOffset(MCSymbol const *Symbol) { + OS << "\t.secoffset\t"; + Symbol->print(OS, MAI); + EmitEOL(); +} + +// We need an XCOFF-specific version of this directive as the AIX syntax +// requires a QualName argument identifying the csect name and storage mapping +// class to appear before the alignment if we are specifying it. +void MCGNUAsmStreamer::emitXCOFFLocalCommonSymbol(MCSymbol *LabelSym, + uint64_t Size, + MCSymbol *CsectSym, + Align Alignment) { + assert(MAI->getLCOMMDirectiveAlignmentType() == LCOMM::Log2Alignment && + "We only support writing log base-2 alignment format with XCOFF."); + + OS << "\t.lcomm\t"; + LabelSym->print(OS, MAI); + OS << ',' << Size << ','; + CsectSym->print(OS, MAI); + OS << ',' << Log2(Alignment); + + EmitEOL(); + + // Print symbol's rename (original name contains invalid character(s)) if + // there is one. + auto *XSym = static_cast<MCSymbolXCOFF *>(CsectSym); + if (XSym->hasRename()) + emitXCOFFRenameDirective(XSym, XSym->getSymbolTableName()); +} + +void MCGNUAsmStreamer::emitXCOFFSymbolLinkageWithVisibility( + MCSymbol *Symbol, MCSymbolAttr Linkage, MCSymbolAttr Visibility) { + auto &Sym = static_cast<MCSymbolXCOFF &>(*Symbol); + switch (Linkage) { + case MCSA_Global: + OS << MAI->getGlobalDirective(); + break; + case MCSA_Weak: + OS << MAI->getWeakDirective(); + break; + case MCSA_Extern: + OS << "\t.extern\t"; + break; + case MCSA_LGlobal: + OS << "\t.lglobl\t"; + break; + default: + report_fatal_error("unhandled linkage type"); + } + + Symbol->print(OS, MAI); + + switch (Visibility) { + case MCSA_Invalid: + // Nothing to do. + break; + case MCSA_Hidden: + OS << ",hidden"; + break; + case MCSA_Protected: + OS << ",protected"; + break; + case MCSA_Exported: + OS << ",exported"; + break; + default: + report_fatal_error("unexpected value for Visibility type"); + } + EmitEOL(); + + // Print symbol's rename (original name contains invalid character(s)) if + // there is one. + if (Sym.hasRename()) + emitXCOFFRenameDirective(&Sym, Sym.getSymbolTableName()); +} + +void MCGNUAsmStreamer::emitXCOFFRenameDirective(const MCSymbol *Name, + StringRef Rename) { + OS << "\t.rename\t"; + Name->print(OS, MAI); + const char DQ = '"'; + OS << ',' << DQ; + for (char C : Rename) { + // To escape a double quote character, the character should be doubled. + if (C == DQ) + OS << DQ; + OS << C; + } + OS << DQ; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitXCOFFRefDirective(const MCSymbol *Symbol) { + OS << "\t.ref "; + Symbol->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitXCOFFExceptDirective(const MCSymbol *Symbol, + const MCSymbol *Trap, + unsigned Lang, unsigned Reason, + unsigned FunctionSize, + bool hasDebug) { + OS << "\t.except\t"; + Symbol->print(OS, MAI); + OS << ", " << Lang << ", " << Reason; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitXCOFFCInfoSym(StringRef Name, StringRef Metadata) { + const char InfoDirective[] = "\t.info "; + const char *Separator = ", "; + constexpr int WordSize = sizeof(uint32_t); + + // Start by emitting the .info pseudo-op and C_INFO symbol name. + OS << InfoDirective; + PrintQuotedString(Name, OS); + OS << Separator; + + size_t MetadataSize = Metadata.size(); + + // Emit the 4-byte length of the metadata. + OS << format_hex(MetadataSize, 10) << Separator; + + // Nothing left to do if there's no metadata. + if (MetadataSize == 0) { + EmitEOL(); + return; + } + + // Metadata needs to be padded out to an even word size when generating + // assembly because the .info pseudo-op can only generate words of data. We + // apply the same restriction to the object case for consistency, however the + // linker doesn't require padding, so it will only save bytes specified by the + // length and discard any padding. + uint32_t PaddedSize = alignTo(MetadataSize, WordSize); + uint32_t PaddingSize = PaddedSize - MetadataSize; + + // Write out the payload a word at a time. + // + // The assembler has a limit on the number of operands in an expression, + // so we need multiple .info pseudo-ops. We choose a small number of words + // per pseudo-op to keep the assembly readable. + constexpr int WordsPerDirective = 5; + // Force emitting a new directive to keep the first directive purely about the + // name and size of the note. + int WordsBeforeNextDirective = 0; + auto PrintWord = [&](const uint8_t *WordPtr) { + if (WordsBeforeNextDirective-- == 0) { + EmitEOL(); + OS << InfoDirective; + WordsBeforeNextDirective = WordsPerDirective; + } + OS << Separator; + uint32_t Word = llvm::support::endian::read32be(WordPtr); + OS << format_hex(Word, 10); + }; + + size_t Index = 0; + for (; Index + WordSize <= MetadataSize; Index += WordSize) + PrintWord(reinterpret_cast<const uint8_t *>(Metadata.data()) + Index); + + // If there is padding, then we have at least one byte of payload left + // to emit. + if (PaddingSize) { + assert(PaddedSize - Index == WordSize); + std::array<uint8_t, WordSize> LastWord = {0}; + ::memcpy(LastWord.data(), Metadata.data() + Index, MetadataSize - Index); + PrintWord(LastWord.data()); + } + EmitEOL(); +} + +void MCGNUAsmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) { + assert(MAI->hasDotTypeDotSizeDirective()); + OS << "\t.size\t"; + Symbol->print(OS, MAI); + OS << ", "; + MAI->printExpr(OS, *Value); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlignment) { + OS << "\t.comm\t"; + Symbol->print(OS, MAI); + OS << ',' << Size; + + if (MAI->getCOMMDirectiveAlignmentIsInBytes()) + OS << ',' << ByteAlignment.value(); + else + OS << ',' << Log2(ByteAlignment); + EmitEOL(); + + // Print symbol's rename (original name contains invalid character(s)) if + // there is one. + if (getContext().isXCOFF()) { + auto *XSym = static_cast<MCSymbolXCOFF *>(Symbol); + if (XSym && XSym->hasRename()) + emitXCOFFRenameDirective(XSym, XSym->getSymbolTableName()); + } +} + +void MCGNUAsmStreamer::emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, + Align ByteAlign) { + OS << "\t.lcomm\t"; + Symbol->print(OS, MAI); + OS << ',' << Size; + + if (ByteAlign > 1) { + switch (MAI->getLCOMMDirectiveAlignmentType()) { + case LCOMM::NoAlignment: + llvm_unreachable("alignment not supported on .lcomm!"); + case LCOMM::ByteAlignment: + OS << ',' << ByteAlign.value(); + break; + case LCOMM::Log2Alignment: + OS << ',' << Log2(ByteAlign); + break; + } + } + EmitEOL(); +} + +void MCGNUAsmStreamer::emitZerofill(MCSection *Section, MCSymbol *Symbol, + uint64_t Size, Align ByteAlignment, + SMLoc Loc) { + if (Symbol) + Symbol->setFragment(&Section->getDummyFragment()); + + // Note: a .zerofill directive does not switch sections. + OS << ".zerofill "; + + assert(getContext().getObjectFileType() == MCContext::IsMachO && + ".zerofill is a Mach-O specific directive"); + // This is a mach-o specific directive. + + const MCSectionMachO *MOSection = ((const MCSectionMachO*)Section); + OS << MOSection->getSegmentName() << "," << MOSection->getName(); + + if (Symbol) { + OS << ','; + Symbol->print(OS, MAI); + OS << ',' << Size; + OS << ',' << Log2(ByteAlignment); + } + EmitEOL(); +} + +// .tbss sym, size, align +// This depends that the symbol has already been mangled from the original, +// e.g. _a. +void MCGNUAsmStreamer::emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, + uint64_t Size, Align ByteAlignment) { + Symbol->setFragment(&Section->getDummyFragment()); + + // Instead of using the Section we'll just use the shortcut. + + assert(getContext().getObjectFileType() == MCContext::IsMachO && + ".zerofill is a Mach-O specific directive"); + // This is a mach-o specific directive and section. + + OS << ".tbss "; + Symbol->print(OS, MAI); + OS << ", " << Size; + + // Output align if we have it. We default to 1 so don't bother printing + // that. + if (ByteAlignment > 1) + OS << ", " << Log2(ByteAlignment); + + EmitEOL(); +} + +static inline bool isPrintableString(StringRef Data) { + const auto BeginPtr = Data.begin(), EndPtr = Data.end(); + for (const unsigned char C : make_range(BeginPtr, EndPtr - 1)) { + if (!isPrint(C)) + return false; + } + return isPrint(Data.back()) || Data.back() == 0; +} + +static inline char toOctal(int X) { return (X&7)+'0'; } + +static void PrintByteList(StringRef Data, raw_ostream &OS, + MCAsmInfo::AsmCharLiteralSyntax ACLS) { + assert(!Data.empty() && "Cannot generate an empty list."); + const auto printCharacterInOctal = [&OS](unsigned char C) { + OS << '0'; + OS << toOctal(C >> 6); + OS << toOctal(C >> 3); + OS << toOctal(C >> 0); + }; + const auto printOneCharacterFor = [printCharacterInOctal]( + auto printOnePrintingCharacter) { + return [printCharacterInOctal, printOnePrintingCharacter](unsigned char C) { + if (isPrint(C)) { + printOnePrintingCharacter(static_cast<char>(C)); + return; + } + printCharacterInOctal(C); + }; + }; + const auto printCharacterList = [Data, &OS](const auto &printOneCharacter) { + const auto BeginPtr = Data.begin(), EndPtr = Data.end(); + for (const unsigned char C : make_range(BeginPtr, EndPtr - 1)) { + printOneCharacter(C); + OS << ','; + } + printOneCharacter(*(EndPtr - 1)); + }; + switch (ACLS) { + case MCAsmInfo::ACLS_Unknown: + printCharacterList(printCharacterInOctal); + return; + case MCAsmInfo::ACLS_SingleQuotePrefix: + printCharacterList(printOneCharacterFor([&OS](char C) { + const char AsmCharLitBuf[2] = {'\'', C}; + OS << StringRef(AsmCharLitBuf, sizeof(AsmCharLitBuf)); + })); + return; + } + llvm_unreachable("Invalid AsmCharLiteralSyntax value!"); +} + +void MCGNUAsmStreamer::PrintQuotedString(StringRef Data, + raw_ostream &OS) const { + OS << '"'; + + if (MAI->isAIX()) { + for (unsigned char C : Data) { + if (C == '"') + OS << "\"\""; + else + OS << (char)C; + } + } else { + for (unsigned char C : Data) { + if (C == '"' || C == '\\') { + OS << '\\' << (char)C; + continue; + } + + if (isPrint(C)) { + OS << (char)C; + continue; + } + + switch (C) { + case '\b': + OS << "\\b"; + break; + case '\f': + OS << "\\f"; + break; + case '\n': + OS << "\\n"; + break; + case '\r': + OS << "\\r"; + break; + case '\t': + OS << "\\t"; + break; + default: + OS << '\\'; + OS << toOctal(C >> 6); + OS << toOctal(C >> 3); + OS << toOctal(C >> 0); + break; + } + } + } + + OS << '"'; +} + +void MCGNUAsmStreamer::emitBytes(StringRef Data) { + assert(getCurrentSectionOnly() && + "Cannot emit contents before setting section!"); + if (Data.empty()) return; + + const auto emitAsString = [this](StringRef Data) { + if (MAI->isAIX()) { + if (isPrintableString(Data)) { + // For target with DoubleQuoteString constants, .string and .byte are + // used as replacement of .asciz and .ascii. + if (Data.back() == 0) { + OS << "\t.string\t"; + Data = Data.substr(0, Data.size() - 1); + } else { + OS << "\t.byte\t"; + } + PrintQuotedString(Data, OS); + } else { + OS << "\t.byte\t"; + PrintByteList(Data, OS, MAI->characterLiteralSyntax()); + } + EmitEOL(); + return true; + } + + // If the data ends with 0 and the target supports .asciz, use it, otherwise + // use .ascii or a byte-list directive + if (MAI->getAscizDirective() && Data.back() == 0) { + OS << MAI->getAscizDirective(); + Data = Data.substr(0, Data.size() - 1); + } else if (LLVM_LIKELY(MAI->getAsciiDirective())) { + OS << MAI->getAsciiDirective(); + } else { + return false; + } + + PrintQuotedString(Data, OS); + EmitEOL(); + return true; + }; + + if (Data.size() != 1 && emitAsString(Data)) + return; + + // Only single byte is provided or no ascii, asciz, or byte-list directives + // are applicable. Emit as vector of individual 8bits data elements. + if (MCTargetStreamer *TS = getTargetStreamer()) { + TS->emitRawBytes(Data); + return; + } + const char *Directive = MAI->getData8bitsDirective(); + for (const unsigned char C : Data.bytes()) { + OS << Directive << (unsigned)C; + EmitEOL(); + } +} + +void MCGNUAsmStreamer::emitBinaryData(StringRef Data) { + // This is binary data. Print it in a grid of hex bytes for readability. + const size_t Cols = 4; + for (size_t I = 0, EI = alignTo(Data.size(), Cols); I < EI; I += Cols) { + size_t J = I, EJ = std::min(I + Cols, Data.size()); + assert(EJ > 0); + OS << MAI->getData8bitsDirective(); + for (; J < EJ - 1; ++J) + OS << format("0x%02x", uint8_t(Data[J])) << ", "; + OS << format("0x%02x", uint8_t(Data[J])); + EmitEOL(); + } +} + +void MCGNUAsmStreamer::emitIntValue(uint64_t Value, unsigned Size) { + emitValue(MCConstantExpr::create(Value, getContext()), Size); +} + +void MCGNUAsmStreamer::emitIntValueInHex(uint64_t Value, unsigned Size) { + emitValue(MCConstantExpr::create(Value, getContext(), true), Size); +} + +void MCGNUAsmStreamer::emitIntValueInHexWithPadding(uint64_t Value, + unsigned Size) { + emitValue(MCConstantExpr::create(Value, getContext(), true, Size), Size); +} + +void MCGNUAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, + SMLoc Loc) { + assert(Size <= 8 && "Invalid size"); + assert(getCurrentSectionOnly() && + "Cannot emit contents before setting section!"); + const char *Directive = nullptr; + switch (Size) { + default: break; + case 1: Directive = MAI->getData8bitsDirective(); break; + case 2: Directive = MAI->getData16bitsDirective(); break; + case 4: Directive = MAI->getData32bitsDirective(); break; + case 8: Directive = MAI->getData64bitsDirective(); break; + } + + if (!Directive) { + int64_t IntValue; + if (!Value->evaluateAsAbsolute(IntValue)) + report_fatal_error("Don't know how to emit this value."); + + // We couldn't handle the requested integer size so we fallback by breaking + // the request down into several, smaller, integers. + // Since sizes greater or equal to "Size" are invalid, we use the greatest + // power of 2 that is less than "Size" as our largest piece of granularity. + bool IsLittleEndian = MAI->isLittleEndian(); + for (unsigned Emitted = 0; Emitted != Size;) { + unsigned Remaining = Size - Emitted; + // The size of our partial emission must be a power of two less than + // Size. + unsigned EmissionSize = llvm::bit_floor(std::min(Remaining, Size - 1)); + // Calculate the byte offset of our partial emission taking into account + // the endianness of the target. + unsigned ByteOffset = + IsLittleEndian ? Emitted : (Remaining - EmissionSize); + uint64_t ValueToEmit = IntValue >> (ByteOffset * 8); + // We truncate our partial emission to fit within the bounds of the + // emission domain. This produces nicer output and silences potential + // truncation warnings when round tripping through another assembler. + uint64_t Shift = 64 - EmissionSize * 8; + assert(Shift < static_cast<uint64_t>( + std::numeric_limits<unsigned long long>::digits) && + "undefined behavior"); + ValueToEmit &= ~0ULL >> Shift; + emitIntValue(ValueToEmit, EmissionSize); + Emitted += EmissionSize; + } + return; + } + + assert(Directive && "Invalid size for machine code value!"); + OS << Directive; + if (MCTargetStreamer *TS = getTargetStreamer()) { + TS->emitValue(Value); + } else { + MAI->printExpr(OS, *Value); + EmitEOL(); + } +} + +void MCGNUAsmStreamer::emitULEB128Value(const MCExpr *Value) { + int64_t IntValue; + if (Value->evaluateAsAbsolute(IntValue)) { + emitULEB128IntValue(IntValue); + return; + } + OS << "\t.uleb128 "; + MAI->printExpr(OS, *Value); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitSLEB128Value(const MCExpr *Value) { + int64_t IntValue; + if (Value->evaluateAsAbsolute(IntValue)) { + emitSLEB128IntValue(IntValue); + return; + } + OS << "\t.sleb128 "; + MAI->printExpr(OS, *Value); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, + SMLoc Loc) { + int64_t IntNumBytes; + const bool IsAbsolute = NumBytes.evaluateAsAbsolute(IntNumBytes); + if (IsAbsolute && IntNumBytes == 0) + return; + + if (const char *ZeroDirective = MAI->getZeroDirective()) { + if (!MAI->isAIX() || FillValue == 0) { + // FIXME: Emit location directives + OS << ZeroDirective; + MAI->printExpr(OS, NumBytes); + if (FillValue != 0) + OS << ',' << (int)FillValue; + EmitEOL(); + } else { + if (!IsAbsolute) + report_fatal_error( + "Cannot emit non-absolute expression lengths of fill."); + for (int i = 0; i < IntNumBytes; ++i) { + OS << MAI->getData8bitsDirective() << (int)FillValue; + EmitEOL(); + } + } + return; + } + + MCStreamer::emitFill(NumBytes, FillValue); +} + +void MCGNUAsmStreamer::emitFill(const MCExpr &NumValues, int64_t Size, + int64_t Expr, SMLoc Loc) { + // FIXME: Emit location directives + OS << "\t.fill\t"; + MAI->printExpr(OS, NumValues); + OS << ", " << Size << ", 0x"; + OS.write_hex(truncateToSize(Expr, 4)); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitAlignmentDirective(uint64_t ByteAlignment, + std::optional<int64_t> Value, + unsigned ValueSize, + unsigned MaxBytesToEmit) { + if (MAI->isAIX()) { + if (!isPowerOf2_64(ByteAlignment)) + report_fatal_error("Only power-of-two alignments are supported " + "with .align."); + OS << "\t.align\t"; + OS << Log2_64(ByteAlignment); + EmitEOL(); + return; + } + + // Some assemblers don't support non-power of two alignments, so we always + // emit alignments as a power of two if possible. + if (isPowerOf2_64(ByteAlignment)) { + switch (ValueSize) { + default: + llvm_unreachable("Invalid size for machine code value!"); + case 1: + OS << "\t.p2align\t"; + break; + case 2: + OS << ".p2alignw "; + break; + case 4: + OS << ".p2alignl "; + break; + case 8: + llvm_unreachable("Unsupported alignment size!"); + } + + OS << Log2_64(ByteAlignment); + + if (Value.has_value() || MaxBytesToEmit) { + if (Value.has_value()) { + OS << ", 0x"; + OS.write_hex(truncateToSize(*Value, ValueSize)); + } else { + OS << ", "; + } + + if (MaxBytesToEmit) + OS << ", " << MaxBytesToEmit; + } + EmitEOL(); + return; + } + + // Non-power of two alignment. This is not widely supported by assemblers. + // FIXME: Parameterize this based on MAI. + switch (ValueSize) { + default: llvm_unreachable("Invalid size for machine code value!"); + case 1: OS << ".balign"; break; + case 2: OS << ".balignw"; break; + case 4: OS << ".balignl"; break; + case 8: llvm_unreachable("Unsupported alignment size!"); + } + + OS << ' ' << ByteAlignment; + if (Value.has_value()) + OS << ", " << truncateToSize(*Value, ValueSize); + else if (MaxBytesToEmit) + OS << ", "; + if (MaxBytesToEmit) + OS << ", " << MaxBytesToEmit; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitValueToAlignment(Align Alignment, int64_t Fill, + uint8_t FillLen, + unsigned MaxBytesToEmit) { + emitAlignmentDirective(Alignment.value(), Fill, FillLen, MaxBytesToEmit); +} + +void MCGNUAsmStreamer::emitCodeAlignment(Align Alignment, + const MCSubtargetInfo *STI, + unsigned MaxBytesToEmit) { + // Emit with a text fill value. + if (MAI->getTextAlignFillValue()) + emitAlignmentDirective(Alignment.value(), MAI->getTextAlignFillValue(), 1, + MaxBytesToEmit); + else + emitAlignmentDirective(Alignment.value(), std::nullopt, 1, MaxBytesToEmit); +} + +void MCGNUAsmStreamer::emitPrefAlign(Align Alignment) { + OS << "\t.prefalign\t" << Alignment.value(); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitValueToOffset(const MCExpr *Offset, + unsigned char Value, SMLoc Loc) { + // FIXME: Verify that Offset is associated with the current section. + OS << ".org "; + MAI->printExpr(OS, *Offset); + OS << ", " << (unsigned)Value; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitFileDirective(StringRef Filename) { + assert(MAI->hasSingleParameterDotFile()); + OS << "\t.file\t"; + PrintQuotedString(Filename, OS); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitFileDirective(StringRef Filename, + StringRef CompilerVersion, + StringRef TimeStamp, + StringRef Description) { + assert(MAI->isAIX()); + OS << "\t.file\t"; + PrintQuotedString(Filename, OS); + bool useTimeStamp = !TimeStamp.empty(); + bool useCompilerVersion = !CompilerVersion.empty(); + bool useDescription = !Description.empty(); + if (useTimeStamp || useCompilerVersion || useDescription) { + OS << ","; + if (useTimeStamp) + PrintQuotedString(TimeStamp, OS); + if (useCompilerVersion || useDescription) { + OS << ","; + if (useCompilerVersion) + PrintQuotedString(CompilerVersion, OS); + if (useDescription) { + OS << ","; + PrintQuotedString(Description, OS); + } + } + } + EmitEOL(); +} + +void MCGNUAsmStreamer::printDwarfFileDirective( + unsigned FileNo, StringRef Directory, StringRef Filename, + std::optional<MD5::MD5Result> Checksum, std::optional<StringRef> Source, + bool UseDwarfDirectory, raw_svector_ostream &OS) const { + SmallString<128> FullPathName; + + if (!UseDwarfDirectory && !Directory.empty()) { + if (sys::path::is_absolute(Filename)) + Directory = ""; + else { + FullPathName = Directory; + sys::path::append(FullPathName, Filename); + Directory = ""; + Filename = FullPathName; + } + } + + OS << "\t.file\t" << FileNo << ' '; + if (!Directory.empty()) { + PrintQuotedString(Directory, OS); + OS << ' '; + } + PrintQuotedString(Filename, OS); + if (Checksum) + OS << " md5 0x" << Checksum->digest(); + if (Source) { + OS << " source "; + PrintQuotedString(*Source, OS); + } +} + +Expected<unsigned> MCGNUAsmStreamer::tryEmitDwarfFileDirective( + unsigned FileNo, StringRef Directory, StringRef Filename, + std::optional<MD5::MD5Result> Checksum, std::optional<StringRef> Source, + unsigned CUID) { + assert(CUID == 0 && "multiple CUs not supported by MCGNUAsmStreamer"); + + MCDwarfLineTable &Table = getContext().getMCDwarfLineTable(CUID); + unsigned NumFiles = Table.getMCDwarfFiles().size(); + Expected<unsigned> FileNoOrErr = + Table.tryGetFile(Directory, Filename, Checksum, Source, + getContext().getDwarfVersion(), FileNo); + if (!FileNoOrErr) + return FileNoOrErr.takeError(); + FileNo = FileNoOrErr.get(); + + // Return early if this file is already emitted before or if target doesn't + // support .file directive. + if (NumFiles == Table.getMCDwarfFiles().size() || MAI->isAIX()) + return FileNo; + + SmallString<128> Str; + raw_svector_ostream OS1(Str); + printDwarfFileDirective(FileNo, Directory, Filename, Checksum, Source, + UseDwarfDirectory, OS1); + + if (MCTargetStreamer *TS = getTargetStreamer()) + TS->emitDwarfFileDirective(OS1.str()); + else + emitRawText(OS1.str()); + + return FileNo; +} + +void MCGNUAsmStreamer::emitDwarfFile0Directive( + StringRef Directory, StringRef Filename, + std::optional<MD5::MD5Result> Checksum, std::optional<StringRef> Source, + unsigned CUID) { + assert(CUID == 0); + // .file 0 is new for DWARF v5. + if (getContext().getDwarfVersion() < 5) + return; + // Inform MCDwarf about the root file. + getContext().setMCLineTableRootFile(CUID, Directory, Filename, Checksum, + Source); + + // Target doesn't support .loc/.file directives, return early. + if (MAI->isAIX()) + return; + + SmallString<128> Str; + raw_svector_ostream OS1(Str); + printDwarfFileDirective(0, Directory, Filename, Checksum, Source, + UseDwarfDirectory, OS1); + + if (MCTargetStreamer *TS = getTargetStreamer()) + TS->emitDwarfFileDirective(OS1.str()); + else + emitRawText(OS1.str()); +} + +/// Helper to emit common .loc directive flags, isa, and discriminator. +void MCGNUAsmStreamer::emitDwarfLocDirectiveFlags(unsigned Flags, unsigned Isa, + unsigned Discriminator) { + if (!MAI->supportsExtendedDwarfLocDirective()) + return; + + if (Flags & DWARF2_FLAG_BASIC_BLOCK) + OS << " basic_block"; + if (Flags & DWARF2_FLAG_PROLOGUE_END) + OS << " prologue_end"; + if (Flags & DWARF2_FLAG_EPILOGUE_BEGIN) + OS << " epilogue_begin"; + + const unsigned OldFlags = getContext().getCurrentDwarfLoc().getFlags(); + if ((Flags & DWARF2_FLAG_IS_STMT) != (OldFlags & DWARF2_FLAG_IS_STMT)) { + OS << " is_stmt "; + OS << ((Flags & DWARF2_FLAG_IS_STMT) ? "1" : "0"); + } + + if (Isa) + OS << " isa " << Isa; + if (Discriminator) + OS << " discriminator " << Discriminator; +} + +/// Helper to emit the common suffix of .loc directives. +void MCGNUAsmStreamer::emitDwarfLocDirectiveSuffix( + unsigned FileNo, unsigned Line, unsigned Column, unsigned Flags, + unsigned Isa, unsigned Discriminator, StringRef FileName, + StringRef Comment) { + // Emit flags, isa, and discriminator. + emitDwarfLocDirectiveFlags(Flags, Isa, Discriminator); + + // Emit verbose comment if enabled. + if (IsVerboseAsm) { + OS.PadToColumn(MAI->getCommentColumn()); + OS << MAI->getCommentString() << ' '; + if (Comment.empty()) + OS << FileName << ':' << Line << ':' << Column; + else + OS << Comment; + } + + // Emit end of line and update the baseclass state. + EmitEOL(); + MCStreamer::emitDwarfLocDirective(FileNo, Line, Column, Flags, Isa, + Discriminator, FileName, Comment); +} + +void MCGNUAsmStreamer::emitDwarfLocDirective(unsigned FileNo, unsigned Line, + unsigned Column, unsigned Flags, + unsigned Isa, + unsigned Discriminator, + StringRef FileName, + StringRef Comment) { + // If target doesn't support .loc/.file directive, we need to record the lines + // same way like we do in object mode. + if (MAI->isAIX()) { + // In case we see two .loc directives in a row, make sure the + // first one gets a line entry. + MCDwarfLineEntry::make(this, getCurrentSectionOnly()); + this->MCStreamer::emitDwarfLocDirective(FileNo, Line, Column, Flags, Isa, + Discriminator, FileName, Comment); + return; + } + + // Emit the basic .loc directive. + OS << "\t.loc\t" << FileNo << " " << Line << " " << Column; + + // Emit common suffix (flags, comment, EOL, parent call). + emitDwarfLocDirectiveSuffix(FileNo, Line, Column, Flags, Isa, Discriminator, + FileName, Comment); +} + +/// This is same as emitDwarfLocDirective, except also emits inlined function +/// and inlined callsite information. +void MCGNUAsmStreamer::emitDwarfLocDirectiveWithInlinedAt( + unsigned FileNo, unsigned Line, unsigned Column, unsigned FileIA, + unsigned LineIA, unsigned ColIA, const MCSymbol *Sym, unsigned Flags, + unsigned Isa, unsigned Discriminator, StringRef FileName, + StringRef Comment) { + // Emit the basic .loc directive with NVPTX-specific extensions. + OS << "\t.loc\t" << FileNo << " " << Line << " " << Column; + OS << ", function_name " << *Sym; + OS << ", inlined_at " << FileIA << " " << LineIA << " " << ColIA; + + // Emit common suffix (flags, comment, EOL, parent call). + emitDwarfLocDirectiveSuffix(FileNo, Line, Column, Flags, Isa, Discriminator, + FileName, Comment); +} + +void MCGNUAsmStreamer::emitDwarfLocLabelDirective(SMLoc Loc, StringRef Name) { + MCStreamer::emitDwarfLocLabelDirective(Loc, Name); + OS << ".loc_label\t" << Name; + EmitEOL(); +} + +MCSymbol *MCGNUAsmStreamer::getDwarfLineTableSymbol(unsigned CUID) { + // Always use the zeroth line table, since asm syntax only supports one line + // table for now. + return MCStreamer::getDwarfLineTableSymbol(0); +} + +bool MCGNUAsmStreamer::emitCVFileDirective(unsigned FileNo, StringRef Filename, + ArrayRef<uint8_t> Checksum, + unsigned ChecksumKind) { + if (!getContext().getCVContext().addFile(*this, FileNo, Filename, Checksum, + ChecksumKind)) + return false; + + OS << "\t.cv_file\t" << FileNo << ' '; + PrintQuotedString(Filename, OS); + + if (!ChecksumKind) { + EmitEOL(); + return true; + } + + OS << ' '; + PrintQuotedString(toHex(Checksum), OS); + OS << ' ' << ChecksumKind; + + EmitEOL(); + return true; +} + +bool MCGNUAsmStreamer::emitCVFuncIdDirective(unsigned FuncId) { + OS << "\t.cv_func_id " << FuncId << '\n'; + return MCStreamer::emitCVFuncIdDirective(FuncId); +} + +bool MCGNUAsmStreamer::emitCVInlineSiteIdDirective(unsigned FunctionId, + unsigned IAFunc, + unsigned IAFile, + unsigned IALine, + unsigned IACol, SMLoc Loc) { + OS << "\t.cv_inline_site_id " << FunctionId << " within " << IAFunc + << " inlined_at " << IAFile << ' ' << IALine << ' ' << IACol << '\n'; + return MCStreamer::emitCVInlineSiteIdDirective(FunctionId, IAFunc, IAFile, + IALine, IACol, Loc); +} + +void MCGNUAsmStreamer::emitCVLocDirective(unsigned FunctionId, unsigned FileNo, + unsigned Line, unsigned Column, + bool PrologueEnd, bool IsStmt, + StringRef FileName, SMLoc Loc) { + // Validate the directive. + if (!checkCVLocSection(FunctionId, FileNo, Loc)) + return; + + OS << "\t.cv_loc\t" << FunctionId << " " << FileNo << " " << Line << " " + << Column; + if (PrologueEnd) + OS << " prologue_end"; + + if (IsStmt) + OS << " is_stmt 1"; + + if (IsVerboseAsm) { + OS.PadToColumn(MAI->getCommentColumn()); + OS << MAI->getCommentString() << ' ' << FileName << ':' << Line << ':' + << Column; + } + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVLinetableDirective(unsigned FunctionId, + const MCSymbol *FnStart, + const MCSymbol *FnEnd) { + OS << "\t.cv_linetable\t" << FunctionId << ", "; + FnStart->print(OS, MAI); + OS << ", "; + FnEnd->print(OS, MAI); + EmitEOL(); + this->MCStreamer::emitCVLinetableDirective(FunctionId, FnStart, FnEnd); +} + +void MCGNUAsmStreamer::emitCVInlineLinetableDirective( + unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, + const MCSymbol *FnStartSym, const MCSymbol *FnEndSym) { + OS << "\t.cv_inline_linetable\t" << PrimaryFunctionId << ' ' << SourceFileId + << ' ' << SourceLineNum << ' '; + FnStartSym->print(OS, MAI); + OS << ' '; + FnEndSym->print(OS, MAI); + EmitEOL(); + this->MCStreamer::emitCVInlineLinetableDirective( + PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); +} + +void MCGNUAsmStreamer::PrintCVDefRangePrefix( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges) { + OS << "\t.cv_def_range\t"; + for (std::pair<const MCSymbol *, const MCSymbol *> Range : Ranges) { + OS << ' '; + Range.first->print(OS, MAI); + OS << ' '; + Range.second->print(OS, MAI); + } +} + +void MCGNUAsmStreamer::emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeRegisterRelHeader DRHdr) { + PrintCVDefRangePrefix(Ranges); + OS << ", reg_rel, "; + OS << DRHdr.Register << ", " << DRHdr.Flags << ", " + << DRHdr.BasePointerOffset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeSubfieldRegisterHeader DRHdr) { + PrintCVDefRangePrefix(Ranges); + OS << ", subfield_reg, "; + OS << DRHdr.Register << ", " << DRHdr.OffsetInParent; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeRegisterHeader DRHdr) { + PrintCVDefRangePrefix(Ranges); + OS << ", reg, "; + OS << DRHdr.Register; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVDefRangeDirective( + ArrayRef<std::pair<const MCSymbol *, const MCSymbol *>> Ranges, + codeview::DefRangeFramePointerRelHeader DRHdr) { + PrintCVDefRangePrefix(Ranges); + OS << ", frame_ptr_rel, "; + OS << DRHdr.Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVStringTableDirective() { + OS << "\t.cv_stringtable"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVFileChecksumsDirective() { + OS << "\t.cv_filechecksums"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVFileChecksumOffsetDirective(unsigned FileNo) { + OS << "\t.cv_filechecksumoffset\t" << FileNo; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCVFPOData(const MCSymbol *ProcSym, SMLoc L) { + OS << "\t.cv_fpo_data\t"; + ProcSym->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitIdent(StringRef IdentString) { + assert(MAI->hasIdentDirective() && ".ident directive not supported"); + OS << "\t.ident\t"; + PrintQuotedString(IdentString, OS); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFISections(bool EH, bool Debug, bool SFrame) { + MCStreamer::emitCFISections(EH, Debug, SFrame); + OS << "\t.cfi_sections "; + bool C = false; + if (EH) { + OS << ".eh_frame"; + C = true; + } + if (Debug) { + if (C) + OS << ", "; + OS << ".debug_frame"; + C = true; + } + if (SFrame) { + if (C) + OS << ", "; + OS << ".sframe"; + } + + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) { + OS << "\t.cfi_startproc"; + if (Frame.IsSimple) + OS << " simple"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIEndProcImpl(MCDwarfFrameInfo &Frame) { + MCStreamer::emitCFIEndProcImpl(Frame); + OS << "\t.cfi_endproc"; + EmitEOL(); +} + +void MCGNUAsmStreamer::EmitRegisterName(int64_t Register) { + if (!MAI->useDwarfRegNumForCFI()) { + // User .cfi_* directives can use arbitrary DWARF register numbers, not + // just ones that map to LLVM register numbers and have known names. + // Fall back to using the original number directly if no name is known. + const MCRegisterInfo *MRI = getContext().getRegisterInfo(); + if (std::optional<MCRegister> LLVMRegister = + MRI->getLLVMRegNum(Register, true)) { + InstPrinter->printRegName(OS, *LLVMRegister); + return; + } + } + OS << Register; +} + +void MCGNUAsmStreamer::emitCFIDefCfa(int64_t Register, int64_t Offset, + SMLoc Loc) { + MCStreamer::emitCFIDefCfa(Register, Offset, Loc); + OS << "\t.cfi_def_cfa "; + EmitRegisterName(Register); + OS << ", " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIDefCfaOffset(int64_t Offset, SMLoc Loc) { + MCStreamer::emitCFIDefCfaOffset(Offset, Loc); + OS << "\t.cfi_def_cfa_offset " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFILLVMDefAspaceCfa(int64_t Register, int64_t Offset, + int64_t AddressSpace, + SMLoc Loc) { + MCStreamer::emitCFILLVMDefAspaceCfa(Register, Offset, AddressSpace, Loc); + OS << "\t.cfi_llvm_def_aspace_cfa "; + EmitRegisterName(Register); + OS << ", " << Offset; + OS << ", " << AddressSpace; + EmitEOL(); +} + +static void PrintCFIEscape(llvm::formatted_raw_ostream &OS, StringRef Values) { + OS << "\t.cfi_escape "; + if (!Values.empty()) { + size_t e = Values.size() - 1; + for (size_t i = 0; i < e; ++i) + OS << format("0x%02x", uint8_t(Values[i])) << ", "; + OS << format("0x%02x", uint8_t(Values[e])); + } +} + +void MCGNUAsmStreamer::emitCFIEscape(StringRef Values, SMLoc Loc) { + MCStreamer::emitCFIEscape(Values, Loc); + PrintCFIEscape(OS, Values); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIGnuArgsSize(int64_t Size, SMLoc Loc) { + MCStreamer::emitCFIGnuArgsSize(Size, Loc); + + uint8_t Buffer[16] = { dwarf::DW_CFA_GNU_args_size }; + unsigned Len = encodeULEB128(Size, Buffer + 1) + 1; + + PrintCFIEscape(OS, StringRef((const char *)&Buffer[0], Len)); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIDefCfaRegister(int64_t Register, SMLoc Loc) { + MCStreamer::emitCFIDefCfaRegister(Register, Loc); + OS << "\t.cfi_def_cfa_register "; + EmitRegisterName(Register); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIOffset(int64_t Register, int64_t Offset, + SMLoc Loc) { + MCStreamer::emitCFIOffset(Register, Offset, Loc); + OS << "\t.cfi_offset "; + EmitRegisterName(Register); + OS << ", " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIPersonality(const MCSymbol *Sym, + unsigned Encoding) { + MCStreamer::emitCFIPersonality(Sym, Encoding); + OS << "\t.cfi_personality " << Encoding << ", "; + Sym->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFILsda(const MCSymbol *Sym, unsigned Encoding) { + MCStreamer::emitCFILsda(Sym, Encoding); + OS << "\t.cfi_lsda " << Encoding << ", "; + Sym->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIRememberState(SMLoc Loc) { + MCStreamer::emitCFIRememberState(Loc); + OS << "\t.cfi_remember_state"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIRestoreState(SMLoc Loc) { + MCStreamer::emitCFIRestoreState(Loc); + OS << "\t.cfi_restore_state"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIRestore(int64_t Register, SMLoc Loc) { + MCStreamer::emitCFIRestore(Register, Loc); + OS << "\t.cfi_restore "; + EmitRegisterName(Register); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFISameValue(int64_t Register, SMLoc Loc) { + MCStreamer::emitCFISameValue(Register, Loc); + OS << "\t.cfi_same_value "; + EmitRegisterName(Register); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIRelOffset(int64_t Register, int64_t Offset, + SMLoc Loc) { + MCStreamer::emitCFIRelOffset(Register, Offset, Loc); + OS << "\t.cfi_rel_offset "; + EmitRegisterName(Register); + OS << ", " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIAdjustCfaOffset(int64_t Adjustment, SMLoc Loc) { + MCStreamer::emitCFIAdjustCfaOffset(Adjustment, Loc); + OS << "\t.cfi_adjust_cfa_offset " << Adjustment; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFISignalFrame() { + MCStreamer::emitCFISignalFrame(); + OS << "\t.cfi_signal_frame"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIUndefined(int64_t Register, SMLoc Loc) { + MCStreamer::emitCFIUndefined(Register, Loc); + OS << "\t.cfi_undefined "; + EmitRegisterName(Register); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIRegister(int64_t Register1, int64_t Register2, + SMLoc Loc) { + MCStreamer::emitCFIRegister(Register1, Register2, Loc); + OS << "\t.cfi_register "; + EmitRegisterName(Register1); + OS << ", "; + EmitRegisterName(Register2); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIWindowSave(SMLoc Loc) { + MCStreamer::emitCFIWindowSave(Loc); + OS << "\t.cfi_window_save"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFINegateRAState(SMLoc Loc) { + MCStreamer::emitCFINegateRAState(Loc); + OS << "\t.cfi_negate_ra_state"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFINegateRAStateWithPC(SMLoc Loc) { + MCStreamer::emitCFINegateRAStateWithPC(Loc); + OS << "\t.cfi_negate_ra_state_with_pc"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIReturnColumn(int64_t Register) { + MCStreamer::emitCFIReturnColumn(Register); + OS << "\t.cfi_return_column "; + EmitRegisterName(Register); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFILabelDirective(SMLoc Loc, StringRef Name) { + MCStreamer::emitCFILabelDirective(Loc, Name); + OS << "\t.cfi_label " << Name; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIBKeyFrame() { + MCStreamer::emitCFIBKeyFrame(); + OS << "\t.cfi_b_key_frame"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIMTETaggedFrame() { + MCStreamer::emitCFIMTETaggedFrame(); + OS << "\t.cfi_mte_tagged_frame"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCFIValOffset(int64_t Register, int64_t Offset, + SMLoc Loc) { + MCStreamer::emitCFIValOffset(Register, Offset, Loc); + OS << "\t.cfi_val_offset "; + EmitRegisterName(Register); + OS << ", " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) { + MCStreamer::emitWinCFIStartProc(Symbol, Loc); + + OS << ".seh_proc "; + Symbol->print(OS, MAI); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIEndProc(SMLoc Loc) { + MCStreamer::emitWinCFIEndProc(Loc); + + OS << "\t.seh_endproc"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIFuncletOrFuncEnd(SMLoc Loc) { + MCStreamer::emitWinCFIFuncletOrFuncEnd(Loc); + + OS << "\t.seh_endfunclet"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFISplitChained(SMLoc Loc) { + MCStreamer::emitWinCFISplitChained(Loc); + + OS << "\t.seh_splitchained"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinEHHandler(const MCSymbol *Sym, bool Unwind, + bool Except, SMLoc Loc) { + MCStreamer::emitWinEHHandler(Sym, Unwind, Except, Loc); + + OS << "\t.seh_handler "; + Sym->print(OS, MAI); + char Marker = '@'; + const Triple &T = getContext().getTargetTriple(); + if (T.getArch() == Triple::arm || T.getArch() == Triple::thumb) + Marker = '%'; + if (Unwind) + OS << ", " << Marker << "unwind"; + if (Except) + OS << ", " << Marker << "except"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinEHHandlerData(SMLoc Loc) { + MCStreamer::emitWinEHHandlerData(Loc); + + // Switch sections. Don't call switchSection directly, because that will + // cause the section switch to be visible in the emitted assembly. + // We only do this so the section switch that terminates the handler + // data block is visible. + WinEH::FrameInfo *CurFrame = getCurrentWinFrameInfo(); + + // Do nothing if no frame is open. MCStreamer should've already reported an + // error. + if (!CurFrame) + return; + + MCSection *TextSec = &CurFrame->Function->getSection(); + MCSection *XData = getAssociatedXDataSection(TextSec); + switchSectionNoPrint(XData); + + OS << "\t.seh_handlerdata"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIPushReg(MCRegister Register, SMLoc Loc) { + MCStreamer::emitWinCFIPushReg(Register, Loc); + + OS << "\t.seh_pushreg "; + InstPrinter->printRegName(OS, Register); + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFISetFrame(MCRegister Register, unsigned Offset, + SMLoc Loc) { + MCStreamer::emitWinCFISetFrame(Register, Offset, Loc); + + OS << "\t.seh_setframe "; + InstPrinter->printRegName(OS, Register); + OS << ", " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIAllocStack(unsigned Size, SMLoc Loc) { + MCStreamer::emitWinCFIAllocStack(Size, Loc); + + OS << "\t.seh_stackalloc " << Size; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFISaveReg(MCRegister Register, unsigned Offset, + SMLoc Loc) { + MCStreamer::emitWinCFISaveReg(Register, Offset, Loc); + + OS << "\t.seh_savereg "; + InstPrinter->printRegName(OS, Register); + OS << ", " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFISaveXMM(MCRegister Register, unsigned Offset, + SMLoc Loc) { + MCStreamer::emitWinCFISaveXMM(Register, Offset, Loc); + + OS << "\t.seh_savexmm "; + InstPrinter->printRegName(OS, Register); + OS << ", " << Offset; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIPushFrame(bool Code, SMLoc Loc) { + MCStreamer::emitWinCFIPushFrame(Code, Loc); + + OS << "\t.seh_pushframe"; + if (Code) + OS << " @code"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIEndProlog(SMLoc Loc) { + MCStreamer::emitWinCFIEndProlog(Loc); + + OS << "\t.seh_endprologue"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIBeginEpilogue(SMLoc Loc) { + MCStreamer::emitWinCFIBeginEpilogue(Loc); + + OS << "\t.seh_startepilogue"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIEndEpilogue(SMLoc Loc) { + MCStreamer::emitWinCFIEndEpilogue(Loc); + + OS << "\t.seh_endepilogue"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIUnwindV2Start(SMLoc Loc) { + MCStreamer::emitWinCFIUnwindV2Start(Loc); + + OS << "\t.seh_unwindv2start"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitWinCFIUnwindVersion(uint8_t Version, SMLoc Loc) { + MCStreamer::emitWinCFIUnwindVersion(Version, Loc); + + OS << "\t.seh_unwindversion " << (unsigned)Version; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, + const MCSymbolRefExpr *To, + uint64_t Count) { + OS << "\t.cg_profile "; + From->getSymbol().print(OS, MAI); + OS << ", "; + To->getSymbol().print(OS, MAI); + OS << ", " << Count; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI) { + if (LFIRewriter && LFIRewriter->rewriteInst(Inst, *this, STI)) + return; + + if (CurFrag) { + MCSection *Sec = getCurrentSectionOnly(); + Sec->setHasInstructions(true); + } + + if (MAI->isAIX() && CurFrag) + // Now that a machine instruction has been assembled into this section, make + // a line entry for any .loc directive that has been seen. + MCDwarfLineEntry::make(this, getCurrentSectionOnly()); + + // Show the encoding in a comment if we have a code emitter. + addEncodingComment(Inst, STI); + + // Show the MCInst if enabled. + if (ShowInst) { + Inst.dump_pretty(getCommentOS(), InstPrinter.get(), "\n ", &getContext()); + getCommentOS() << "\n"; + } + + if(getTargetStreamer()) + getTargetStreamer()->prettyPrintAsm(*InstPrinter, 0, Inst, STI, OS); + else + InstPrinter->printInst(&Inst, 0, "", STI, OS); + + StringRef Comments = CommentToEmit; + if (Comments.size() && Comments.back() != '\n') + getCommentOS() << "\n"; + + EmitEOL(); +} + +void MCGNUAsmStreamer::emitPseudoProbe( + uint64_t Guid, uint64_t Index, uint64_t Type, uint64_t Attr, + uint64_t Discriminator, const MCPseudoProbeInlineStack &InlineStack, + MCSymbol *FnSym) { + OS << "\t.pseudoprobe\t" << Guid << " " << Index << " " << Type << " " << Attr; + if (Discriminator) + OS << " " << Discriminator; + // Emit inline stack like + // @ GUIDmain:3 @ GUIDCaller:1 @ GUIDDirectCaller:11 + for (const auto &Site : InlineStack) + OS << " @ " << std::get<0>(Site) << ":" << std::get<1>(Site); + + OS << " "; + FnSym->print(OS, MAI); + + EmitEOL(); +} + +void MCGNUAsmStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, + const MCExpr *Expr, SMLoc) { + OS << "\t.reloc "; + MAI->printExpr(OS, Offset); + OS << ", " << Name; + if (Expr) { + OS << ", "; + MAI->printExpr(OS, *Expr); + } + EmitEOL(); +} + +void MCGNUAsmStreamer::emitAddrsig() { + OS << "\t.addrsig"; + EmitEOL(); +} + +void MCGNUAsmStreamer::emitAddrsigSym(const MCSymbol *Sym) { + OS << "\t.addrsig_sym "; + Sym->print(OS, MAI); + EmitEOL(); +} + +/// EmitRawText - If this file is backed by an assembly streamer, this dumps +/// the specified string in the output .s file. This capability is +/// indicated by the hasRawTextSupport() predicate. +void MCGNUAsmStreamer::emitRawTextImpl(StringRef String) { + String.consume_back("\n"); + OS << String; + EmitEOL(); +} + +void MCGNUAsmStreamer::finishImpl() { + // If we are generating dwarf for assembly source files dump out the sections. + if (getContext().getGenDwarfForAssembly()) + MCGenDwarfInfo::Emit(this); + + // Now it is time to emit debug line sections if target doesn't support .loc + // and .line directives. + if (MAI->isAIX()) { + MCDwarfLineTable::emit(this, getAssembler().getDWARFLinetableParams()); + return; + } + + // Emit the label for the line table, if requested - since the rest of the + // line table will be defined by .loc/.file directives, and not emitted + // directly, the label is the only work required here. + const auto &Tables = getContext().getMCDwarfLineTables(); + if (!Tables.empty()) { + assert(Tables.size() == 1 && "asm output only supports one line table"); + if (auto *Label = Tables.begin()->second.getLabel()) { + switchSection(getContext().getObjectFileInfo()->getDwarfLineSection(), 0); + emitLabel(Label); + } + } +} + +void MCGNUAsmStreamer::emitDwarfUnitLength(uint64_t Length, + const Twine &Comment) { + // If the assembler on some target fills in the DWARF unit length, we + // don't want to emit the length in the compiler. For example, the AIX + // assembler requires the assembly file with the unit length omitted from + // the debug section headers. In such cases, any label we placed occurs + // after the implied length field. We need to adjust the reference here + // to account for the offset introduced by the inserted length field. + if (MAI->isAIX()) + return; + MCStreamer::emitDwarfUnitLength(Length, Comment); +} + +MCSymbol *MCGNUAsmStreamer::emitDwarfUnitLength(const Twine &Prefix, + const Twine &Comment) { + // If the assembler on some target fills in the DWARF unit length, we + // don't want to emit the length in the compiler. For example, the AIX + // assembler requires the assembly file with the unit length omitted from + // the debug section headers. In such cases, any label we placed occurs + // after the implied length field. We need to adjust the reference here + // to account for the offset introduced by the inserted length field. + if (MAI->isAIX()) + return getContext().createTempSymbol(Prefix + "_end"); + return MCStreamer::emitDwarfUnitLength(Prefix, Comment); +} + +void MCGNUAsmStreamer::emitDwarfLineStartLabel(MCSymbol *StartSym) { + // If the assembler on some target fills in the DWARF unit length, we + // don't want to emit the length in the compiler. For example, the AIX + // assembler requires the assembly file with the unit length omitted from + // the debug section headers. In such cases, any label we placed occurs + // after the implied length field. We need to adjust the reference here + // to account for the offset introduced by the inserted length field. + MCContext &Ctx = getContext(); + if (MAI->isAIX()) { + MCSymbol *DebugLineSymTmp = Ctx.createTempSymbol("debug_line_"); + // Emit the symbol which does not contain the unit length field. + emitLabel(DebugLineSymTmp); + + // Adjust the outer reference to account for the offset introduced by the + // inserted length field. + unsigned LengthFieldSize = + dwarf::getUnitLengthFieldByteSize(Ctx.getDwarfFormat()); + const MCExpr *EntrySize = MCConstantExpr::create(LengthFieldSize, Ctx); + const MCExpr *OuterSym = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(DebugLineSymTmp, Ctx), EntrySize, Ctx); + + emitAssignment(StartSym, OuterSym); + return; + } + MCStreamer::emitDwarfLineStartLabel(StartSym); +} + +void MCGNUAsmStreamer::emitDwarfLineEndEntry(MCSection *Section, + MCSymbol *LastLabel, + MCSymbol *EndLabel) { + // If the targets write the raw debug line data for assembly output (We can + // not switch to Section and add the end symbol there for assembly output) + // we currently use the .text end label as any section end. This will not + // impact the debugability as we will jump to the caller of the last function + // in the section before we come into the .text end address. + assert(MAI->isAIX() && + ".loc should not be generated together with raw data!"); + + MCContext &Ctx = getContext(); + + // FIXME: use section end symbol as end of the Section. We need to consider + // the explicit sections and -ffunction-sections when we try to generate or + // find section end symbol for the Section. + MCSection *TextSection = Ctx.getObjectFileInfo()->getTextSection(); + assert(TextSection->hasEnded() && ".text section is not end!"); + + if (!EndLabel) + EndLabel = TextSection->getEndSymbol(Ctx); + const MCAsmInfo *AsmInfo = Ctx.getAsmInfo(); + emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, EndLabel, + AsmInfo->getCodePointerSize()); +} + +// Generate DWARF line sections for assembly mode without .loc/.file +void MCGNUAsmStreamer::emitDwarfAdvanceLineAddr(int64_t LineDelta, + const MCSymbol *LastLabel, + const MCSymbol *Label, + unsigned PointerSize) { + assert(MAI->isAIX() && + ".loc/.file don't need raw data in debug line section!"); + + // Set to new address. + AddComment("Set address to " + Label->getName()); + emitIntValue(dwarf::DW_LNS_extended_op, 1); + emitULEB128IntValue(PointerSize + 1); + emitIntValue(dwarf::DW_LNE_set_address, 1); + emitSymbolValue(Label, PointerSize); + + if (!LastLabel) { + // Emit the sequence for the LineDelta (from 1) and a zero address delta. + AddComment("Start sequence"); + MCDwarfLineAddr::Emit(this, MCDwarfLineTableParams(), LineDelta, 0); + return; + } + + // INT64_MAX is a signal of the end of the section. Emit DW_LNE_end_sequence + // for the end of the section. + if (LineDelta == INT64_MAX) { + AddComment("End sequence"); + emitIntValue(dwarf::DW_LNS_extended_op, 1); + emitULEB128IntValue(1); + emitIntValue(dwarf::DW_LNE_end_sequence, 1); + return; + } + + // Advance line. + AddComment("Advance line " + Twine(LineDelta)); + emitIntValue(dwarf::DW_LNS_advance_line, 1); + emitSLEB128IntValue(LineDelta); + emitIntValue(dwarf::DW_LNS_copy, 1); +} + +MCStreamer *llvm::createAsmStreamer(MCContext &Context, + std::unique_ptr<formatted_raw_ostream> OS, + std::unique_ptr<MCInstPrinter> IP, + std::unique_ptr<MCCodeEmitter> CE, + std::unique_ptr<MCAsmBackend> MAB) { + return new MCGNUAsmStreamer(Context, std::move(OS), std::move(IP), std::move(CE), + std::move(MAB)); +} diff --git a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h index 42d49b7..56f922b 100644 --- a/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h +++ b/llvm/lib/Target/SystemZ/MCTargetDesc/SystemZHLASMAsmStreamer.h @@ -16,7 +16,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCAsmBackend.h" -#include "llvm/MC/MCAsmBaseStreamer.h" +#include "llvm/MC/MCAsmStreamer.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" @@ -31,7 +31,7 @@ namespace llvm { class MCSymbolGOFF; -class SystemZHLASMAsmStreamer final : public MCAsmBaseStreamer { +class SystemZHLASMAsmStreamer final : public MCAsmStreamer { constexpr static size_t InstLimit = 80; constexpr static size_t ContIndicatorColumn = 72; constexpr static size_t ContStartColumn = 15; @@ -50,7 +50,7 @@ public: std::unique_ptr<MCInstPrinter> Printer, std::unique_ptr<MCCodeEmitter> Emitter, std::unique_ptr<MCAsmBackend> AsmBackend) - : MCAsmBaseStreamer(Context, std::move(Emitter), std::move(AsmBackend)), + : MCAsmStreamer(Context, std::move(Emitter), std::move(AsmBackend)), FOSOwner(std::move(OS)), FOS(*FOSOwner), OS(Str), MAI(Context.getAsmInfo()), InstPrinter(std::move(Printer)) { assert(InstPrinter); |
