[WebAssembly] Implement @llvm.global_ctors and @llvm.global_dtors

Summary:
- lowers @llvm.global_dtors by adding @llvm.global_ctors
  functions which register the destructors with `__cxa_atexit`.
- impements @llvm.global_ctors with wasm start functions and linker metadata

See [here](https://github.com/WebAssembly/tool-conventions/issues/25) for more background.

Subscribers: jfb, dschuff, mgorny, jgravelle-google, aheejin, sunfish

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

llvm-svn: 320774
diff --git a/llvm/lib/MC/WasmObjectWriter.cpp b/llvm/lib/MC/WasmObjectWriter.cpp
index b2c5b14..6e76c5f 100644
--- a/llvm/lib/MC/WasmObjectWriter.cpp
+++ b/llvm/lib/MC/WasmObjectWriter.cpp
@@ -284,7 +284,8 @@
   void writeDataRelocSection();
   void writeLinkingMetaDataSection(
       ArrayRef<WasmDataSegment> Segments, uint32_t DataSize,
-      SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags);
+      const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags,
+      const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs);
 
   uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry);
   void applyRelocations(ArrayRef<WasmRelocationEntry> Relocations,
@@ -366,6 +367,10 @@
   uint64_t FixupOffset = Layout.getFragmentOffset(Fragment) + Fixup.getOffset();
   MCContext &Ctx = Asm.getContext();
 
+  // The .init_array isn't translated as data, so don't do relocations in it.
+  if (FixupSection.getSectionName().startswith(".init_array"))
+    return;
+
   if (const MCSymbolRefExpr *RefB = Target.getSymB()) {
     assert(RefB->getKind() == MCSymbolRefExpr::VK_None &&
            "Should not have constructed this");
@@ -905,7 +910,8 @@
 
 void WasmObjectWriter::writeLinkingMetaDataSection(
     ArrayRef<WasmDataSegment> Segments, uint32_t DataSize,
-    SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags) {
+    const SmallVector<std::pair<StringRef, uint32_t>, 4> &SymbolFlags,
+    const SmallVector<std::pair<uint16_t, uint32_t>, 2> &InitFuncs) {
   SectionBookkeeping Section;
   startSection(Section, wasm::WASM_SEC_CUSTOM, "linking");
   SectionBookkeeping SubSection;
@@ -937,6 +943,16 @@
     endSection(SubSection);
   }
 
+  if (!InitFuncs.empty()) {
+    startSection(SubSection, wasm::WASM_INIT_FUNCS);
+    encodeULEB128(InitFuncs.size(), getStream());
+    for (auto &StartFunc : InitFuncs) {
+      encodeULEB128(StartFunc.first, getStream()); // priority
+      encodeULEB128(StartFunc.second, getStream()); // function index
+    }
+    endSection(SubSection);
+  }
+
   endSection(Section);
 }
 
@@ -977,6 +993,7 @@
   SmallVector<WasmImport, 4> Imports;
   SmallVector<WasmExport, 4> Exports;
   SmallVector<std::pair<StringRef, uint32_t>, 4> SymbolFlags;
+  SmallVector<std::pair<uint16_t, uint32_t>, 2> InitFuncs;
   SmallPtrSet<const MCSymbolWasm *, 4> IsAddressTaken;
   unsigned NumFuncImports = 0;
   SmallVector<WasmDataSegment, 4> DataSegments;
@@ -1132,6 +1149,10 @@
     if (!Section.isWasmData())
       continue;
 
+    // .init_array sections are handled specially elsewhere.
+    if (cast<MCSectionWasm>(Sec).getSectionName().startswith(".init_array"))
+      continue;
+
     DataSize = alignTo(DataSize, Section.getAlignment());
     DataSegments.emplace_back();
     WasmDataSegment &Segment = DataSegments.back();
@@ -1291,6 +1312,56 @@
     registerFunctionType(*Fixup.Symbol);
   }
 
+  // Translate .init_array section contents into start functions.
+  for (const MCSection &S : Asm) {
+    const auto &WS = static_cast<const MCSectionWasm &>(S);
+    if (WS.getSectionName().startswith(".fini_array"))
+      report_fatal_error(".fini_array sections are unsupported");
+    if (!WS.getSectionName().startswith(".init_array"))
+      continue;
+    if (WS.getFragmentList().empty())
+      continue;
+    if (WS.getFragmentList().size() != 2)
+      report_fatal_error("only one .init_array section fragment supported");
+    const MCFragment &AlignFrag = *WS.begin();
+    if (AlignFrag.getKind() != MCFragment::FT_Align)
+      report_fatal_error(".init_array section should be aligned");
+    if (cast<MCAlignFragment>(AlignFrag).getAlignment() != (is64Bit() ? 8 : 4))
+      report_fatal_error(".init_array section should be aligned for pointers");
+    const MCFragment &Frag = *std::next(WS.begin());
+    if (Frag.hasInstructions() || Frag.getKind() != MCFragment::FT_Data)
+      report_fatal_error("only data supported in .init_array section");
+    uint16_t Priority = UINT16_MAX;
+    if (WS.getSectionName().size() != 11) {
+      if (WS.getSectionName()[11] != '.')
+        report_fatal_error(".init_array section priority should start with '.'");
+      if (WS.getSectionName().substr(12).getAsInteger(10, Priority))
+        report_fatal_error("invalid .init_array section priority");
+    }
+    const auto &DataFrag = cast<MCDataFragment>(Frag);
+    const SmallVectorImpl<char> &Contents = DataFrag.getContents();
+    for (const uint8_t *p = (const uint8_t *)Contents.data(),
+                     *end = (const uint8_t *)Contents.data() + Contents.size();
+         p != end; ++p) {
+      if (*p != 0)
+        report_fatal_error("non-symbolic data in .init_array section");
+    }
+    for (const MCFixup &Fixup : DataFrag.getFixups()) {
+      assert(Fixup.getKind() == MCFixup::getKindForSize(is64Bit() ? 8 : 4, false));
+      const MCExpr *Expr = Fixup.getValue();
+      auto *Sym = dyn_cast<MCSymbolRefExpr>(Expr);
+      if (!Sym)
+        report_fatal_error("fixups in .init_array should be symbol references");
+      if (Sym->getKind() != MCSymbolRefExpr::VK_WebAssembly_FUNCTION)
+        report_fatal_error("symbols in .init_array should be for functions");
+      auto I = SymbolIndices.find(cast<MCSymbolWasm>(&Sym->getSymbol()));
+      if (I == SymbolIndices.end())
+        report_fatal_error("symbols in .init_array should be defined");
+      uint32_t Index = I->second;
+      InitFuncs.push_back(std::make_pair(Priority, Index));
+    }
+  }
+
   // Write out the Wasm header.
   writeHeader(Asm);
 
@@ -1301,14 +1372,14 @@
   // Skip the "memory" section; we import the memory instead.
   writeGlobalSection();
   writeExportSection(Exports);
-  // TODO: Start Section
   writeElemSection(TableElems);
   writeCodeSection(Asm, Layout, Functions);
   writeDataSection(DataSegments);
   writeNameSection(Functions, Imports, NumFuncImports);
   writeCodeRelocSection();
   writeDataRelocSection();
-  writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags);
+  writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags,
+                              InitFuncs);
 
   // TODO: Translate the .comment section to the output.
   // TODO: Translate debug sections to the output.