diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4805,7 +4805,8 @@ // Add the split debug info name to the command lines here so we // can propagate it to the backend. bool SplitDWARF = (DwarfFission != DwarfFissionKind::None) && - TC.getTriple().isOSBinFormatELF() && + (TC.getTriple().isOSBinFormatELF() || + TC.getTriple().isOSBinFormatWasm()) && (isa(JA) || isa(JA) || isa(JA)); if (SplitDWARF) { diff --git a/clang/test/Driver/split-debug.c b/clang/test/Driver/split-debug.c --- a/clang/test/Driver/split-debug.c +++ b/clang/test/Driver/split-debug.c @@ -10,6 +10,11 @@ // RUN: %clang -target x86_64-unknown-linux-gnu -gsplit-dwarf=split -c -### %s 2> %t // RUN: FileCheck -check-prefix=CHECK-ACTIONS < %t %s +// RUN: %clang -target wasm32-unknown-unknown -gsplit-dwarf -c -### %s 2> %t +// RUN: FileCheck -check-prefix=CHECK-ACTIONS < %t %s +// RUN: %clang -target wasm32-unknown-unknown -gsplit-dwarf=split -c -### %s 2> %t +// RUN: FileCheck -check-prefix=CHECK-ACTIONS < %t %s + // RUN: %clang -target x86_64-unknown-linux-gnu -gsplit-dwarf=single -c -### %s 2> %t // RUN: FileCheck -check-prefix=CHECK-ACTIONS-SINGLE-SPLIT < %t %s // diff --git a/llvm/include/llvm/MC/MCWasmObjectWriter.h b/llvm/include/llvm/MC/MCWasmObjectWriter.h --- a/llvm/include/llvm/MC/MCWasmObjectWriter.h +++ b/llvm/include/llvm/MC/MCWasmObjectWriter.h @@ -52,6 +52,10 @@ createWasmObjectWriter(std::unique_ptr MOTW, raw_pwrite_stream &OS); +std::unique_ptr +createWasmDwoObjectWriter(std::unique_ptr MOTW, + raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS); + } // namespace llvm #endif diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -422,7 +422,10 @@ // FIXME: duplicated from Target/WebAssembly/WebAssembly.h // don't want to depend on target specific headers in this code? const unsigned TI_GLOBAL_RELOC = 3; - if (FrameBase.Location.WasmLoc.Kind == TI_GLOBAL_RELOC) { + // FIXME: when writing dwo, we need to avoid relocations. Probably + // the "right" solution is to treat globals the way func and data symbols + // are (with entries in .debug_addr). + if (FrameBase.Location.WasmLoc.Kind == TI_GLOBAL_RELOC && !isDwoUnit()) { // These need to be relocatable. assert(FrameBase.Location.WasmLoc.Index == 0); // Only SP so far. auto SPSym = cast( diff --git a/llvm/lib/MC/MCAsmBackend.cpp b/llvm/lib/MC/MCAsmBackend.cpp --- a/llvm/lib/MC/MCAsmBackend.cpp +++ b/llvm/lib/MC/MCAsmBackend.cpp @@ -54,10 +54,17 @@ MCAsmBackend::createDwoObjectWriter(raw_pwrite_stream &OS, raw_pwrite_stream &DwoOS) const { auto TW = createObjectTargetWriter(); - if (TW->getFormat() != Triple::ELF) - report_fatal_error("dwo only supported with ELF"); - return createELFDwoObjectWriter(cast(std::move(TW)), - OS, DwoOS, Endian == support::little); + switch (TW->getFormat()) { + case Triple::ELF: + return createELFDwoObjectWriter( + cast(std::move(TW)), OS, DwoOS, + Endian == support::little); + case Triple::Wasm: + return createWasmDwoObjectWriter( + cast(std::move(TW)), OS, DwoOS); + default: + report_fatal_error("dwo only supported with ELF and Wasm"); + } } Optional MCAsmBackend::getFixupKind(StringRef Name) const { diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -796,6 +796,10 @@ DwarfFrameSection = Ctx->getWasmSection(".debug_frame", SectionKind::getMetadata()); DwarfPubNamesSection = Ctx->getWasmSection(".debug_pubnames", SectionKind::getMetadata()); DwarfPubTypesSection = Ctx->getWasmSection(".debug_pubtypes", SectionKind::getMetadata()); + DwarfGnuPubNamesSection = + Ctx->getWasmSection(".debug_gnu_pubnames", SectionKind::getMetadata()); + DwarfGnuPubTypesSection = + Ctx->getWasmSection(".debug_gnu_pubtypes", SectionKind::getMetadata()); DwarfDebugNamesSection = Ctx->getWasmSection(".debug_names", SectionKind::getMetadata()); @@ -808,6 +812,37 @@ DwarfLoclistsSection = Ctx->getWasmSection(".debug_loclists", SectionKind::getMetadata()); + // Fission Sections + DwarfInfoDWOSection = + Ctx->getWasmSection(".debug_info.dwo", SectionKind::getMetadata()); + DwarfTypesDWOSection = + Ctx->getWasmSection(".debug_types.dwo", SectionKind::getMetadata()); + DwarfAbbrevDWOSection = + Ctx->getWasmSection(".debug_abbrev.dwo", SectionKind::getMetadata()); + DwarfStrDWOSection = + Ctx->getWasmSection(".debug_str.dwo", SectionKind::getMetadata()); + DwarfLineDWOSection = + Ctx->getWasmSection(".debug_line.dwo", SectionKind::getMetadata()); + DwarfLocDWOSection = + Ctx->getWasmSection(".debug_loc.dwo", SectionKind::getMetadata()); + DwarfStrOffDWOSection = + Ctx->getWasmSection(".debug_str_offsets.dwo", SectionKind::getMetadata()); + DwarfRnglistsDWOSection = + Ctx->getWasmSection(".debug_rnglists.dwo", SectionKind::getMetadata()); + DwarfMacinfoDWOSection = + Ctx->getWasmSection(".debug_macinfo.dwo", SectionKind::getMetadata()); + DwarfMacroDWOSection = + Ctx->getWasmSection(".debug_macro.dwo", SectionKind::getMetadata()); + + DwarfLoclistsDWOSection = + Ctx->getWasmSection(".debug_loclists.dwo", SectionKind::getMetadata()); + + // DWP Sections + DwarfCUIndexSection = + Ctx->getWasmSection(".debug_cu_index", SectionKind::getMetadata(), 0); + DwarfTUIndexSection = + Ctx->getWasmSection(".debug_tu_index", SectionKind::getMetadata(), 0); + // Wasm use data section for LSDA. // TODO Consider putting each function's exception table in a separate // section, as in -function-sections, to facilitate lld's --gc-section. diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp --- a/llvm/lib/MC/WasmObjectWriter.cpp +++ b/llvm/lib/MC/WasmObjectWriter.cpp @@ -216,8 +216,12 @@ Stream.pwrite((char *)Buffer, sizeof(Buffer), Offset); } +bool isDwoSection(const MCSection &Sec) { + return Sec.getName().endswith(".dwo"); +} + class WasmObjectWriter : public MCObjectWriter { - support::endian::Writer W; + support::endian::Writer *W; /// The target specific Wasm writer instance. std::unique_ptr TargetObjectWriter; @@ -260,7 +264,16 @@ unsigned NumEventImports = 0; uint32_t SectionCount = 0; - // TargetObjectWriter wrappers. + enum class DwoMode { + AllSections, + NonDwoOnly, + DwoOnly, + }; + bool IsSplitDwarf = false; + raw_pwrite_stream *OS = nullptr; + raw_pwrite_stream *DwoOS = nullptr; + + // TargetObjectWriter wranppers. bool is64Bit() const { return TargetObjectWriter->is64Bit(); } bool isEmscripten() const { return TargetObjectWriter->isEmscripten(); } @@ -270,8 +283,13 @@ public: WasmObjectWriter(std::unique_ptr MOTW, - raw_pwrite_stream &OS) - : W(OS, support::little), TargetObjectWriter(std::move(MOTW)) {} + raw_pwrite_stream &OS_) + : TargetObjectWriter(std::move(MOTW)), OS(&OS_) {} + + WasmObjectWriter(std::unique_ptr MOTW, + raw_pwrite_stream &OS_, raw_pwrite_stream &DwoOS_) + : TargetObjectWriter(std::move(MOTW)), IsSplitDwarf(true), OS(&OS_), + DwoOS(&DwoOS_) {} private: void reset() override { @@ -303,27 +321,31 @@ void executePostLayoutBinding(MCAssembler &Asm, const MCAsmLayout &Layout) override; - + void prepareImports(SmallVectorImpl &Imports, + MCAssembler &Asm, const MCAsmLayout &Layout); uint64_t writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) override; + uint64_t writeOneObject(MCAssembler &Asm, const MCAsmLayout &Layout, + DwoMode Mode); + void writeString(const StringRef Str) { - encodeULEB128(Str.size(), W.OS); - W.OS << Str; + encodeULEB128(Str.size(), W->OS); + W->OS << Str; } void writeI32(int32_t val) { char Buffer[4]; support::endian::write32le(Buffer, val); - W.OS.write(Buffer, sizeof(Buffer)); + W->OS.write(Buffer, sizeof(Buffer)); } void writeI64(int64_t val) { char Buffer[8]; support::endian::write64le(Buffer, val); - W.OS.write(Buffer, sizeof(Buffer)); + W->OS.write(Buffer, sizeof(Buffer)); } - void writeValueType(wasm::ValType Ty) { W.OS << static_cast(Ty); } + void writeValueType(wasm::ValType Ty) { W->OS << static_cast(Ty); } void writeTypeSection(ArrayRef Signatures); void writeImportSection(ArrayRef Imports, uint64_t DataSize, @@ -368,17 +390,17 @@ void WasmObjectWriter::startSection(SectionBookkeeping &Section, unsigned SectionId) { LLVM_DEBUG(dbgs() << "startSection " << SectionId << "\n"); - W.OS << char(SectionId); + W->OS << char(SectionId); - Section.SizeOffset = W.OS.tell(); + Section.SizeOffset = W->OS.tell(); // The section size. We don't know the size yet, so reserve enough space // for any 32-bit value; we'll patch it later. - encodeULEB128(0, W.OS, 5); + encodeULEB128(0, W->OS, 5); // The position where the section starts, for measuring its size. - Section.ContentsOffset = W.OS.tell(); - Section.PayloadOffset = W.OS.tell(); + Section.ContentsOffset = W->OS.tell(); + Section.PayloadOffset = W->OS.tell(); Section.Index = SectionCount++; } @@ -388,19 +410,19 @@ startSection(Section, wasm::WASM_SEC_CUSTOM); // The position where the section header ends, for measuring its size. - Section.PayloadOffset = W.OS.tell(); + Section.PayloadOffset = W->OS.tell(); // Custom sections in wasm also have a string identifier. writeString(Name); // The position where the custom section starts. - Section.ContentsOffset = W.OS.tell(); + Section.ContentsOffset = W->OS.tell(); } // Now that the section is complete and we know how big it is, patch up the // section size field at the start of the section. void WasmObjectWriter::endSection(SectionBookkeeping &Section) { - uint64_t Size = W.OS.tell(); + uint64_t Size = W->OS.tell(); // /dev/null doesn't support seek/tell and can report offset of 0. // Simply skip this patching in that case. if (!Size) @@ -414,14 +436,14 @@ // Write the final section size to the payload_len field, which follows // the section id byte. - writePatchableLEB<5>(static_cast(W.OS), Size, + writePatchableLEB<5>(static_cast(W->OS), Size, Section.SizeOffset); } // Emit the Wasm header. void WasmObjectWriter::writeHeader(const MCAssembler &Asm) { - W.OS.write(wasm::WasmMagic, sizeof(wasm::WasmMagic)); - W.write(wasm::WasmVersion); + W->OS.write(wasm::WasmMagic, sizeof(wasm::WasmMagic)); + W->write(wasm::WasmVersion); } void WasmObjectWriter::executePostLayoutBinding(MCAssembler &Asm, @@ -663,7 +685,7 @@ void WasmObjectWriter::applyRelocations( ArrayRef Relocations, uint64_t ContentsOffset, const MCAsmLayout &Layout) { - auto &Stream = static_cast(W.OS); + auto &Stream = static_cast(W->OS); for (const WasmRelocationEntry &RelEntry : Relocations) { uint64_t Offset = ContentsOffset + RelEntry.FixupSection->getSectionOffset() + @@ -718,14 +740,14 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_TYPE); - encodeULEB128(Signatures.size(), W.OS); + encodeULEB128(Signatures.size(), W->OS); for (const WasmSignature &Sig : Signatures) { - W.OS << char(wasm::WASM_TYPE_FUNC); - encodeULEB128(Sig.Params.size(), W.OS); + W->OS << char(wasm::WASM_TYPE_FUNC); + encodeULEB128(Sig.Params.size(), W->OS); for (wasm::ValType Ty : Sig.Params) writeValueType(Ty); - encodeULEB128(Sig.Returns.size(), W.OS); + encodeULEB128(Sig.Returns.size(), W->OS); for (wasm::ValType Ty : Sig.Returns) writeValueType(Ty); } @@ -744,32 +766,32 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_IMPORT); - encodeULEB128(Imports.size(), W.OS); + encodeULEB128(Imports.size(), W->OS); for (const wasm::WasmImport &Import : Imports) { writeString(Import.Module); writeString(Import.Field); - W.OS << char(Import.Kind); + W->OS << char(Import.Kind); switch (Import.Kind) { case wasm::WASM_EXTERNAL_FUNCTION: - encodeULEB128(Import.SigIndex, W.OS); + encodeULEB128(Import.SigIndex, W->OS); break; case wasm::WASM_EXTERNAL_GLOBAL: - W.OS << char(Import.Global.Type); - W.OS << char(Import.Global.Mutable ? 1 : 0); + W->OS << char(Import.Global.Type); + W->OS << char(Import.Global.Mutable ? 1 : 0); break; case wasm::WASM_EXTERNAL_MEMORY: - encodeULEB128(Import.Memory.Flags, W.OS); - encodeULEB128(NumPages, W.OS); // initial + encodeULEB128(Import.Memory.Flags, W->OS); + encodeULEB128(NumPages, W->OS); // initial break; case wasm::WASM_EXTERNAL_TABLE: - W.OS << char(Import.Table.ElemType); - encodeULEB128(0, W.OS); // flags - encodeULEB128(NumElements, W.OS); // initial + W->OS << char(Import.Table.ElemType); + encodeULEB128(0, W->OS); // flags + encodeULEB128(NumElements, W->OS); // initial break; case wasm::WASM_EXTERNAL_EVENT: - encodeULEB128(Import.Event.Attribute, W.OS); - encodeULEB128(Import.Event.SigIndex, W.OS); + encodeULEB128(Import.Event.Attribute, W->OS); + encodeULEB128(Import.Event.SigIndex, W->OS); break; default: llvm_unreachable("unsupported import kind"); @@ -786,9 +808,9 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_FUNCTION); - encodeULEB128(Functions.size(), W.OS); + encodeULEB128(Functions.size(), W->OS); for (const WasmFunction &Func : Functions) - encodeULEB128(Func.SigIndex, W.OS); + encodeULEB128(Func.SigIndex, W->OS); endSection(Section); } @@ -800,10 +822,10 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_EVENT); - encodeULEB128(Events.size(), W.OS); + encodeULEB128(Events.size(), W->OS); for (const wasm::WasmEventType &Event : Events) { - encodeULEB128(Event.Attribute, W.OS); - encodeULEB128(Event.SigIndex, W.OS); + encodeULEB128(Event.Attribute, W->OS); + encodeULEB128(Event.SigIndex, W->OS); } endSection(Section); @@ -816,17 +838,17 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_GLOBAL); - encodeULEB128(Globals.size(), W.OS); + encodeULEB128(Globals.size(), W->OS); for (const wasm::WasmGlobal &Global : Globals) { - encodeULEB128(Global.Type.Type, W.OS); - W.OS << char(Global.Type.Mutable); - W.OS << char(Global.InitExpr.Opcode); + encodeULEB128(Global.Type.Type, W->OS); + W->OS << char(Global.Type.Mutable); + W->OS << char(Global.InitExpr.Opcode); switch (Global.Type.Type) { case wasm::WASM_TYPE_I32: - encodeSLEB128(0, W.OS); + encodeSLEB128(0, W->OS); break; case wasm::WASM_TYPE_I64: - encodeSLEB128(0, W.OS); + encodeSLEB128(0, W->OS); break; case wasm::WASM_TYPE_F32: writeI32(0); @@ -840,7 +862,7 @@ default: llvm_unreachable("unexpected type"); } - W.OS << char(wasm::WASM_OPCODE_END); + W->OS << char(wasm::WASM_OPCODE_END); } endSection(Section); @@ -853,11 +875,11 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_EXPORT); - encodeULEB128(Exports.size(), W.OS); + encodeULEB128(Exports.size(), W->OS); for (const wasm::WasmExport &Export : Exports) { writeString(Export.Name); - W.OS << char(Export.Kind); - encodeULEB128(Export.Index, W.OS); + W->OS << char(Export.Kind); + encodeULEB128(Export.Index, W->OS); } endSection(Section); @@ -870,17 +892,17 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_ELEM); - encodeULEB128(1, W.OS); // number of "segments" - encodeULEB128(0, W.OS); // the table index + encodeULEB128(1, W->OS); // number of "segments" + encodeULEB128(0, W->OS); // the table index // init expr for starting offset - W.OS << char(wasm::WASM_OPCODE_I32_CONST); - encodeSLEB128(InitialTableOffset, W.OS); - W.OS << char(wasm::WASM_OPCODE_END); + W->OS << char(wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(InitialTableOffset, W->OS); + W->OS << char(wasm::WASM_OPCODE_END); - encodeULEB128(TableElems.size(), W.OS); + encodeULEB128(TableElems.size(), W->OS); for (uint32_t Elem : TableElems) - encodeULEB128(Elem, W.OS); + encodeULEB128(Elem, W->OS); endSection(Section); } @@ -891,7 +913,7 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_DATACOUNT); - encodeULEB128(DataSegments.size(), W.OS); + encodeULEB128(DataSegments.size(), W->OS); endSection(Section); } @@ -904,7 +926,7 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_CODE); - encodeULEB128(Functions.size(), W.OS); + encodeULEB128(Functions.size(), W->OS); for (const WasmFunction &Func : Functions) { auto &FuncSection = static_cast(Func.Sym->getSection()); @@ -913,9 +935,9 @@ if (!Func.Sym->getSize()->evaluateAsAbsolute(Size, Layout)) report_fatal_error(".size expression must be evaluatable"); - encodeULEB128(Size, W.OS); - FuncSection.setSectionOffset(W.OS.tell() - Section.ContentsOffset); - Asm.writeSectionData(W.OS, &FuncSection, Layout); + encodeULEB128(Size, W->OS); + FuncSection.setSectionOffset(W->OS.tell() - Section.ContentsOffset); + Asm.writeSectionData(W->OS, &FuncSection, Layout); } // Apply fixups. @@ -932,21 +954,21 @@ SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_DATA); - encodeULEB128(DataSegments.size(), W.OS); // count + encodeULEB128(DataSegments.size(), W->OS); // count for (const WasmDataSegment &Segment : DataSegments) { - encodeULEB128(Segment.InitFlags, W.OS); // flags + encodeULEB128(Segment.InitFlags, W->OS); // flags if (Segment.InitFlags & wasm::WASM_SEGMENT_HAS_MEMINDEX) - encodeULEB128(0, W.OS); // memory index + encodeULEB128(0, W->OS); // memory index if ((Segment.InitFlags & wasm::WASM_SEGMENT_IS_PASSIVE) == 0) { - W.OS << char(Segment.Offset > INT32_MAX ? wasm::WASM_OPCODE_I64_CONST - : wasm::WASM_OPCODE_I32_CONST); - encodeSLEB128(Segment.Offset, W.OS); // offset - W.OS << char(wasm::WASM_OPCODE_END); + W->OS << char(Segment.Offset > INT32_MAX ? wasm::WASM_OPCODE_I64_CONST + : wasm::WASM_OPCODE_I32_CONST); + encodeSLEB128(Segment.Offset, W->OS); // offset + W->OS << char(wasm::WASM_OPCODE_END); } - encodeULEB128(Segment.Data.size(), W.OS); // size - Segment.Section->setSectionOffset(W.OS.tell() - Section.ContentsOffset); - W.OS << Segment.Data; // data + encodeULEB128(Segment.Data.size(), W->OS); // size + Segment.Section->setSectionOffset(W->OS.tell() - Section.ContentsOffset); + W->OS << Segment.Data; // data } // Apply fixups. @@ -979,18 +1001,18 @@ SectionBookkeeping Section; startCustomSection(Section, std::string("reloc.") + Name.str()); - encodeULEB128(SectionIndex, W.OS); - encodeULEB128(Relocs.size(), W.OS); + encodeULEB128(SectionIndex, W->OS); + encodeULEB128(Relocs.size(), W->OS); for (const WasmRelocationEntry &RelEntry : Relocs) { uint64_t Offset = RelEntry.Offset + RelEntry.FixupSection->getSectionOffset(); uint32_t Index = getRelocationIndexValue(RelEntry); - W.OS << char(RelEntry.Type); - encodeULEB128(Offset, W.OS); - encodeULEB128(Index, W.OS); + W->OS << char(RelEntry.Type); + encodeULEB128(Offset, W->OS); + encodeULEB128(Index, W->OS); if (RelEntry.hasAddend()) - encodeSLEB128(RelEntry.Addend, W.OS); + encodeSLEB128(RelEntry.Addend, W->OS); } endSection(Section); @@ -1009,20 +1031,20 @@ const std::map> &Comdats) { SectionBookkeeping Section; startCustomSection(Section, "linking"); - encodeULEB128(wasm::WasmMetadataVersion, W.OS); + encodeULEB128(wasm::WasmMetadataVersion, W->OS); SectionBookkeeping SubSection; if (SymbolInfos.size() != 0) { startSection(SubSection, wasm::WASM_SYMBOL_TABLE); - encodeULEB128(SymbolInfos.size(), W.OS); + encodeULEB128(SymbolInfos.size(), W->OS); for (const wasm::WasmSymbolInfo &Sym : SymbolInfos) { - encodeULEB128(Sym.Kind, W.OS); - encodeULEB128(Sym.Flags, W.OS); + encodeULEB128(Sym.Kind, W->OS); + encodeULEB128(Sym.Flags, W->OS); switch (Sym.Kind) { case wasm::WASM_SYMBOL_TYPE_FUNCTION: case wasm::WASM_SYMBOL_TYPE_GLOBAL: case wasm::WASM_SYMBOL_TYPE_EVENT: - encodeULEB128(Sym.ElementIndex, W.OS); + encodeULEB128(Sym.ElementIndex, W->OS); if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0 || (Sym.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0) writeString(Sym.Name); @@ -1030,15 +1052,15 @@ case wasm::WASM_SYMBOL_TYPE_DATA: writeString(Sym.Name); if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) { - encodeULEB128(Sym.DataRef.Segment, W.OS); - encodeULEB128(Sym.DataRef.Offset, W.OS); - encodeULEB128(Sym.DataRef.Size, W.OS); + encodeULEB128(Sym.DataRef.Segment, W->OS); + encodeULEB128(Sym.DataRef.Offset, W->OS); + encodeULEB128(Sym.DataRef.Size, W->OS); } break; case wasm::WASM_SYMBOL_TYPE_SECTION: { const uint32_t SectionIndex = CustomSections[Sym.ElementIndex].OutputIndex; - encodeULEB128(SectionIndex, W.OS); + encodeULEB128(SectionIndex, W->OS); break; } default: @@ -1050,35 +1072,35 @@ if (DataSegments.size()) { startSection(SubSection, wasm::WASM_SEGMENT_INFO); - encodeULEB128(DataSegments.size(), W.OS); + encodeULEB128(DataSegments.size(), W->OS); for (const WasmDataSegment &Segment : DataSegments) { writeString(Segment.Name); - encodeULEB128(Segment.Alignment, W.OS); - encodeULEB128(Segment.LinkerFlags, W.OS); + encodeULEB128(Segment.Alignment, W->OS); + encodeULEB128(Segment.LinkerFlags, W->OS); } endSection(SubSection); } if (!InitFuncs.empty()) { startSection(SubSection, wasm::WASM_INIT_FUNCS); - encodeULEB128(InitFuncs.size(), W.OS); + encodeULEB128(InitFuncs.size(), W->OS); for (auto &StartFunc : InitFuncs) { - encodeULEB128(StartFunc.first, W.OS); // priority - encodeULEB128(StartFunc.second, W.OS); // function index + encodeULEB128(StartFunc.first, W->OS); // priority + encodeULEB128(StartFunc.second, W->OS); // function index } endSection(SubSection); } if (Comdats.size()) { startSection(SubSection, wasm::WASM_COMDAT_INFO); - encodeULEB128(Comdats.size(), W.OS); + encodeULEB128(Comdats.size(), W->OS); for (const auto &C : Comdats) { writeString(C.first); - encodeULEB128(0, W.OS); // flags for future use - encodeULEB128(C.second.size(), W.OS); + encodeULEB128(0, W->OS); // flags for future use + encodeULEB128(C.second.size(), W->OS); for (const WasmComdatEntry &Entry : C.second) { - encodeULEB128(Entry.Kind, W.OS); - encodeULEB128(Entry.Index, W.OS); + encodeULEB128(Entry.Kind, W->OS); + encodeULEB128(Entry.Index, W->OS); } } endSection(SubSection); @@ -1094,8 +1116,8 @@ auto *Sec = CustomSection.Section; startCustomSection(Section, CustomSection.Name); - Sec->setSectionOffset(W.OS.tell() - Section.ContentsOffset); - Asm.writeSectionData(W.OS, Sec, Layout); + Sec->setSectionOffset(W->OS.tell() - Section.ContentsOffset); + Asm.writeSectionData(W->OS, Sec, Layout); CustomSection.OutputContentsOffset = Section.ContentsOffset; CustomSection.OutputIndex = Section.Index; @@ -1175,25 +1197,9 @@ return true; } - -uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm, - const MCAsmLayout &Layout) { - uint64_t StartOffset = W.OS.tell(); - - LLVM_DEBUG(dbgs() << "WasmObjectWriter::writeObject\n"); - - // Collect information from the available symbols. - SmallVector Functions; - SmallVector TableElems; - SmallVector Imports; - SmallVector Exports; - SmallVector Events; - SmallVector Globals; - SmallVector SymbolInfos; - SmallVector, 2> InitFuncs; - std::map> Comdats; - uint64_t DataSize = 0; - +void WasmObjectWriter::prepareImports( + SmallVectorImpl &Imports, MCAssembler &Asm, + const MCAsmLayout &Layout) { // For now, always emit the memory import, since loads and stores are not // valid without it. In the future, we could perhaps be more clever and omit // it if there are no loads or stores. @@ -1291,13 +1297,57 @@ GOTIndices[&WS] = NumGlobalImports++; } } +} + +uint64_t WasmObjectWriter::writeObject(MCAssembler &Asm, + const MCAsmLayout &Layout) { + support::endian::Writer MainWriter(*OS, support::little); + W = &MainWriter; + if (IsSplitDwarf) { + uint64_t TotalSize = writeOneObject(Asm, Layout, DwoMode::NonDwoOnly); + assert(DwoOS); + support::endian::Writer DwoWriter(*DwoOS, support::little); + W = &DwoWriter; + return TotalSize + writeOneObject(Asm, Layout, DwoMode::DwoOnly); + } else { + return writeOneObject(Asm, Layout, DwoMode::AllSections); + } +} + +uint64_t WasmObjectWriter::writeOneObject(MCAssembler &Asm, + const MCAsmLayout &Layout, + DwoMode Mode) { + uint64_t StartOffset = W->OS.tell(); + SectionCount = 0; + CustomSections.clear(); + LLVM_DEBUG(dbgs() << "WasmObjectWriter::writeObject\n"); + + // Collect information from the available symbols. + SmallVector Functions; + SmallVector TableElems; + SmallVector Imports; + SmallVector Exports; + SmallVector Events; + SmallVector Globals; + SmallVector SymbolInfos; + SmallVector, 2> InitFuncs; + std::map> Comdats; + uint64_t DataSize = 0; + if (Mode != DwoMode::DwoOnly) { + prepareImports(Imports, Asm, Layout); + } // Populate DataSegments and CustomSections, which must be done before // populating DataLocations. for (MCSection &Sec : Asm) { auto &Section = static_cast(Sec); StringRef SectionName = Section.getName(); + if (Mode == DwoMode::NonDwoOnly && isDwoSection(Sec)) + continue; + if (Mode == DwoMode::DwoOnly && !isDwoSection(Sec)) + continue; + // .init_array sections are handled specially elsewhere. if (SectionName.startswith(".init_array")) continue; @@ -1694,23 +1744,33 @@ // Write out the Wasm header. writeHeader(Asm); - writeTypeSection(Signatures); - writeImportSection(Imports, DataSize, TableElems.size()); - writeFunctionSection(Functions); - // Skip the "table" section; we import the table instead. - // Skip the "memory" section; we import the memory instead. - writeEventSection(Events); - writeGlobalSection(Globals); - writeExportSection(Exports); - writeElemSection(TableElems); - writeDataCountSection(); - uint32_t CodeSectionIndex = writeCodeSection(Asm, Layout, Functions); - uint32_t DataSectionIndex = writeDataSection(Layout); - for (auto &CustomSection : CustomSections) + uint32_t CodeSectionIndex, DataSectionIndex; + if (Mode != DwoMode::DwoOnly) { + writeTypeSection(Signatures); + writeImportSection(Imports, DataSize, TableElems.size()); + writeFunctionSection(Functions); + // Skip the "table" section; we import the table instead. + // Skip the "memory" section; we import the memory instead. + writeEventSection(Events); + writeGlobalSection(Globals); + writeExportSection(Exports); + writeElemSection(TableElems); + writeDataCountSection(); + + CodeSectionIndex = writeCodeSection(Asm, Layout, Functions); + DataSectionIndex = writeDataSection(Layout); + } + + for (auto &CustomSection : CustomSections) { writeCustomSection(CustomSection, Asm, Layout); - writeLinkingMetaDataSection(SymbolInfos, InitFuncs, Comdats); - writeRelocSection(CodeSectionIndex, "CODE", CodeRelocations); - writeRelocSection(DataSectionIndex, "DATA", DataRelocations); + } + + if (Mode != DwoMode::DwoOnly) { + writeLinkingMetaDataSection(SymbolInfos, InitFuncs, Comdats); + + writeRelocSection(CodeSectionIndex, "CODE", CodeRelocations); + writeRelocSection(DataSectionIndex, "DATA", DataRelocations); + } writeCustomRelocSections(); if (ProducersSection) writeCustomSection(*ProducersSection, Asm, Layout); @@ -1718,7 +1778,7 @@ writeCustomSection(*TargetFeaturesSection, Asm, Layout); // TODO: Translate the .comment section to the output. - return W.OS.tell() - StartOffset; + return W->OS.tell() - StartOffset; } std::unique_ptr @@ -1726,3 +1786,10 @@ raw_pwrite_stream &OS) { return std::make_unique(std::move(MOTW), OS); } + +std::unique_ptr +llvm::createWasmDwoObjectWriter(std::unique_ptr MOTW, + raw_pwrite_stream &OS, + raw_pwrite_stream &DwoOS) { + return std::make_unique(std::move(MOTW), OS, DwoOS); +} diff --git a/llvm/test/DebugInfo/WebAssembly/fission-cu.ll b/llvm/test/DebugInfo/WebAssembly/fission-cu.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/WebAssembly/fission-cu.ll @@ -0,0 +1,121 @@ +; RUN: llc -split-dwarf-file=baz.dwo -O0 %s -mtriple=wasm32-unknown-unknown -filetype=obj -o %t +; RUN: llvm-dwarfdump -v -all %t | FileCheck %s +; RUN: llvm-readobj --relocations %t | FileCheck --check-prefix=OBJ %s +; RUN: llvm-objdump -h %t | FileCheck --check-prefix=HDR %s + +; This test is derived from test/DebugInfo/X86/fission-cu.ll + +source_filename = "test/DebugInfo/WebAssembly/fission-cu.ll" + +@a = global i32 0, align 4, !dbg !0 + +!llvm.dbg.cu = !{!4} +!llvm.module.flags = !{!7} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = !DIGlobalVariable(name: "a", scope: null, file: !2, line: 1, type: !3, isLocal: false, isDefinition: true) +!2 = !DIFile(filename: "baz.c", directory: "/usr/local/google/home/echristo/tmp") +!3 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang version 3.3 (trunk 169021) (llvm/trunk 169020)", isOptimized: false, runtimeVersion: 0, splitDebugFilename: "baz.dwo", emissionKind: FullDebug, enums: !5, retainedTypes: !5, globals: !6, imports: !5) +!5 = !{} +; Check that the skeleton compile unit contains the proper attributes: +; This DIE has the following attributes: DW_AT_comp_dir, DW_AT_stmt_list, +; DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_dwo_name, DW_AT_dwo_id, +; DW_AT_ranges_base, DW_AT_addr_base. + +; CHECK: .debug_abbrev contents: +; CHECK: Abbrev table for offset: 0x00000000 +; CHECK: [1] DW_TAG_compile_unit DW_CHILDREN_no +; CHECK: DW_AT_stmt_list DW_FORM_sec_offset +; CHECK: DW_AT_comp_dir DW_FORM_strp +; CHECK: DW_AT_GNU_dwo_name DW_FORM_strp +; CHECK: DW_AT_GNU_dwo_id DW_FORM_data8 + +; Check that we're using the right forms. +; CHECK: .debug_abbrev.dwo contents: +; CHECK: Abbrev table for offset: 0x00000000 +; CHECK: [1] DW_TAG_compile_unit DW_CHILDREN_yes +; CHECK: DW_AT_producer DW_FORM_GNU_str_index +; CHECK: DW_AT_language DW_FORM_data2 +; CHECK: DW_AT_name DW_FORM_GNU_str_index +; CHECK: DW_AT_GNU_dwo_name DW_FORM_GNU_str_index +; CHECK-NOT: DW_AT_low_pc +; CHECK-NOT: DW_AT_stmt_list +; CHECK-NOT: DW_AT_comp_dir +; CHECK: DW_AT_GNU_dwo_id DW_FORM_data8 + +; CHECK: [2] DW_TAG_variable DW_CHILDREN_no +; CHECK: DW_AT_name DW_FORM_GNU_str_index +; CHECK: DW_AT_type DW_FORM_ref4 +; CHECK: DW_AT_external DW_FORM_flag_present +; CHECK: DW_AT_decl_file DW_FORM_data1 +; CHECK: DW_AT_decl_line DW_FORM_data1 +; CHECK: DW_AT_location DW_FORM_exprloc + +; CHECK: [3] DW_TAG_base_type DW_CHILDREN_no +; CHECK: DW_AT_name DW_FORM_GNU_str_index +; CHECK: DW_AT_encoding DW_FORM_data1 +; CHECK: DW_AT_byte_size DW_FORM_data1 + +; CHECK: .debug_info contents: +; CHECK: DW_TAG_compile_unit +; CHECK-NEXT: DW_AT_stmt_list [DW_FORM_sec_offset] (0x00000000) +; CHECK-NEXT: DW_AT_comp_dir [DW_FORM_strp] ( .debug_str[0x00000000] = "/usr/local/google/home/echristo/tmp") +; CHECK-NEXT: DW_AT_GNU_dwo_name [DW_FORM_strp] ( .debug_str[0x00000024] = "baz.dwo") +; CHECK-NEXT: DW_AT_GNU_dwo_id [DW_FORM_data8] (0x1f1f859683d49324) + +; Check that the rest of the compile units have information. +; CHECK: .debug_info.dwo contents: +; CHECK: DW_TAG_compile_unit +; CHECK: DW_AT_producer [DW_FORM_GNU_str_index] (indexed (00000002) string = "clang version 3.3 (trunk 169021) (llvm/trunk 169020)") +; CHECK: DW_AT_language [DW_FORM_data2] (DW_LANG_C99) +; CHECK: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000003) string = "baz.c") +; CHECK: DW_AT_GNU_dwo_name [DW_FORM_GNU_str_index] (indexed (00000004) string = "baz.dwo") +; CHECK-NOT: DW_AT_low_pc +; CHECK-NOT: DW_AT_stmt_list +; CHECK-NOT: DW_AT_comp_dir +; CHECK: DW_AT_GNU_dwo_id [DW_FORM_data8] (0x1f1f859683d49324) +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000000) string = "a") +; CHECK: DW_AT_type [DW_FORM_ref4] (cu + 0x{{[0-9a-f]*}} => {[[TYPE:0x[0-9a-f]*]]} +; CHECK: DW_AT_external [DW_FORM_flag_present] (true) +; CHECK: DW_AT_decl_file [DW_FORM_data1] (0x01) +; CHECK: DW_AT_decl_line [DW_FORM_data1] (1) +; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_GNU_addr_index 0x0) +; CHECK: [[TYPE]]: DW_TAG_base_type +; CHECK: DW_AT_name [DW_FORM_GNU_str_index] (indexed (00000001) string = "int") + +; CHECK: .debug_str contents: +; CHECK: 0x00000000: "/usr/local/google/home/echristo/tmp" +; CHECK: 0x00000024: "baz.dwo" + +; CHECK: .debug_str.dwo contents: +; CHECK: 0x00000000: "a" +; CHECK: 0x00000002: "int" +; CHECK: 0x00000006: "clang version 3.3 (trunk 169021) (llvm/trunk 169020)" +; CHECK: 0x0000003b: "baz.c" +; CHECK: 0x00000041: "baz.dwo" + +; CHECK: .debug_str_offsets.dwo contents: +; CHECK: 0x00000000: 00000000 +; CHECK: 0x00000004: 00000002 +; CHECK: 0x00000008: 00000006 +; CHECK: 0x0000000c: 0000003b +; CHECK: 0x00000010: 00000041 + +; Object file checks +; For wasm we should have this set of relocations for the debug info section +; +; OBJ: .debug_info +; OBJ-NEXT: R_WASM_SECTION_OFFSET_I32 .debug_abbrev 0 +; OBJ-NEXT: R_WASM_SECTION_OFFSET_I32 .debug_line 0 +; OBJ-NEXT: R_WASM_SECTION_OFFSET_I32 .debug_str 0 +; OBJ-NEXT: R_WASM_SECTION_OFFSET_I32 .debug_str 36 +; OBJ-NEXT: R_WASM_SECTION_OFFSET_I32 .debug_addr 0 +; OBJ-NEXT: } + +; HDR-NOT: .debug_aranges +; HDR-NOT: .rela.{{.*}}.dwo + +!6 = !{!0} +!7 = !{i32 1, !"Debug Info Version", i32 3} diff --git a/llvm/test/DebugInfo/WebAssembly/fission-sections.ll b/llvm/test/DebugInfo/WebAssembly/fission-sections.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/WebAssembly/fission-sections.ll @@ -0,0 +1,48 @@ +; RUN: llc -split-dwarf-file=baz.dwo -split-dwarf-output=%t.dwo -O0 %s -mtriple=wasm32-unknown-unknown -filetype=obj -o %t +; RUN: llvm-objdump -h %t | FileCheck --check-prefix=OBJ %s +; RUN: llvm-objdump -h %t.dwo | FileCheck --check-prefix=DWO %s + + +; This test is derived from test/DebugInfo/X86/fission-cu.ll +; But it checks that the output objects have the expected sections + +source_filename = "test/DebugInfo/WebAssembly/fission-cu.ll" + +@a = global i32 0, align 4, !dbg !0 + +!llvm.dbg.cu = !{!4} +!llvm.module.flags = !{!7} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = !DIGlobalVariable(name: "a", scope: null, file: !2, line: 1, type: !3, isLocal: false, isDefinition: true) +!2 = !DIFile(filename: "baz.c", directory: "/usr/local/google/home/echristo/tmp") +!3 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang version 3.3 (trunk 169021) (llvm/trunk 169020)", isOptimized: false, runtimeVersion: 0, splitDebugFilename: "baz.dwo", emissionKind: FullDebug, enums: !5, retainedTypes: !5, globals: !6, imports: !5) +!5 = !{} +!6 = !{!0} +!7 = !{i32 1, !"Debug Info Version", i32 3} + +; CHECK-LABEL: Sections: + +; OBJ: Idx Name +; OBJ-NEXT: 0 IMPORT +; OBJ-NEXT: DATACOUNT +; OBJ-NEXT: DATA +; OBJ-NEXT: .debug_abbrev +; OBJ-NEXT: .debug_info +; OBJ-NEXT: .debug_str +; OBJ-NEXT: .debug_addr +; OBJ-NEXT: .debug_pubnames +; OBJ-NEXT: .debug_pubtypes +; OBJ-NEXT: .debug_line +; OBJ-NEXT: linking + + +; DWO: Idx Name +; DWO-NOT: IMPORT +; DWO-NOT: DATA +; DWO: 0 .debug_str.dwo +; DWO-NEXT: .debug_str_offsets.dwo +; DWO-NEXT: .debug_info.dwo +; DWO-NEXT: .debug_abbrev.dwo +; DWO-NEXT: producers