Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -883,8 +883,8 @@ The linker may choose any COMDAT key but the sections must contain the same amount of data. -Note that the Mach-O platform doesn't support COMDATs and ELF only supports -``any`` as a selection kind. +Note that the Mach-O platform doesn't support COMDATs, and ELF and WebAssembly +only support ``any`` as a selection kind. Here is an example of a COMDAT group where a function will only be selected if the COMDAT key's section is the largest: Index: include/llvm/ADT/Triple.h =================================================================== --- include/llvm/ADT/Triple.h +++ include/llvm/ADT/Triple.h @@ -660,9 +660,9 @@ return getArch() == Triple::aarch64 || getArch() == Triple::aarch64_be; } - /// Tests wether the target supports comdat + /// Tests whether the target supports comdat bool supportsCOMDAT() const { - return !isOSBinFormatMachO() && !isOSBinFormatWasm(); + return !isOSBinFormatMachO(); } /// @} Index: include/llvm/BinaryFormat/Wasm.h =================================================================== --- include/llvm/BinaryFormat/Wasm.h +++ include/llvm/BinaryFormat/Wasm.h @@ -71,6 +71,13 @@ WasmInitExpr InitExpr; }; +struct WasmGlobalDecl : WasmGlobal { + // Type + // Mutable + // InitExpr + StringRef Comdat; +}; + struct WasmImport { StringRef Module; StringRef Field; @@ -91,6 +98,7 @@ struct WasmFunction { std::vector Locals; ArrayRef Body; + StringRef Comdat; }; struct WasmDataSegment { @@ -100,6 +108,7 @@ StringRef Name; uint32_t Alignment; uint32_t Flags; + StringRef Comdat; }; struct WasmElemSegment { @@ -187,6 +196,7 @@ WASM_DATA_SIZE = 0x3, WASM_DATA_ALIGNMENT = 0x4, WASM_SEGMENT_INFO = 0x5, + WASM_COMDAT_INFO = 0x6, }; const unsigned WASM_SYMBOL_BINDING_MASK = 0x3; @@ -201,6 +211,12 @@ WASM_SYMBOL_VISIBILITY_HIDDEN = 0x4, }; +enum : unsigned { + WASM_COMDAT_DATA = 0x1, + WASM_COMDAT_FUNCTION = 0x2, + WASM_COMDAT_GLOBAL = 0x3, +}; + #define WASM_RELOC(name, value) name = value, enum : unsigned { Index: include/llvm/Object/Wasm.h =================================================================== --- include/llvm/Object/Wasm.h +++ include/llvm/Object/Wasm.h @@ -20,6 +20,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" #include "llvm/BinaryFormat/Wasm.h" #include "llvm/Object/Binary.h" #include "llvm/Object/ObjectFile.h" @@ -133,9 +134,10 @@ const std::vector& imports() const { return Imports; } const std::vector& tables() const { return Tables; } const std::vector& memories() const { return Memories; } - const std::vector& globals() const { return Globals; } + const std::vector& globals() const { return Globals; } const std::vector& exports() const { return Exports; } const wasm::WasmLinkingData& linkingData() const { return LinkingData; } + const llvm::StringSet<>& comdats() const { return Comdats; } uint32_t getNumberOfSymbols() const { return Symbols.size(); @@ -244,13 +246,14 @@ std::vector FunctionTypes; std::vector Tables; std::vector Memories; - std::vector Globals; + std::vector Globals; std::vector Imports; std::vector Exports; std::vector ElemSegments; std::vector DataSegments; std::vector Functions; std::vector Symbols; + StringSet<> Comdats; ArrayRef CodeSection; uint32_t StartFunction = -1; bool HasLinkingSection = false; Index: include/llvm/ObjectYAML/WasmYAML.h =================================================================== --- include/llvm/ObjectYAML/WasmYAML.h +++ include/llvm/ObjectYAML/WasmYAML.h @@ -68,6 +68,14 @@ wasm::WasmInitExpr InitExpr; }; +struct GlobalDecl : Global { + // Type + // Mutable + // InitExpr + StringRef Comdat; +}; + + struct Import { StringRef Module; StringRef Field; @@ -88,6 +96,7 @@ struct Function { std::vector Locals; yaml::BinaryRef Body; + StringRef Comdat; }; struct Relocation { @@ -102,6 +111,7 @@ uint32_t SectionOffset; wasm::WasmInitExpr Offset; yaml::BinaryRef Content; + StringRef Comdat; }; struct NameEntry { @@ -229,7 +239,7 @@ return S->Type == wasm::WASM_SEC_GLOBAL; } - std::vector Globals; + std::vector Globals; }; struct ExportSection : Section { @@ -299,7 +309,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::ElemSegment) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Limits) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::DataSegment) -LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Global) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::GlobalDecl) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Function) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::LocalDecl) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::Relocation) @@ -330,8 +340,8 @@ static void mapping(IO &IO, WasmYAML::Export &Export); }; -template <> struct MappingTraits { - static void mapping(IO &IO, WasmYAML::Global &Global); +template <> struct MappingTraits { + static void mapping(IO &IO, WasmYAML::GlobalDecl &Global); }; template <> struct ScalarEnumerationTraits { Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -1254,15 +1254,17 @@ // Wasm //===----------------------------------------------------------------------===// -static void checkWasmComdat(const GlobalValue *GV) { +static const Comdat *getWasmComdat(const GlobalValue *GV) { const Comdat *C = GV->getComdat(); if (!C) - return; + return nullptr; - // TODO(sbc): At some point we may need COMDAT support but currently - // they are not supported. - report_fatal_error("WebAssembly doesn't support COMDATs, '" + C->getName() + - "' cannot be lowered."); + if (C->getSelectionKind() != Comdat::Any) + report_fatal_error("WebAssembly COMDATs only support " + "SelectionKind::Any, '" + C->getName() + "' cannot be " + "lowered."); + + return C; } static SectionKind getWasmKindForNamedSection(StringRef Name, SectionKind K) { @@ -1278,16 +1280,25 @@ MCSection *TargetLoweringObjectFileWasm::getExplicitSectionGlobal( const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { StringRef Name = GO->getSection(); - checkWasmComdat(GO); + Kind = getWasmKindForNamedSection(Name, Kind); - return getContext().getWasmSection(Name, Kind); + + StringRef Group = ""; + if (const Comdat *C = getWasmComdat(GO)) { + Group = C->getName(); + } + + return getContext().getWasmSection(Name, Kind, Group, + MCContext::GenericSectionID); } static MCSectionWasm *selectWasmSectionForGlobal( MCContext &Ctx, const GlobalObject *GO, SectionKind Kind, Mangler &Mang, const TargetMachine &TM, bool EmitUniqueSection, unsigned *NextUniqueID) { StringRef Group = ""; - checkWasmComdat(GO); + if (const Comdat *C = getWasmComdat(GO)) { + Group = C->getName(); + } bool UniqueSectionNames = TM.getUniqueSectionNames(); SmallString<128> Name = getSectionPrefixForGlobal(Kind); Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -139,6 +139,14 @@ uint32_t ImportIndex; }; +// Information about a single item which is part of a COMDAT. For each data +// segment or function which is in the COMDAT, there is a corresponding +// WasmComdatEntry. +struct WasmComdatEntry { + unsigned Kind; + uint32_t Index; +}; + // Information about a single relocation. struct WasmRelocationEntry { uint64_t Offset; // Where is the relocation. @@ -287,6 +295,7 @@ void writeLinkingMetaDataSection( ArrayRef Segments, uint32_t DataSize, SmallVector, 4> SymbolFlags, + const std::map>& Comdats, bool HasStackPointer, uint32_t StackPointerGlobal); uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry); @@ -929,6 +938,7 @@ void WasmObjectWriter::writeLinkingMetaDataSection( ArrayRef Segments, uint32_t DataSize, SmallVector, 4> SymbolFlags, + const std::map>& Comdats, bool HasStackPointer, uint32_t StackPointerGlobal) { SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_CUSTOM, "linking"); @@ -967,6 +977,20 @@ endSection(SubSection); } + if (Comdats.size()) { + startSection(SubSection, wasm::WASM_COMDAT_INFO); + encodeULEB128(Comdats.size(), getStream()); + for (const auto &C : Comdats) { + writeString(C.first); + encodeULEB128(C.second.size(), getStream()); + for (const WasmComdatEntry &Entry : C.second) { + encodeULEB128(Entry.Kind, getStream()); + encodeULEB128(Entry.Index, getStream()); + } + } + endSection(SubSection); + } + endSection(Section); } @@ -1007,6 +1031,7 @@ SmallVector Imports; SmallVector Exports; SmallVector, 4> SymbolFlags; + std::map> Comdats; SmallPtrSet IsAddressTaken; unsigned NumFuncImports = 0; SmallVector DataSegments; @@ -1165,6 +1190,12 @@ Segment.Flags = 0; DataSize += Segment.Data.size(); Section.setMemoryOffset(Segment.Offset); + + if (const MCSymbolWasm *C = Section.getGroup()) { + Comdats[C->getName()].emplace_back( + WasmComdatEntry{wasm::WASM_COMDAT_DATA, + static_cast(DataSegments.size()) - 1}); + } } // Handle regular defined and undefined symbols. @@ -1193,6 +1224,7 @@ continue; unsigned Index; + unsigned ComdatKind = 0; if (WS.isFunction()) { if (WS.isDefined(/*SetUsed=*/false)) { @@ -1213,6 +1245,7 @@ Func.Sym = &WS; SymbolIndices[&WS] = Index; Functions.push_back(Func); + ComdatKind = wasm::WASM_COMDAT_FUNCTION; } else { // An import; the index was assigned above. Index = SymbolIndices.find(&WS)->second; @@ -1255,6 +1288,7 @@ SymbolIndices[&WS] = Index; DEBUG(dbgs() << " -> global index: " << Index << "\n"); Globals.push_back(Global); + ComdatKind = wasm::WASM_COMDAT_GLOBAL; } // If the symbol is visible outside this translation unit, export it. @@ -1268,8 +1302,13 @@ Export.Kind = wasm::WASM_EXTERNAL_GLOBAL; DEBUG(dbgs() << " -> export " << Exports.size() << "\n"); Exports.push_back(Export); + if (!WS.isExternal()) SymbolFlags.emplace_back(WS.getName(), wasm::WASM_SYMBOL_BINDING_LOCAL); + + auto &Section = static_cast(WS.getSection(false)); + if (const MCSymbolWasm *C = Section.getGroup()) + Comdats[C->getName()].emplace_back(WasmComdatEntry{ComdatKind, Index}); } } @@ -1330,7 +1369,7 @@ writeNameSection(Functions, Imports, NumFuncImports); writeCodeRelocSection(); writeDataRelocSection(); - writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags, + writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags, Comdats, HasStackPointer, StackPointerGlobal); // TODO: Translate the .comment section to the output. Index: lib/Object/WasmObjectFile.cpp =================================================================== --- lib/Object/WasmObjectFile.cpp +++ lib/Object/WasmObjectFile.cpp @@ -398,6 +398,59 @@ } break; } + case wasm::WASM_COMDAT_INFO: { + uint32_t ComdatCount = readVaruint32(Ptr); + for (uint32_t i = 0; i < ComdatCount; i++) { + StringRef Name = readString(Ptr); + if (Name.empty() || !Comdats.insert(Name).second) { + return make_error("Bad/duplicate COMDAT name", + object_error::parse_failed); + } + + uint32_t EntryCount = readVaruint32(Ptr); + for (uint32_t j = 0; j < EntryCount; j++) { + unsigned Kind = readVaruint32(Ptr); + unsigned Index = readVaruint32(Ptr); + switch (Kind) { + default: + return make_error("Invalid COMDAT entry type", + object_error::parse_failed); + case wasm::WASM_COMDAT_DATA: + if (Index >= DataSegments.size()) + return make_error("COMDAT data index out of range", + object_error::parse_failed); + if (!DataSegments[Index].Data.Comdat.empty()) + return make_error("Data segment in two COMDATs", + object_error::parse_failed); + DataSegments[Index].Data.Comdat = Name; + break; + case wasm::WASM_COMDAT_FUNCTION: + if (Index < NumImportedFunctions || + Index >= Functions.size() + NumImportedFunctions) + return make_error("COMDAT function index out of range", + object_error::parse_failed); + Index -= NumImportedFunctions; + if (!Functions[Index].Comdat.empty()) + return make_error("Function in two COMDATs", + object_error::parse_failed); + Functions[Index].Comdat = Name; + break; + case wasm::WASM_COMDAT_GLOBAL: + if (Index < NumImportedGlobals || + Index >= Globals.size() + NumImportedGlobals) + return make_error("COMDAT global index out of range", + object_error::parse_failed); + Index -= NumImportedGlobals; + if (!Globals[Index].Comdat.empty()) + return make_error("Global in two COMDATs", + object_error::parse_failed); + Globals[Index].Comdat = Name; + break; + } + } + } + break; + } case wasm::WASM_STACK_POINTER: default: Ptr += Size; @@ -608,7 +661,7 @@ uint32_t Count = readVaruint32(Ptr); Globals.reserve(Count); while (Count--) { - wasm::WasmGlobal Global; + wasm::WasmGlobalDecl Global; Global.Type = readVarint7(Ptr); Global.Mutable = readVaruint1(Ptr); if (Error Err = readInitExpr(Global.InitExpr, Ptr)) Index: lib/ObjectYAML/WasmYAML.cpp =================================================================== --- lib/ObjectYAML/WasmYAML.cpp +++ lib/ObjectYAML/WasmYAML.cpp @@ -249,6 +249,7 @@ WasmYAML::Function &Function) { IO.mapRequired("Locals", Function.Locals); IO.mapRequired("Body", Function.Body); + IO.mapOptional("Comdat", Function.Comdat, StringRef()); } void MappingTraits::mapping( @@ -320,11 +321,12 @@ IO.mapRequired("Index", Export.Index); } -void MappingTraits::mapping(IO &IO, - WasmYAML::Global &Global) { +void MappingTraits::mapping(IO &IO, + WasmYAML::GlobalDecl &Global) { IO.mapRequired("Type", Global.Type); IO.mapRequired("Mutable", Global.Mutable); IO.mapRequired("InitExpr", Global.InitExpr); + IO.mapOptional("Comdat", Global.Comdat, StringRef()); } void MappingTraits::mapping(IO &IO, @@ -357,6 +359,7 @@ IO.mapRequired("MemoryIndex", Segment.MemoryIndex); IO.mapRequired("Offset", Segment.Offset); IO.mapRequired("Content", Segment.Content); + IO.mapOptional("Comdat", Segment.Comdat, StringRef()); } void MappingTraits::mapping(IO &IO, Index: tools/obj2yaml/wasm2yaml.cpp =================================================================== --- tools/obj2yaml/wasm2yaml.cpp +++ tools/obj2yaml/wasm2yaml.cpp @@ -84,6 +84,8 @@ LinkingSec->SymbolInfos.push_back(Info); } } + // (COMDATs are attached to the symbols they apply to rather than being + // represented as part of the custom section in the YAML output.) LinkingSec->DataSize = Obj.linkingData().DataSize; CustomSec = std::move(LinkingSec); } else { @@ -181,10 +183,11 @@ case wasm::WASM_SEC_GLOBAL: { auto GlobalSec = make_unique(); for (auto &Global : Obj.globals()) { - WasmYAML::Global G; + WasmYAML::GlobalDecl G; G.Type = Global.Type; G.Mutable = Global.Mutable; G.InitExpr = Global.InitExpr; + G.Comdat = Global.Comdat; GlobalSec->Globals.push_back(G); } S = std::move(GlobalSec); @@ -233,6 +236,7 @@ Function.Locals.push_back(LocalDecl); } Function.Body = yaml::BinaryRef(Func.Body); + Function.Comdat = Func.Comdat; CodeSec->Functions.push_back(Function); } S = std::move(CodeSec); @@ -246,6 +250,7 @@ Seg.MemoryIndex = Segment.Data.MemoryIndex; Seg.Offset = Segment.Data.Offset; Seg.Content = yaml::BinaryRef(Segment.Data.Content); + Seg.Comdat = Segment.Data.Comdat; DataSec->Segments.push_back(Seg); } S = std::move(DataSec);