Index: include/llvm/BinaryFormat/Wasm.h =================================================================== --- include/llvm/BinaryFormat/Wasm.h +++ include/llvm/BinaryFormat/Wasm.h @@ -43,6 +43,12 @@ std::vector Needed; // Shared library depenedencies }; +struct WasmProducerInfo { + std::vector> Languages; + std::vector> Tools; + std::vector> SDKs; +}; + struct WasmExport { StringRef Name; uint8_t Kind; Index: include/llvm/Object/Wasm.h =================================================================== --- include/llvm/Object/Wasm.h +++ include/llvm/Object/Wasm.h @@ -130,6 +130,7 @@ static bool classof(const Binary *v) { return v->isWasm(); } const wasm::WasmDylinkInfo &dylinkInfo() const { return DylinkInfo; } + const wasm::WasmProducerInfo &getProducerInfo() const { return ProducerInfo; } ArrayRef types() const { return Signatures; } ArrayRef functionTypes() const { return FunctionTypes; } ArrayRef imports() const { return Imports; } @@ -149,7 +150,6 @@ uint32_t getNumImportedGlobals() const { return NumImportedGlobals; } uint32_t getNumImportedFunctions() const { return NumImportedFunctions; } uint32_t getNumImportedEvents() const { return NumImportedEvents; } - void moveSymbolNext(DataRefImpl &Symb) const override; uint32_t getSymbolFlags(DataRefImpl Symb) const override; @@ -252,11 +252,13 @@ Error parseLinkingSection(ReadContext &Ctx); Error parseLinkingSectionSymtab(ReadContext &Ctx); Error parseLinkingSectionComdat(ReadContext &Ctx); + Error parseProducersSection(ReadContext &Ctx); Error parseRelocSection(StringRef Name, ReadContext &Ctx); wasm::WasmObjectHeader Header; std::vector Sections; wasm::WasmDylinkInfo DylinkInfo; + wasm::WasmProducerInfo ProducerInfo; std::vector Signatures; std::vector FunctionTypes; std::vector Tables; Index: include/llvm/ObjectYAML/WasmYAML.h =================================================================== --- include/llvm/ObjectYAML/WasmYAML.h +++ include/llvm/ObjectYAML/WasmYAML.h @@ -123,6 +123,11 @@ StringRef Name; }; +struct ProducerEntry { + std::string Name; + std::string Version; +}; + struct SegmentInfo { uint32_t Index; StringRef Name; @@ -224,6 +229,19 @@ std::vector Comdats; }; +struct ProducersSection : CustomSection { + ProducersSection() : CustomSection("producers") {} + + static bool classof(const Section *S) { + auto C = dyn_cast(S); + return C && C->Name == "producers"; + } + + std::vector Languages; + std::vector Tools; + std::vector SDKs; +}; + struct TypeSection : Section { TypeSection() : Section(wasm::WASM_SEC_TYPE) {} @@ -366,6 +384,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::LocalDecl) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Relocation) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::NameEntry) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::ProducerEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::SegmentInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::SymbolInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::InitFunction) @@ -444,6 +463,10 @@ static void mapping(IO &IO, WasmYAML::NameEntry &NameEntry); }; +template <> struct MappingTraits { + static void mapping(IO &IO, WasmYAML::ProducerEntry &ProducerEntry); +}; + template <> struct MappingTraits { static void mapping(IO &IO, WasmYAML::SegmentInfo &SegmentInfo); }; Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -224,6 +224,7 @@ // Stores output data (index, relocations, content offset) for custom // section. std::vector CustomSections; + std::unique_ptr ProducersSection; // Relocations for fixing up references in the custom sections. DenseMap> CustomSectionsRelocations; @@ -265,6 +266,8 @@ WasmIndices.clear(); TableIndices.clear(); DataLocations.clear(); + CustomSections.clear(); + ProducersSection.reset(); CustomSectionsRelocations.clear(); SignatureIndices.clear(); Signatures.clear(); @@ -311,7 +314,8 @@ ArrayRef SymbolInfos, ArrayRef> InitFuncs, const std::map> &Comdats); - void writeCustomSections(const MCAssembler &Asm, const MCAsmLayout &Layout); + void writeCustomSection(WasmCustomSection &CustomSection, + const MCAssembler &Asm, const MCAsmLayout &Layout); void writeCustomRelocSections(); void updateCustomSectionRelocations(const SmallVector &Functions, @@ -1045,25 +1049,24 @@ endSection(Section); } -void WasmObjectWriter::writeCustomSections(const MCAssembler &Asm, - const MCAsmLayout &Layout) { - for (auto &CustomSection : CustomSections) { - SectionBookkeeping Section; - auto *Sec = CustomSection.Section; - startCustomSection(Section, CustomSection.Name); +void WasmObjectWriter::writeCustomSection(WasmCustomSection &CustomSection, + const MCAssembler &Asm, + const MCAsmLayout &Layout) { + SectionBookkeeping Section; + 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; + CustomSection.OutputContentsOffset = Section.ContentsOffset; + CustomSection.OutputIndex = Section.Index; - endSection(Section); + endSection(Section); - // Apply fixups. - auto &Relocations = CustomSectionsRelocations[CustomSection.Section]; - applyRelocations(Relocations, CustomSection.OutputContentsOffset); - } + // Apply fixups. + auto &Relocations = CustomSectionsRelocations[CustomSection.Section]; + applyRelocations(Relocations, CustomSection.OutputContentsOffset); } uint32_t WasmObjectWriter::getFunctionType(const MCSymbolWasm &Symbol) { @@ -1282,6 +1285,13 @@ report_fatal_error("section name and begin symbol should match: " + Twine(SectionName)); } + + // Separate out the producers section + if (Name == "producers") { + ProducersSection = llvm::make_unique(Name, &Section); + continue; + } + CustomSections.emplace_back(Name, &Section); } } @@ -1570,11 +1580,14 @@ writeElemSection(TableElems); writeCodeSection(Asm, Layout, Functions); writeDataSection(); - writeCustomSections(Asm, Layout); + for (auto &CustomSection : CustomSections) + writeCustomSection(CustomSection, Asm, Layout); writeLinkingMetaDataSection(SymbolInfos, InitFuncs, Comdats); writeRelocSection(CodeSectionIndex, "CODE", CodeRelocations); writeRelocSection(DataSectionIndex, "DATA", DataRelocations); writeCustomRelocSections(); + if (ProducersSection) + writeCustomSection(*ProducersSection, Asm, Layout); // TODO: Translate the .comment section to the output. return W.OS.tell() - StartOffset; Index: lib/Object/WasmObjectFile.cpp =================================================================== --- lib/Object/WasmObjectFile.cpp +++ lib/Object/WasmObjectFile.cpp @@ -10,6 +10,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Triple.h" @@ -659,6 +660,47 @@ return Error::success(); } +Error WasmObjectFile::parseProducersSection(ReadContext &Ctx) { + llvm::SmallSet FieldsSeen; + uint32_t Fields = readVaruint32(Ctx); + for (size_t i = 0; i < Fields; ++i) { + StringRef FieldName = readString(Ctx); + if (!FieldsSeen.insert(FieldName).second) + return make_error( + "Producers section does not have unique fields", + object_error::parse_failed); + std::vector> *ProducerVec = nullptr; + if (FieldName == "language") { + ProducerVec = &ProducerInfo.Languages; + } else if (FieldName == "processed-by") { + ProducerVec = &ProducerInfo.Tools; + } else if (FieldName == "sdk") { + ProducerVec = &ProducerInfo.SDKs; + } else { + return make_error( + "Producers section field is not named one of language, processed-by, " + "or sdk", + object_error::parse_failed); + } + uint32_t ValueCount = readVaruint32(Ctx); + llvm::SmallSet ProducersSeen; + for (size_t j = 0; j < ValueCount; ++j) { + StringRef Name = readString(Ctx); + StringRef Version = readString(Ctx); + if (!ProducersSeen.insert(Name).second) { + return make_error( + "Producers section contains repeated producer", + object_error::parse_failed); + } + ProducerVec->emplace_back(Name, Version); + } + } + if (Ctx.Ptr != Ctx.End) + return make_error("Producers section ended prematurely", + object_error::parse_failed); + return Error::success(); +} + Error WasmObjectFile::parseRelocSection(StringRef Name, ReadContext &Ctx) { uint32_t SectionIndex = readVaruint32(Ctx); if (SectionIndex >= Sections.size()) @@ -757,6 +799,9 @@ } else if (Sec.Name == "linking") { if (Error Err = parseLinkingSection(Ctx)) return Err; + } else if (Sec.Name == "producers") { + if (Error Err = parseProducersSection(Ctx)) + return Err; } else if (Sec.Name.startswith("reloc.")) { if (Error Err = parseRelocSection(Sec.Name, Ctx)) return Err; Index: lib/ObjectYAML/WasmYAML.cpp =================================================================== --- lib/ObjectYAML/WasmYAML.cpp +++ lib/ObjectYAML/WasmYAML.cpp @@ -74,6 +74,14 @@ IO.mapOptional("Comdats", Section.Comdats); } +static void sectionMapping(IO &IO, WasmYAML::ProducersSection &Section) { + commonSectionMapping(IO, Section); + IO.mapRequired("Name", Section.Name); + IO.mapOptional("Languages", Section.Languages); + IO.mapOptional("Tools", Section.Tools); + IO.mapOptional("SDKs", Section.SDKs); +} + static void sectionMapping(IO &IO, WasmYAML::CustomSection &Section) { commonSectionMapping(IO, Section); IO.mapRequired("Name", Section.Name); @@ -169,6 +177,10 @@ if (!IO.outputting()) Section.reset(new WasmYAML::NameSection()); sectionMapping(IO, *cast(Section.get())); + } else if (SectionName == "producers") { + if (!IO.outputting()) + Section.reset(new WasmYAML::ProducersSection()); + sectionMapping(IO, *cast(Section.get())); } else { if (!IO.outputting()) Section.reset(new WasmYAML::CustomSection(SectionName)); @@ -293,6 +305,12 @@ IO.mapRequired("Name", NameEntry.Name); } +void MappingTraits::mapping( + IO &IO, WasmYAML::ProducerEntry &ProducerEntry) { + IO.mapRequired("Name", ProducerEntry.Name); + IO.mapRequired("Version", ProducerEntry.Version); +} + void MappingTraits::mapping( IO &IO, WasmYAML::SegmentInfo &SegmentInfo) { IO.mapRequired("Index", SegmentInfo.Index); Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -22,6 +22,7 @@ #include "WebAssemblyMCInstLower.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyRegisterInfo.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/AsmPrinter.h" @@ -146,6 +147,35 @@ OutStreamer->PopSection(); } } + + if (const NamedMDNode *Ident = M.getNamedMetadata("llvm.ident")) { + llvm::SmallSet SeenTools; + llvm::SmallVector, 4> Tools; + for (size_t i = 0, e = Ident->getNumOperands(); i < e; ++i) { + const auto *S = cast(Ident->getOperand(i)->getOperand(0)); + std::pair Field = S->getString().split("version"); + StringRef Name = Field.first.trim(); + StringRef Version = Field.second.trim(); + if (!SeenTools.insert(Name).second) + continue; + Tools.emplace_back(Name, Version); + } + MCSectionWasm *Producers = OutContext.getWasmSection( + ".custom_section.producers", SectionKind::getMetadata()); + OutStreamer->PushSection(); + OutStreamer->SwitchSection(Producers); + OutStreamer->EmitULEB128IntValue(1); + OutStreamer->EmitULEB128IntValue(strlen("processed-by")); + OutStreamer->EmitBytes("processed-by"); + OutStreamer->EmitULEB128IntValue(Tools.size()); + for (auto &Tool : Tools) { + OutStreamer->EmitULEB128IntValue(Tool.first.size()); + OutStreamer->EmitBytes(Tool.first); + OutStreamer->EmitULEB128IntValue(Tool.second.size()); + OutStreamer->EmitBytes(Tool.second); + } + OutStreamer->PopSection(); + } } void WebAssemblyAsmPrinter::EmitConstantPool() { Index: test/CodeGen/WebAssembly/custom-sections.ll =================================================================== --- test/CodeGen/WebAssembly/custom-sections.ll +++ test/CodeGen/WebAssembly/custom-sections.ll @@ -10,6 +10,9 @@ !2 = !{ !"green", !"qux" } !wasm.custom_sections = !{ !0, !1, !2 } +!llvm.ident = !{!3} +!3 = !{!"clang version 123"} + ; CHECK: .section .custom_section.red,"",@ ; CHECK-NEXT: .ascii "foo" @@ -18,3 +21,13 @@ ; CHECK: .section .custom_section.green,"",@ ; CHECK-NEXT: .ascii "qux" + +; CHECK: .section .custom_section.producers,"",@ +; CHECK-NEXT: .int8 1 +; CHECK-NEXT: .int8 12 +; CHECK-NEXT: .ascii "processed-by" +; CHECK-NEXT: .int8 1 +; CHECK-NEXT: .int8 5 +; CHECK-NEXT: .ascii "clang" +; CHECK-NEXT: .int8 3 +; CHECK-NEXT: .ascii "123" Index: test/MC/WebAssembly/custom-sections.ll =================================================================== --- test/MC/WebAssembly/custom-sections.ll +++ test/MC/WebAssembly/custom-sections.ll @@ -9,6 +9,9 @@ !2 = !{ !"green", !"qux" } !wasm.custom_sections = !{ !0, !1, !2 } +!3 = !{ !"clang version 123"} +!llvm.ident = !{!3} + ; CHECK: Section { ; CHECK: Type: CUSTOM (0x0) ; CHECK: Size: 3 @@ -21,3 +24,9 @@ ; CHECK: Offset: 85 ; CHECK: Name: green ; CHECK: } +; CHECK: Section { +; CHECK: Type: CUSTOM (0x0) +; CHECK: Size: 25 +; CHECK: Offset: 118 +; CHECK: Name: producers +; CHECK: } Index: test/MC/WebAssembly/debug-info.ll =================================================================== --- test/MC/WebAssembly/debug-info.ll +++ test/MC/WebAssembly/debug-info.ll @@ -124,6 +124,12 @@ ; CHECK-NEXT: Offset: 991 ; CHECK-NEXT: Name: reloc..debug_line ; CHECK-NEXT: } +; CHECK-NEXT: Section { +; CHECK-NEXT: Type: CUSTOM (0x0) +; CHECK-NEXT: Size: 62 +; CHECK-NEXT: Offset: 1021 +; CHECK-NEXT: Name: producers +; CHECK-NEXT: } ; CHECK-NEXT:] ; CHECK-NEXT:Relocations [ ; CHECK-NEXT: Section (6) DATA { Index: test/ObjectYAML/wasm/producers_section.yaml =================================================================== --- /dev/null +++ test/ObjectYAML/wasm/producers_section.yaml @@ -0,0 +1,29 @@ +# RUN: yaml2obj %s | obj2yaml | FileCheck %s +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: producers + Languages: + - Name: C++ + Version: C++11 + Tools: + - Name: clang + Version: 123 + SDKs: + - Name: emscripten + Version: 9001 +... +# CHECK: Sections: +# CHECK: - Type: CUSTOM +# CHECK: Name: producers +# CHECK: Languages: +# CHECK: - Name: 'C++' +# CHECK: Version: 'C++11' +# CHECK: Tools: +# CHECK: - Name: clang +# CHECK: Version: '123' +# CHECK: SDKs: +# CHECK: - Name: emscripten +# CHECK: Version: '9001' Index: test/ObjectYAML/wasm/producers_section_repeat.yaml =================================================================== --- /dev/null +++ test/ObjectYAML/wasm/producers_section_repeat.yaml @@ -0,0 +1,14 @@ +# RUN: yaml2obj %s | not obj2yaml 2>&1 | FileCheck %s +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: producers + Languages: + - Name: C++ + Version: C++11 + - Name: C++ + Version: C++17 +... +# CHECK: Producers section contains repeated producer Index: tools/obj2yaml/wasm2yaml.cpp =================================================================== --- tools/obj2yaml/wasm2yaml.cpp +++ tools/obj2yaml/wasm2yaml.cpp @@ -133,6 +133,29 @@ } CustomSec = std::move(LinkingSec); + } else if (WasmSec.Name == "producers") { + std::unique_ptr ProducersSec = + make_unique(); + const llvm::wasm::WasmProducerInfo &Info = Obj.getProducerInfo(); + for (auto &E : Info.Languages) { + WasmYAML::ProducerEntry Producer; + Producer.Name = E.first; + Producer.Version = E.second; + ProducersSec->Languages.push_back(Producer); + } + for (auto &E : Info.Tools) { + WasmYAML::ProducerEntry Producer; + Producer.Name = E.first; + Producer.Version = E.second; + ProducersSec->Tools.push_back(Producer); + } + for (auto &E : Info.SDKs) { + WasmYAML::ProducerEntry Producer; + Producer.Name = E.first; + Producer.Version = E.second; + ProducersSec->SDKs.push_back(Producer); + } + CustomSec = std::move(ProducersSec); } else { CustomSec = make_unique(WasmSec.Name); } Index: tools/yaml2obj/yaml2wasm.cpp =================================================================== --- tools/yaml2obj/yaml2wasm.cpp +++ tools/yaml2obj/yaml2wasm.cpp @@ -49,6 +49,7 @@ int writeSectionContent(raw_ostream &OS, WasmYAML::DylinkSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::ProducersSection &Section); WasmYAML::Object &Obj; uint32_t NumImportedFunctions = 0; uint32_t NumImportedGlobals = 0; @@ -255,6 +256,29 @@ return 0; } +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::ProducersSection &Section) { + writeStringRef(Section.Name, OS); + int Fields = int(!Section.Languages.empty()) + int(!Section.Tools.empty()) + + int(!Section.SDKs.empty()); + if (Fields == 0) + return 0; + encodeULEB128(Fields, OS); + for (auto &Field : {std::make_pair(StringRef("language"), &Section.Languages), + std::make_pair(StringRef("processed-by"), &Section.Tools), + std::make_pair(StringRef("sdk"), &Section.SDKs)}) { + if (Field.second->empty()) + continue; + writeStringRef(Field.first, OS); + encodeULEB128(Field.second->size(), OS); + for (auto &Entry : *Field.second) { + writeStringRef(Entry.Name, OS); + writeStringRef(Entry.Version, OS); + } + } + return 0; +} + int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section) { if (auto S = dyn_cast(&Section)) { @@ -266,6 +290,9 @@ } else if (auto S = dyn_cast(&Section)) { if (auto Err = writeSectionContent(OS, *S)) return Err; + } else if (auto S = dyn_cast(&Section)) { + if (auto Err = writeSectionContent(OS, *S)) + return Err; } else { writeStringRef(Section.Name, OS); Section.Payload.writeAsBinary(OS);