Index: lld/trunk/test/wasm/Inputs/disallow-feature-foo.yaml =================================================================== --- lld/trunk/test/wasm/Inputs/disallow-feature-foo.yaml +++ lld/trunk/test/wasm/Inputs/disallow-feature-foo.yaml @@ -0,0 +1,13 @@ +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: + - Prefix: DISALLOWED + Name: "foo" +... Index: lld/trunk/test/wasm/Inputs/no-feature-foo.yaml =================================================================== --- lld/trunk/test/wasm/Inputs/no-feature-foo.yaml +++ lld/trunk/test/wasm/Inputs/no-feature-foo.yaml @@ -0,0 +1,11 @@ +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: [ ] +... Index: lld/trunk/test/wasm/Inputs/require-feature-foo.yaml =================================================================== --- lld/trunk/test/wasm/Inputs/require-feature-foo.yaml +++ lld/trunk/test/wasm/Inputs/require-feature-foo.yaml @@ -0,0 +1,13 @@ +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: + - Prefix: REQUIRED + Name: "foo" +... Index: lld/trunk/test/wasm/Inputs/use-feature-foo.yaml =================================================================== --- lld/trunk/test/wasm/Inputs/use-feature-foo.yaml +++ lld/trunk/test/wasm/Inputs/use-feature-foo.yaml @@ -0,0 +1,13 @@ +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: + - Prefix: USED + Name: "foo" +... Index: lld/trunk/test/wasm/target-feature-disallowed.yaml =================================================================== --- lld/trunk/test/wasm/target-feature-disallowed.yaml +++ lld/trunk/test/wasm/target-feature-disallowed.yaml @@ -0,0 +1,44 @@ +# RUN: yaml2obj %s -o %t1.o + +# RUN: yaml2obj %S/Inputs/disallow-feature-foo.yaml -o %t.disallowed.o +# RUN: wasm-ld --no-entry -o - %t1.o %t.disallowed.o | obj2yaml | FileCheck %s --check-prefix DISALLOWED + +# RUN: yaml2obj %S/Inputs/no-feature-foo.yaml -o %t.none.o +# RUN: wasm-ld --no-entry -o - %t1.o %t.none.o | obj2yaml | FileCheck %s --check-prefix NONE + +# Check that the following combinations of feature linkage policies +# give the expected results: +# +# DISALLOWED x DISALLOWED => NONE +# DISALLOWED x NONE => NONE + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: + - Prefix: DISALLOWED + Name: "foo" + # included so output has target features section + - Prefix: USED + Name: "bar" +... + +# DISALLOWED: - Type: CUSTOM +# DISALLOWED-NEXT: Name: target_features +# DISALLOWED-NEXT: Features: +# DISALLOWED-NEXT: - Prefix: USED +# DISALLOWED-NEXT: Name: bar +# DISALLOWED-NEXT: ... + +# NONE: - Type: CUSTOM +# NONE-NEXT: Name: target_features +# NONE-NEXT: Features: +# NONE-NEXT: - Prefix: USED +# NONE-NEXT: Name: bar +# NONE-NEXT: ... Index: lld/trunk/test/wasm/target-feature-required.yaml =================================================================== --- lld/trunk/test/wasm/target-feature-required.yaml +++ lld/trunk/test/wasm/target-feature-required.yaml @@ -0,0 +1,42 @@ +# RUN: yaml2obj %s -o %t1.o + +# RUN: yaml2obj %S/Inputs/require-feature-foo.yaml -o %t.required.o +# RUN: wasm-ld --no-entry -o - %t1.o %t.required.o | obj2yaml | FileCheck %s --check-prefix REQUIRED + +# RUN: yaml2obj %S/Inputs/disallow-feature-foo.yaml -o %t.disallowed.o +# RUN: not wasm-ld --no-entry -o - %t1.o %t.disallowed.o 2>&1 | FileCheck %s --check-prefix DISALLOWED + +# RUN: yaml2obj %S/Inputs/no-feature-foo.yaml -o %t.none.o +# RUN: not wasm-ld --no-entry -o - %t1.o %t.none.o 2>&1 | FileCheck %s --check-prefix NONE + +# Check that the following combinations of feature linkage policies +# give the expected results: +# +# REQUIRED x REQUIRED => USED +# REQUIRED x DISALLOWED => Error +# REQUIRED x NONE => Error + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: + - Prefix: REQUIRED + Name: "foo" +... + +# REQUIRED: - Type: CUSTOM +# REQUIRED-NEXT: Name: target_features +# REQUIRED-NEXT: Features: +# REQUIRED-NEXT: - Prefix: USED +# REQUIRED-NEXT: Name: foo +# REQUIRED-NEXT: ... + +# DISALLOWED: Target feature "foo" is disallowed + +# NONE: Missing required target feature "foo" Index: lld/trunk/test/wasm/target-feature-used.yaml =================================================================== --- lld/trunk/test/wasm/target-feature-used.yaml +++ lld/trunk/test/wasm/target-feature-used.yaml @@ -0,0 +1,58 @@ +# RUN: yaml2obj %s -o %t1.o + +# RUN: yaml2obj %S/Inputs/use-feature-foo.yaml -o %t.used.o +# RUN: wasm-ld --no-entry -o - %t1.o %t.used.o | obj2yaml | FileCheck %s --check-prefix USED + +# RUN: yaml2obj %S/Inputs/require-feature-foo.yaml -o %t.required.o +# RUN: wasm-ld --no-entry -o - %t1.o %t.required.o | obj2yaml | FileCheck %s --check-prefix REQUIRED + +# RUN: yaml2obj %S/Inputs/disallow-feature-foo.yaml -o %t.disallowed.o +# RUN: not wasm-ld --no-entry -o - %t1.o %t.disallowed.o 2>&1 | FileCheck %s --check-prefix DISALLOWED + +# RUN: yaml2obj %S/Inputs/no-feature-foo.yaml -o %t.none.o +# RUN: wasm-ld --no-entry -o - %t1.o %t.none.o | obj2yaml | FileCheck %s --check-prefix NONE + +# Check that the following combinations of feature linkage policies +# give the expected results: +# +# USED x USED => USED +# USED x REQUIRED => USED +# USED x DISALLOWED => Error +# USED x NONE => USED + +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: linking + Version: 2 + - Type: CUSTOM + Name: target_features + Features: + - Prefix: USED + Name: "foo" +... + +# USED: - Type: CUSTOM +# USED-NEXT: Name: target_features +# USED-NEXT: Features: +# USED-NEXT: - Prefix: USED +# USED-NEXT: Name: foo +# USED-NEXT: ... + +# REQUIRED: - Type: CUSTOM +# REQUIRED-NEXT: Name: target_features +# REQUIRED-NEXT: Features: +# REQUIRED-NEXT: - Prefix: USED +# REQUIRED-NEXT: Name: foo +# REQUIRED-NEXT: ... + +# DISALLOWED: Target feature "foo" is disallowed + +# NONE: - Type: CUSTOM +# NONE-NEXT: Name: target_features +# NONE-NEXT: Features: +# NONE-NEXT: - Prefix: USED +# NONE-NEXT: Name: foo +# NONE-NEXT: ... Index: lld/trunk/wasm/InputFiles.h =================================================================== --- lld/trunk/wasm/InputFiles.h +++ lld/trunk/wasm/InputFiles.h @@ -99,7 +99,6 @@ const WasmSection *CodeSection = nullptr; const WasmSection *DataSection = nullptr; - const WasmSection *ProducersSection = nullptr; // Maps input type indices to output type indices std::vector TypeMap; Index: lld/trunk/wasm/InputFiles.cpp =================================================================== --- lld/trunk/wasm/InputFiles.cpp +++ lld/trunk/wasm/InputFiles.cpp @@ -244,8 +244,6 @@ CustomSections.emplace_back(make(Section, this)); CustomSections.back()->setRelocations(Section.Relocations); CustomSectionsByIndex[SectionIndex] = CustomSections.back(); - if (Section.Name == "producers") - ProducersSection = &Section; } SectionIndex++; } Index: lld/trunk/wasm/Writer.cpp =================================================================== --- lld/trunk/wasm/Writer.cpp +++ lld/trunk/wasm/Writer.cpp @@ -20,6 +20,7 @@ #include "lld/Common/Strings.h" #include "lld/Common/Threads.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/BinaryFormat/Wasm.h" @@ -68,6 +69,7 @@ void calculateInitFunctions(); void processRelocations(InputChunk *Chunk); void assignIndexes(); + void calculateTargetFeatures(); void calculateImports(); void calculateExports(); void calculateCustomSections(); @@ -99,6 +101,7 @@ void createLinkingSection(); void createNameSection(); void createProducersSection(); + void createTargetFeaturesSection(); void writeHeader(); void writeSections(); @@ -129,6 +132,7 @@ llvm::StringMap> CustomSectionMapping; llvm::StringMap CustomSectionSymbols; + llvm::SmallSet TargetFeatures; // Elements that are used to construct the final output std::string Header; @@ -344,7 +348,7 @@ // These custom sections are known the linker and synthesized rather than // blindly copied if (Name == "linking" || Name == "name" || Name == "producers" || - Name.startswith("reloc.")) + Name == "target_features" || Name.startswith("reloc.")) continue; // .. or it is a debug section if (StripDebug && Name.startswith(".debug_")) @@ -701,6 +705,23 @@ } } +void Writer::createTargetFeaturesSection() { + if (TargetFeatures.size() == 0) + return; + + SmallVector Emitted(TargetFeatures.begin(), + TargetFeatures.end()); + std::sort(Emitted.begin(), Emitted.end()); + SyntheticSection *Section = + createSyntheticSection(WASM_SEC_CUSTOM, "target_features"); + auto &OS = Section->getStream(); + writeUleb128(OS, Emitted.size(), "feature count"); + for (auto &Feature : Emitted) { + writeU8(OS, WASM_FEATURE_PREFIX_USED, "feature used prefix"); + writeStr(OS, Feature, "feature name"); + } +} + void Writer::writeHeader() { memcpy(Buffer->getBufferStart(), Header.data(), Header.size()); } @@ -844,8 +865,10 @@ if (!Config->StripDebug && !Config->StripAll) createNameSection(); - if (!Config->StripAll) + if (!Config->StripAll) { createProducersSection(); + createTargetFeaturesSection(); + } for (OutputSection *S : OutputSections) { S->setOffset(FileSize); @@ -854,6 +877,48 @@ } } +void Writer::calculateTargetFeatures() { + SmallSet Required; + SmallSet Disallowed; + + // Find the sets of used, required, and disallowed features + for (ObjFile *File : Symtab->ObjectFiles) { + for (auto &Feature : File->getWasmObj()->getTargetFeatures()) { + switch (Feature.Prefix) { + case WASM_FEATURE_PREFIX_USED: + TargetFeatures.insert(Feature.Name); + break; + case WASM_FEATURE_PREFIX_REQUIRED: + TargetFeatures.insert(Feature.Name); + Required.insert(Feature.Name); + break; + case WASM_FEATURE_PREFIX_DISALLOWED: + Disallowed.insert(Feature.Name); + break; + default: + error("Unrecognized feature policy prefix " + + std::to_string(Feature.Prefix)); + } + } + } + + // Validate the required and disallowed constraints for each file + for (ObjFile *File : Symtab->ObjectFiles) { + SmallSet ObjectFeatures; + for (auto &Feature : File->getWasmObj()->getTargetFeatures()) { + if (Feature.Prefix == WASM_FEATURE_PREFIX_DISALLOWED) + continue; + ObjectFeatures.insert(Feature.Name); + if (Disallowed.count(Feature.Name)) + error("Target feature \"" + Feature.Name + "\" is disallowed"); + } + for (auto &Feature : Required) { + if (!ObjectFeatures.count(Feature)) + error(Twine("Missing required target feature \"") + Feature + "\""); + } + } +} + void Writer::calculateImports() { for (Symbol *Sym : Symtab->getSymbols()) { if (!Sym->isUndefined()) @@ -1225,6 +1290,8 @@ if (!Config->Pic) TableBase = 1; + log("-- calculateTargetFeatures"); + calculateTargetFeatures(); log("-- calculateImports"); calculateImports(); log("-- assignIndexes"); Index: llvm/trunk/include/llvm/BinaryFormat/Wasm.h =================================================================== --- llvm/trunk/include/llvm/BinaryFormat/Wasm.h +++ llvm/trunk/include/llvm/BinaryFormat/Wasm.h @@ -48,6 +48,11 @@ std::vector> SDKs; }; +struct WasmFeatureEntry { + uint8_t Prefix; + std::string Name; +}; + struct WasmExport { StringRef Name; uint8_t Kind; @@ -253,6 +258,13 @@ WASM_SEGMENT_HAS_MEMINDEX = 0x02, }; +// Feature policy prefixes used in the custom "target_features" section +enum : uint8_t { + WASM_FEATURE_PREFIX_USED = '+', + WASM_FEATURE_PREFIX_REQUIRED = '=', + WASM_FEATURE_PREFIX_DISALLOWED = '-', +}; + // Kind codes used in the custom "name" section enum : unsigned { WASM_NAMES_FUNCTION = 0x1, Index: llvm/trunk/include/llvm/Object/Wasm.h =================================================================== --- llvm/trunk/include/llvm/Object/Wasm.h +++ llvm/trunk/include/llvm/Object/Wasm.h @@ -130,6 +130,9 @@ const wasm::WasmDylinkInfo &dylinkInfo() const { return DylinkInfo; } const wasm::WasmProducerInfo &getProducerInfo() const { return ProducerInfo; } + ArrayRef getTargetFeatures() const { + return TargetFeatures; + } ArrayRef types() const { return Signatures; } ArrayRef functionTypes() const { return FunctionTypes; } ArrayRef imports() const { return Imports; } @@ -252,12 +255,14 @@ Error parseLinkingSectionSymtab(ReadContext &Ctx); Error parseLinkingSectionComdat(ReadContext &Ctx); Error parseProducersSection(ReadContext &Ctx); + Error parseTargetFeaturesSection(ReadContext &Ctx); Error parseRelocSection(StringRef Name, ReadContext &Ctx); wasm::WasmObjectHeader Header; std::vector Sections; wasm::WasmDylinkInfo DylinkInfo; wasm::WasmProducerInfo ProducerInfo; + std::vector TargetFeatures; std::vector Signatures; std::vector FunctionTypes; std::vector Tables; @@ -318,6 +323,8 @@ WASM_SEC_ORDER_NAME, // "producers" section must appear after "name" section. WASM_SEC_ORDER_PRODUCERS, + // "target_features" section must appear after producers section + WASM_SEC_ORDER_TARGET_FEATURES, // Must be last WASM_NUM_SEC_ORDERS Index: llvm/trunk/include/llvm/ObjectYAML/WasmYAML.h =================================================================== --- llvm/trunk/include/llvm/ObjectYAML/WasmYAML.h +++ llvm/trunk/include/llvm/ObjectYAML/WasmYAML.h @@ -38,6 +38,7 @@ LLVM_YAML_STRONG_TYPEDEF(uint32_t, SegmentFlags) LLVM_YAML_STRONG_TYPEDEF(uint32_t, LimitFlags) LLVM_YAML_STRONG_TYPEDEF(uint32_t, ComdatKind) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, FeaturePolicyPrefix) struct FileHeader { yaml::Hex32 Version; @@ -128,6 +129,11 @@ std::string Version; }; +struct FeatureEntry { + FeaturePolicyPrefix Prefix; + std::string Name; +}; + struct SegmentInfo { uint32_t Index; StringRef Name; @@ -242,6 +248,17 @@ std::vector SDKs; }; +struct TargetFeaturesSection : CustomSection { + TargetFeaturesSection() : CustomSection("target_features") {} + + static bool classof(const Section *S) { + auto C = dyn_cast(S); + return C && C->Name == "target_features"; + } + + std::vector Features; +}; + struct TypeSection : Section { TypeSection() : Section(wasm::WASM_SEC_TYPE) {} @@ -385,6 +402,7 @@ 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::FeatureEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::SegmentInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::SymbolInfo) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::WasmYAML::InitFunction) @@ -467,6 +485,14 @@ static void mapping(IO &IO, WasmYAML::ProducerEntry &ProducerEntry); }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, WasmYAML::FeaturePolicyPrefix &Prefix); +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, WasmYAML::FeatureEntry &FeatureEntry); +}; + template <> struct MappingTraits { static void mapping(IO &IO, WasmYAML::SegmentInfo &SegmentInfo); }; Index: llvm/trunk/lib/MC/MCParser/WasmAsmParser.cpp =================================================================== --- llvm/trunk/lib/MC/MCParser/WasmAsmParser.cpp +++ llvm/trunk/lib/MC/MCParser/WasmAsmParser.cpp @@ -135,9 +135,10 @@ SectionKind Kind; }; static SectionType SectionTypes[] = { - { ".text", SectionKind::getText() }, - { ".rodata", SectionKind::getReadOnly() }, - { ".data", SectionKind::getData() }, + {".text", SectionKind::getText()}, + {".rodata", SectionKind::getReadOnly()}, + {".data", SectionKind::getData()}, + {".custom_section", SectionKind::getMetadata()}, // TODO: add more types. }; for (size_t I = 0; I < sizeof(SectionTypes) / sizeof(SectionType); I++) { Index: llvm/trunk/lib/MC/WasmObjectWriter.cpp =================================================================== --- llvm/trunk/lib/MC/WasmObjectWriter.cpp +++ llvm/trunk/lib/MC/WasmObjectWriter.cpp @@ -250,6 +250,7 @@ // section. std::vector CustomSections; std::unique_ptr ProducersSection; + std::unique_ptr TargetFeaturesSection; // Relocations for fixing up references in the custom sections. DenseMap> CustomSectionsRelocations; @@ -291,6 +292,7 @@ DataLocations.clear(); CustomSections.clear(); ProducersSection.reset(); + TargetFeaturesSection.reset(); CustomSectionsRelocations.clear(); SignatureIndices.clear(); Signatures.clear(); @@ -1286,11 +1288,16 @@ Twine(SectionName)); } - // Separate out the producers section + // Separate out the producers and target features sections if (Name == "producers") { ProducersSection = llvm::make_unique(Name, &Section); continue; } + if (Name == "target_features") { + TargetFeaturesSection = + llvm::make_unique(Name, &Section); + continue; + } CustomSections.emplace_back(Name, &Section); } @@ -1593,6 +1600,8 @@ writeCustomRelocSections(); if (ProducersSection) writeCustomSection(*ProducersSection, Asm, Layout); + if (TargetFeaturesSection) + writeCustomSection(*TargetFeaturesSection, Asm, Layout); // TODO: Translate the .comment section to the output. return W.OS.tell() - StartOffset; Index: llvm/trunk/lib/Object/WasmObjectFile.cpp =================================================================== --- llvm/trunk/lib/Object/WasmObjectFile.cpp +++ llvm/trunk/lib/Object/WasmObjectFile.cpp @@ -715,6 +715,36 @@ return Error::success(); } +Error WasmObjectFile::parseTargetFeaturesSection(ReadContext &Ctx) { + llvm::SmallSet FeaturesSeen; + uint32_t FeatureCount = readVaruint32(Ctx); + for (size_t I = 0; I < FeatureCount; ++I) { + wasm::WasmFeatureEntry Feature; + Feature.Prefix = readUint8(Ctx); + switch (Feature.Prefix) { + case wasm::WASM_FEATURE_PREFIX_USED: + case wasm::WASM_FEATURE_PREFIX_REQUIRED: + case wasm::WASM_FEATURE_PREFIX_DISALLOWED: + break; + default: + return make_error("Unknown feature policy prefix", + object_error::parse_failed); + } + Feature.Name = readString(Ctx); + if (!FeaturesSeen.insert(Feature.Name).second) + return make_error( + "Target features section contains repeated feature \"" + + Feature.Name + "\"", + object_error::parse_failed); + TargetFeatures.push_back(Feature); + } + if (Ctx.Ptr != Ctx.End) + return make_error( + "Target features 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()) @@ -816,6 +846,9 @@ } else if (Sec.Name == "producers") { if (Error Err = parseProducersSection(Ctx)) return Err; + } else if (Sec.Name == "target_features") { + if (Error Err = parseTargetFeaturesSection(Ctx)) + return Err; } else if (Sec.Name.startswith("reloc.")) { if (Error Err = parseRelocSection(Sec.Name, Ctx)) return Err; @@ -1528,6 +1561,7 @@ .StartsWith("reloc.", WASM_SEC_ORDER_RELOC) .Case("name", WASM_SEC_ORDER_NAME) .Case("producers", WASM_SEC_ORDER_PRODUCERS) + .Case("target_features", WASM_SEC_ORDER_TARGET_FEATURES) .Default(WASM_SEC_ORDER_NONE); case wasm::WASM_SEC_TYPE: return WASM_SEC_ORDER_TYPE; @@ -1584,7 +1618,8 @@ {WASM_SEC_ORDER_LINKING, WASM_SEC_ORDER_RELOC, WASM_SEC_ORDER_NAME}, // WASM_SEC_ORDER_LINKING, {}, // WASM_SEC_ORDER_RELOC (can be repeated), {WASM_SEC_ORDER_NAME, WASM_SEC_ORDER_PRODUCERS}, // WASM_SEC_ORDER_NAME, - {WASM_SEC_ORDER_PRODUCERS}, // WASM_SEC_ORDER_PRODUCERS, + {WASM_SEC_ORDER_PRODUCERS, WASM_SEC_ORDER_TARGET_FEATURES}, // WASM_SEC_ORDER_PRODUCERS, + {WASM_SEC_ORDER_TARGET_FEATURES} // WASM_SEC_ORDER_TARGET_FEATURES }; bool WasmSectionOrderChecker::isValidSectionOrder(unsigned ID, Index: llvm/trunk/lib/ObjectYAML/WasmYAML.cpp =================================================================== --- llvm/trunk/lib/ObjectYAML/WasmYAML.cpp +++ llvm/trunk/lib/ObjectYAML/WasmYAML.cpp @@ -81,6 +81,12 @@ IO.mapOptional("SDKs", Section.SDKs); } +static void sectionMapping(IO &IO, WasmYAML::TargetFeaturesSection &Section) { + commonSectionMapping(IO, Section); + IO.mapRequired("Name", Section.Name); + IO.mapRequired("Features", Section.Features); +} + static void sectionMapping(IO &IO, WasmYAML::CustomSection &Section) { commonSectionMapping(IO, Section); IO.mapRequired("Name", Section.Name); @@ -180,6 +186,10 @@ if (!IO.outputting()) Section.reset(new WasmYAML::ProducersSection()); sectionMapping(IO, *cast(Section.get())); + } else if (SectionName == "target_features") { + if (!IO.outputting()) + Section.reset(new WasmYAML::TargetFeaturesSection()); + sectionMapping(IO, *cast(Section.get())); } else { if (!IO.outputting()) Section.reset(new WasmYAML::CustomSection(SectionName)); @@ -310,6 +320,21 @@ IO.mapRequired("Version", ProducerEntry.Version); } +void ScalarEnumerationTraits::enumeration( + IO &IO, WasmYAML::FeaturePolicyPrefix &Kind) { +#define ECase(X) IO.enumCase(Kind, #X, wasm::WASM_FEATURE_PREFIX_##X); + ECase(USED); + ECase(REQUIRED); + ECase(DISALLOWED); +#undef ECase +} + +void MappingTraits::mapping( + IO &IO, WasmYAML::FeatureEntry &FeatureEntry) { + IO.mapRequired("Prefix", FeatureEntry.Prefix); + IO.mapRequired("Name", FeatureEntry.Name); +} + void MappingTraits::mapping( IO &IO, WasmYAML::SegmentInfo &SegmentInfo) { IO.mapRequired("Index", SegmentInfo.Index); Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -59,6 +59,7 @@ void EmitEndOfAsmFile(Module &M) override; void EmitProducerInfo(Module &M); + void EmitTargetFeatures(); void EmitJumpTableInfo() override; void EmitConstantPool() override; void EmitFunctionBodyStart() override; Index: llvm/trunk/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- llvm/trunk/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -21,8 +21,10 @@ #include "WebAssemblyMCInstLower.h" #include "WebAssemblyMachineFunctionInfo.h" #include "WebAssemblyRegisterInfo.h" +#include "WebAssemblyTargetMachine.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/BinaryFormat/Wasm.h" #include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/MachineConstantPool.h" @@ -159,6 +161,7 @@ } EmitProducerInfo(M); + EmitTargetFeatures(); } void WebAssemblyAsmPrinter::EmitProducerInfo(Module &M) { @@ -212,6 +215,61 @@ } } +void WebAssemblyAsmPrinter::EmitTargetFeatures() { + static const std::pair FeaturePairs[] = { + {WebAssembly::FeatureAtomics, "atomics"}, + {WebAssembly::FeatureBulkMemory, "bulk-memory"}, + {WebAssembly::FeatureExceptionHandling, "exception-handling"}, + {WebAssembly::FeatureNontrappingFPToInt, "nontrapping-fptoint"}, + {WebAssembly::FeatureSignExt, "sign-ext"}, + {WebAssembly::FeatureSIMD128, "simd128"}, + }; + + struct FeatureEntry { + uint8_t Prefix; + StringRef Name; + }; + + FeatureBitset UsedFeatures = + static_cast(TM).getUsedFeatures(); + + // Calculate the features and linkage policies to emit + SmallVector EmittedFeatures; + for (auto &F : FeaturePairs) { + FeatureEntry Entry; + Entry.Name = F.second; + if (F.first == WebAssembly::FeatureAtomics) { + // "atomics" is special: code compiled without atomics may have had its + // atomics lowered to nonatomic operations. Such code would be dangerous + // to mix with proper atomics, so it is always Required or Disallowed. + Entry.Prefix = UsedFeatures[F.first] ? wasm::WASM_FEATURE_PREFIX_REQUIRED + : wasm::WASM_FEATURE_PREFIX_DISALLOWED; + EmittedFeatures.push_back(Entry); + } else { + // Other features are marked Used or not mentioned + if (UsedFeatures[F.first]) { + Entry.Prefix = wasm::WASM_FEATURE_PREFIX_USED; + EmittedFeatures.push_back(Entry); + } + } + } + + // Emit features and linkage policies into the "target_features" section + MCSectionWasm *FeaturesSection = OutContext.getWasmSection( + ".custom_section.target_features", SectionKind::getMetadata()); + OutStreamer->PushSection(); + OutStreamer->SwitchSection(FeaturesSection); + + OutStreamer->EmitULEB128IntValue(EmittedFeatures.size()); + for (auto &F : EmittedFeatures) { + OutStreamer->EmitIntValue(F.Prefix, 1); + OutStreamer->EmitULEB128IntValue(F.Name.size()); + OutStreamer->EmitBytes(F.Name); + } + + OutStreamer->PopSection(); +} + void WebAssemblyAsmPrinter::EmitConstantPool() { assert(MF->getConstantPool()->getConstants().empty() && "WebAssembly disables constant pools"); Index: llvm/trunk/test/CodeGen/WebAssembly/target-features.ll =================================================================== --- llvm/trunk/test/CodeGen/WebAssembly/target-features.ll +++ llvm/trunk/test/CodeGen/WebAssembly/target-features.ll @@ -0,0 +1,67 @@ +; RUN: llc < %s | FileCheck %s --check-prefixes CHECK,ATTRS +; RUN: llc < %s -mattr=+simd128 | FileCheck %s --check-prefixes CHECK,SIMD128 +; RUN; llc < %s -mattr=+atomics | FileCheck %s --check-prefixes CHECK,ATOMICS +; RUN: llc < %s -mcpu=bleeding-edge | FileCheck %s --check-prefixes CHECK,BLEEDING-EDGE + +; Test that codegen emits target features from the command line or +; function attributes correctly. + +target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +define void @foo() #0 { + ret void +} + +define void @bar() #1 { + ret void +} + +attributes #0 = { "target-features"="+sign-ext" } +attributes #1 = { "target-features"="+nontrapping-fptoint" } + +; CHECK-LABEL: .custom_section.target_features,"",@ + +; -atomics, +sign_ext +; ATTRS-NEXT: .int8 3 +; ATTRS-NEXT: .int8 45 +; ATTRS-NEXT: .int8 7 +; ATTRS-NEXT: .ascii "atomics" +; ATTRS-NEXT: .int8 43 +; ATTRS-NEXT: .int8 19 +; ATTRS-NEXT: .ascii "nontrapping-fptoint" +; ATTRS-NEXT: .int8 43 +; ATTRS-NEXT: int8 8 +; ATTRS-NEXT: .ascii "sign-ext" + +; -atomics, +simd128 +; SIMD128-NEXT: .int8 2 +; SIMD128-NEXT: .int8 45 +; SIMD128-NEXT: .int8 7 +; SIMD128-NEXT: .ascii "atomics" +; SIMD128-NEXT: .int8 43 +; SIMD128-NEXT: .int8 7 +; SIMD128-NEXT: .ascii "simd128" + +; =atomics +; ATOMICS-NEXT: .int8 1 +; ATOMICS-NEXT: .int8 61 +; ATOMICS-NEXT: .int8 7 +; ATOMICS-NEXT: .ascii "atomics" + +; =atomics, +nontrapping-fptoint, +sign-ext, +simd128 +; BLEEDING-EDGE-NEXT: .int8 4 +; BLEEDING-EDGE-NEXT: .int8 61 +; BLEEDING-EDGE-NEXT: .int8 7 +; BLEEDING-EDGE-NEXT: .ascii "atomics" +; BLEEDING-EDGE-NEXT: .int8 43 +; BLEEDING-EDGE-NEXT: .int8 19 +; BLEEDING-EDGE-NEXT: .ascii "nontrapping-fptoint" +; BLEEDING-EDGE-NEXT: .int8 43 +; BLEEDING-EDGE-NEXT: .int8 8 +; BLEEDING-EDGE-NEXT: .ascii "sign-ext" +; BLEEDING-EDGE-NEXT: .int8 43 +; BLEEDING-EDGE-NEXT: .int8 7 +; BLEEDING-EDGE-NEXT: .ascii "simd128" + +; CHECK-NEXT: .text Index: llvm/trunk/test/MC/WebAssembly/array-fill.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/array-fill.ll +++ llvm/trunk/test/MC/WebAssembly/array-fill.ll @@ -24,4 +24,9 @@ ; CHECK-NEXT: Name: .data ; CHECK-NEXT: Alignment: 0 ; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/MC/WebAssembly/assembler-binary.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/assembler-binary.ll +++ llvm/trunk/test/MC/WebAssembly/assembler-binary.ll @@ -87,4 +87,9 @@ ; CHECK-NEXT: Name: bar ; CHECK-NEXT: Flags: [ UNDEFINED ] ; CHECK-NEXT: Function: 0 +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/MC/WebAssembly/bss.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/bss.ll +++ llvm/trunk/test/MC/WebAssembly/bss.ll @@ -78,4 +78,9 @@ ; CHECK-NEXT: Name: .bss.bar ; CHECK-NEXT: Alignment: 0 ; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/MC/WebAssembly/comdat.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/comdat.ll +++ llvm/trunk/test/MC/WebAssembly/comdat.ll @@ -119,4 +119,9 @@ ; CHECK-NEXT: Index: 3 ; CHECK-NEXT: - Kind: DATA ; CHECK-NEXT: Index: 0 +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/MC/WebAssembly/debug-info.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/debug-info.ll +++ llvm/trunk/test/MC/WebAssembly/debug-info.ll @@ -130,6 +130,12 @@ ; CHECK-NEXT: Offset: 1021 ; CHECK-NEXT: Name: producers ; CHECK-NEXT: } +; CHECK-NEXT: Section { +; CHECK-NEXT: Type: CUSTOM (0x0) +; CHECK-NEXT: Size: 10 +; CHECK-NEXT: Offset: 1114 +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: } ; CHECK-NEXT:] ; CHECK-NEXT:Relocations [ ; CHECK-NEXT: Section (6) DATA { Index: llvm/trunk/test/MC/WebAssembly/explicit-sections.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/explicit-sections.ll +++ llvm/trunk/test/MC/WebAssembly/explicit-sections.ll @@ -70,4 +70,9 @@ ; CHECK-NEXT: Name: .sec2 ; CHECK-NEXT: Alignment: 3 ; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/MC/WebAssembly/global-ctor-dtor.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/global-ctor-dtor.ll +++ llvm/trunk/test/MC/WebAssembly/global-ctor-dtor.ll @@ -181,4 +181,9 @@ ; CHECK-NEXT: Symbol: 10 ; CHECK-NEXT: - Priority: 65535 ; CHECK-NEXT: Symbol: 7 +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/MC/WebAssembly/visibility.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/visibility.ll +++ llvm/trunk/test/MC/WebAssembly/visibility.ll @@ -25,4 +25,9 @@ ; CHECK-NEXT: Name: hiddenVis ; CHECK-NEXT: Flags: [ VISIBILITY_HIDDEN ] ; CHECK-NEXT: Function: 1 +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/MC/WebAssembly/weak-alias.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/weak-alias.ll +++ llvm/trunk/test/MC/WebAssembly/weak-alias.ll @@ -207,6 +207,11 @@ ; CHECK-NEXT: Name: .data.alias_address ; CHECK-NEXT: Alignment: 3 ; CHECK-NEXT: Flags: [ ] +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... ; CHECK-SYMS: SYMBOL TABLE: Index: llvm/trunk/test/MC/WebAssembly/weak.ll =================================================================== --- llvm/trunk/test/MC/WebAssembly/weak.ll +++ llvm/trunk/test/MC/WebAssembly/weak.ll @@ -30,4 +30,9 @@ ; CHECK-NEXT: Kind: DATA ; CHECK-NEXT: Name: weak_external_data ; CHECK-NEXT: Flags: [ BINDING_WEAK, UNDEFINED ] +; CHECK-NEXT: - Type: CUSTOM +; CHECK-NEXT: Name: target_features +; CHECK-NEXT: Features: +; CHECK-NEXT: - Prefix: DISALLOWED +; CHECK-NEXT: Name: atomics ; CHECK-NEXT: ... Index: llvm/trunk/test/ObjectYAML/wasm/target-features-section.yaml =================================================================== --- llvm/trunk/test/ObjectYAML/wasm/target-features-section.yaml +++ llvm/trunk/test/ObjectYAML/wasm/target-features-section.yaml @@ -0,0 +1,25 @@ +# RUN: yaml2obj %s | obj2yaml | FileCheck %s +--- !WASM +FileHeader: + Version: 0x00000001 +Sections: + - Type: CUSTOM + Name: target_features + Features: + - Prefix: USED + Name: "foo" + - Prefix: REQUIRED + Name: "bar" + - Prefix: DISALLOWED + Name: "" +... +# CHECK-LABEL: Sections: +# CHECK-NEXT: - Type: CUSTOM +# CHECK-NEXT: Name: target_features +# CHECK-NEXT: Features: +# CHECK-NEXT: - Prefix: USED +# CHECK-NEXT: Name: foo +# CHECK-NEXT: - Prefix: REQUIRED +# CHECK-NEXT: Name: bar +# CHECK-NEXT: - Prefix: DISALLOWED +# CHECK-NEXT: Name: '' Index: llvm/trunk/tools/obj2yaml/wasm2yaml.cpp =================================================================== --- llvm/trunk/tools/obj2yaml/wasm2yaml.cpp +++ llvm/trunk/tools/obj2yaml/wasm2yaml.cpp @@ -155,6 +155,16 @@ ProducersSec->SDKs.push_back(Producer); } CustomSec = std::move(ProducersSec); + } else if (WasmSec.Name == "target_features") { + std::unique_ptr TargetFeaturesSec = + make_unique(); + for (auto &E : Obj.getTargetFeatures()) { + WasmYAML::FeatureEntry Feature; + Feature.Prefix = E.Prefix; + Feature.Name = E.Name; + TargetFeaturesSec->Features.push_back(Feature); + } + CustomSec = std::move(TargetFeaturesSec); } else { CustomSec = make_unique(WasmSec.Name); } Index: llvm/trunk/tools/yaml2obj/yaml2wasm.cpp =================================================================== --- llvm/trunk/tools/yaml2obj/yaml2wasm.cpp +++ llvm/trunk/tools/yaml2obj/yaml2wasm.cpp @@ -49,6 +49,8 @@ int writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::ProducersSection &Section); + int writeSectionContent(raw_ostream &OS, + WasmYAML::TargetFeaturesSection &Section); WasmYAML::Object &Obj; uint32_t NumImportedFunctions = 0; uint32_t NumImportedGlobals = 0; @@ -280,6 +282,17 @@ } int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::TargetFeaturesSection &Section) { + writeStringRef(Section.Name, OS); + encodeULEB128(Section.Features.size(), OS); + for (auto &E : Section.Features) { + writeUint8(OS, E.Prefix); + writeStringRef(E.Name, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section) { if (auto S = dyn_cast(&Section)) { if (auto Err = writeSectionContent(OS, *S)) @@ -293,6 +306,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);