[WebAssembly] Target features section

Summary:
Implements a new target features section in assembly and object files
that records what features are used, required, and disallowed in
WebAssembly objects. The linker uses this information to ensure that
all objects participating in a link are feature-compatible and records
the set of used features in the output binary for use by optimizers
and other tools later in the toolchain.

The "atomics" feature is always required or disallowed to prevent
linking code with stripped atomics into multithreaded binaries. Other
features are marked used if they are enabled globally or on any
function in a module.

Future CLs will add linker flags for ignoring feature compatibility
checks and for specifying the set of allowed features, implement using
the presence of the "atomics" feature to control the type of memory
and segments in the linked binary, and add front-end flags for
relaxing the linkage policy for atomics.

Reviewers: aheejin, sbc100, dschuff

Subscribers: jgravelle-google, hiraditya, sunfish, mgrang, jfb, jdoerfert, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D59173

llvm-svn: 356610
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 96c4736..897908a 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/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<std::vector<InputSection *>> CustomSectionMapping;
   llvm::StringMap<SectionSymbol *> CustomSectionSymbols;
+  llvm::SmallSet<std::string, 8> 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<std::string, 8> 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<std::string, 8> Required;
+  SmallSet<std::string, 8> 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<std::string, 8> 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");