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

Sam Clegg via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 14 16:17:10 PST 2017


Author: sbc
Date: Thu Dec 14 16:17:10 2017
New Revision: 320774

URL: http://llvm.org/viewvc/llvm-project?rev=320774&view=rev
Log:
[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

Added:
    llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp   (with props)
    llvm/trunk/test/CodeGen/WebAssembly/lower-global-dtors.ll
Modified:
    llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
    llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
    llvm/trunk/lib/MC/WasmObjectWriter.cpp
    llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt
    llvm/trunk/lib/Target/WebAssembly/WebAssembly.h
    llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
    llvm/trunk/test/MC/WebAssembly/init-fini-array.ll

Modified: llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h?rev=320774&r1=320773&r2=320774&view=diff
==============================================================================
--- llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h (original)
+++ llvm/trunk/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h Thu Dec 14 16:17:10 2017
@@ -182,6 +182,10 @@ public:
                                            const Function &F) const override;
 
   void InitializeWasm();
+  MCSection *getStaticCtorSection(unsigned Priority,
+                                  const MCSymbol *KeySym) const override;
+  MCSection *getStaticDtorSection(unsigned Priority,
+                                  const MCSymbol *KeySym) const override;
 
   const MCExpr *lowerRelativeReference(const GlobalValue *LHS,
                                        const GlobalValue *RHS,

Modified: llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp?rev=320774&r1=320773&r2=320774&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp (original)
+++ llvm/trunk/lib/CodeGen/TargetLoweringObjectFileImpl.cpp Thu Dec 14 16:17:10 2017
@@ -1359,6 +1359,18 @@ const MCExpr *TargetLoweringObjectFileWa
 void TargetLoweringObjectFileWasm::InitializeWasm() {
   StaticCtorSection =
       getContext().getWasmSection(".init_array", SectionKind::getData());
-  StaticDtorSection =
-      getContext().getWasmSection(".fini_array", SectionKind::getData());
+}
+
+MCSection *TargetLoweringObjectFileWasm::getStaticCtorSection(
+    unsigned Priority, const MCSymbol *KeySym) const {
+  return Priority == UINT16_MAX ?
+         StaticCtorSection :
+         getContext().getWasmSection(".init_array." + utostr(Priority),
+                                     SectionKind::getData());
+}
+
+MCSection *TargetLoweringObjectFileWasm::getStaticDtorSection(
+    unsigned Priority, const MCSymbol *KeySym) const {
+  llvm_unreachable("@llvm.global_dtors should have been lowered already");
+  return nullptr;
 }

Modified: llvm/trunk/lib/MC/WasmObjectWriter.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/MC/WasmObjectWriter.cpp?rev=320774&r1=320773&r2=320774&view=diff
==============================================================================
--- llvm/trunk/lib/MC/WasmObjectWriter.cpp (original)
+++ llvm/trunk/lib/MC/WasmObjectWriter.cpp Thu Dec 14 16:17:10 2017
@@ -284,7 +284,8 @@ private:
   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 @@ void WasmObjectWriter::recordRelocation(
   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::writeDataRelocSec
 
 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 @@ void WasmObjectWriter::writeLinkingMetaD
     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 @@ void WasmObjectWriter::writeObject(MCAss
   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 @@ void WasmObjectWriter::writeObject(MCAss
     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 @@ void WasmObjectWriter::writeObject(MCAss
     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 @@ void WasmObjectWriter::writeObject(MCAss
   // 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.

Modified: llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt?rev=320774&r1=320773&r2=320774&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt (original)
+++ llvm/trunk/lib/Target/WebAssembly/CMakeLists.txt Thu Dec 14 16:17:10 2017
@@ -25,6 +25,7 @@ add_llvm_target(WebAssemblyCodeGen
   WebAssemblyInstrInfo.cpp
   WebAssemblyLowerBrUnless.cpp
   WebAssemblyLowerEmscriptenEHSjLj.cpp
+  WebAssemblyLowerGlobalDtors.cpp
   WebAssemblyMachineFunctionInfo.cpp
   WebAssemblyMCInstLower.cpp
   WebAssemblyOptimizeLiveIntervals.cpp

Modified: llvm/trunk/lib/Target/WebAssembly/WebAssembly.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssembly.h?rev=320774&r1=320773&r2=320774&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssembly.h (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssembly.h Thu Dec 14 16:17:10 2017
@@ -28,6 +28,7 @@ class FunctionPass;
 // LLVM IR passes.
 ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj);
 void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &);
+ModulePass *createWebAssemblyLowerGlobalDtors();
 ModulePass *createWebAssemblyFixFunctionBitcasts();
 FunctionPass *createWebAssemblyOptimizeReturned();
 

Added: llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp?rev=320774&view=auto
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp (added)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp Thu Dec 14 16:17:10 2017
@@ -0,0 +1,191 @@
+//===-- WebAssemblyLowerGlobalDtors.cpp - Lower @llvm.global_dtors --------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Lower @llvm.global_dtors.
+///
+/// WebAssembly doesn't have a builtin way to invoke static destructors.
+/// Implement @llvm.global_dtors by creating wrapper functions that are
+/// registered in @llvm.global_ctors and which contain a call to
+/// `__cxa_atexit` to register their destructor functions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "WebAssembly.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+#include "llvm/Pass.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-lower-global-dtors"
+
+namespace {
+class LowerGlobalDtors final : public ModulePass {
+  StringRef getPassName() const override {
+    return "WebAssembly Lower @llvm.global_dtors";
+  }
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesCFG();
+    ModulePass::getAnalysisUsage(AU);
+  }
+
+  bool runOnModule(Module &M) override;
+
+public:
+  static char ID;
+  LowerGlobalDtors() : ModulePass(ID) {}
+};
+} // End anonymous namespace
+
+char LowerGlobalDtors::ID = 0;
+ModulePass *llvm::createWebAssemblyLowerGlobalDtors() {
+  return new LowerGlobalDtors();
+}
+
+bool LowerGlobalDtors::runOnModule(Module &M) {
+  GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors");
+  if (!GV)
+    return false;
+
+  const ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer());
+  if (!InitList)
+    return false;
+
+  // Sanity-check @llvm.global_dtor's type.
+  StructType *ETy = dyn_cast<StructType>(InitList->getType()->getElementType());
+  if (!ETy || ETy->getNumElements() != 3 ||
+      !ETy->getTypeAtIndex(0U)->isIntegerTy() ||
+      !ETy->getTypeAtIndex(1U)->isPointerTy() ||
+      !ETy->getTypeAtIndex(2U)->isPointerTy())
+    return false; // Not (int, ptr, ptr).
+
+  // Collect the contents of @llvm.global_dtors, collated by priority and
+  // associated symbol.
+  std::map<uint16_t, MapVector<Constant *, std::vector<Constant *> > > DtorFuncs;
+  for (Value *O : InitList->operands()) {
+    ConstantStruct *CS = dyn_cast<ConstantStruct>(O);
+    if (!CS) continue; // Malformed.
+
+    ConstantInt *Priority = dyn_cast<ConstantInt>(CS->getOperand(0));
+    if (!Priority) continue; // Malformed.
+    uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX);
+
+    Constant *DtorFunc = CS->getOperand(1);
+    if (DtorFunc->isNullValue())
+      break;  // Found a null terminator, skip the rest.
+
+    Constant *Associated = CS->getOperand(2);
+    Associated = cast<Constant>(Associated->stripPointerCastsNoFollowAliases());
+
+    DtorFuncs[PriorityValue][Associated].push_back(DtorFunc);
+  }
+  if (DtorFuncs.empty())
+    return false;
+
+  // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d);
+  LLVMContext &C = M.getContext();
+  PointerType *VoidStar = Type::getInt8PtrTy(C);
+  Type *AtExitFuncArgs[] = { VoidStar };
+  FunctionType *AtExitFuncTy = FunctionType::get(
+          Type::getVoidTy(C),
+          AtExitFuncArgs,
+          /*isVarArg=*/false);
+
+  Type *AtExitArgs[] = {
+    PointerType::get(AtExitFuncTy, 0),
+    VoidStar,
+    VoidStar
+  };
+  FunctionType *AtExitTy = FunctionType::get(
+          Type::getInt32Ty(C),
+          AtExitArgs,
+          /*isVarArg=*/false);
+  Constant *AtExit = M.getOrInsertFunction("__cxa_atexit", AtExitTy);
+
+  // Declare __dso_local.
+  Constant *DsoHandle = M.getNamedValue("__dso_handle");
+  if (!DsoHandle) {
+    Type *DsoHandleTy = Type::getInt8Ty(C);
+    GlobalVariable *Handle =
+        new GlobalVariable(M, DsoHandleTy, /*isConstant=*/true,
+                           GlobalVariable::ExternalWeakLinkage,
+                           nullptr, "__dso_handle");
+    Handle->setVisibility(GlobalVariable::HiddenVisibility);
+    DsoHandle = Handle;
+  }
+
+  // For each unique priority level and associated symbol, generate a function
+  // to call all the destructors at that level, and a function to register the
+  // first function with __cxa_atexit.
+  for (auto &PriorityAndMore : DtorFuncs) {
+    uint16_t Priority = PriorityAndMore.first;
+    for (auto &AssociatedAndMore : PriorityAndMore.second) {
+      Constant *Associated = AssociatedAndMore.first;
+
+      Function *CallDtors = Function::Create(
+              AtExitFuncTy, Function::PrivateLinkage,
+              "call_dtors" +
+              (Priority != UINT16_MAX ?
+                 (Twine(".") + Twine(Priority)) : Twine()) +
+              (!Associated->isNullValue() ?
+                 (Twine(".") + Associated->getName()) : Twine()),
+              &M);
+      BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors);
+
+      for (auto Dtor : AssociatedAndMore.second)
+        CallInst::Create(Dtor, "", BB);
+      ReturnInst::Create(C, BB);
+
+      FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C),
+                                                 /*isVarArg=*/false);
+      Function *RegisterCallDtors = Function::Create(
+              VoidVoid, Function::PrivateLinkage,
+              "register_call_dtors" +
+              (Priority != UINT16_MAX ?
+                 (Twine(".") + Twine(Priority)) : Twine()) +
+              (!Associated->isNullValue() ?
+                 (Twine(".") + Associated->getName()) : Twine()),
+              &M);
+      BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors);
+      BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors);
+      BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors);
+
+      Value *Null = ConstantPointerNull::get(VoidStar);
+      Value *Args[] = { CallDtors, Null, DsoHandle };
+      Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB);
+      Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res,
+                                Constant::getNullValue(Res->getType()));
+      BranchInst::Create(FailBB, RetBB, Cmp, EntryBB);
+
+      // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave.
+      // This should be very rare, because if the process is running out of memory
+      // before main has even started, something is wrong.
+      CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap),
+                       "", FailBB);
+      new UnreachableInst(C, FailBB);
+
+      ReturnInst::Create(C, RetBB);
+
+      // Now register the registration function with @llvm.global_ctors.
+      appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated);
+    }
+  }
+
+  // Now that we've lowered everything, remove @llvm.global_dtors.
+  GV->eraseFromParent();
+
+  return true;
+}

Propchange: llvm/trunk/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp
------------------------------------------------------------------------------
    svn:eol-style = LF

Modified: llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp?rev=320774&r1=320773&r2=320774&view=diff
==============================================================================
--- llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp (original)
+++ llvm/trunk/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp Thu Dec 14 16:17:10 2017
@@ -175,6 +175,9 @@ void WebAssemblyPassConfig::addIRPasses(
     // control specifically what gets lowered.
     addPass(createAtomicExpandPass());
 
+  // Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls.
+  addPass(createWebAssemblyLowerGlobalDtors());
+
   // Fix function bitcasts, as WebAssembly requires caller and callee signatures
   // to match.
   addPass(createWebAssemblyFixFunctionBitcasts());

Added: llvm/trunk/test/CodeGen/WebAssembly/lower-global-dtors.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/WebAssembly/lower-global-dtors.ll?rev=320774&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/WebAssembly/lower-global-dtors.ll (added)
+++ llvm/trunk/test/CodeGen/WebAssembly/lower-global-dtors.ll Thu Dec 14 16:17:10 2017
@@ -0,0 +1,139 @@
+; RUN: llc < %s -asm-verbose=false | FileCheck --check-prefix=CHECK --check-prefix=FINI --check-prefix=NULL %s
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Test that @llvm.global_dtors is properly lowered into @llvm.global_ctors,
+; grouping dtor calls by priority and associated symbol.
+
+declare void @orig_ctor()
+declare void @orig_dtor0()
+declare void @orig_dtor1a()
+declare void @orig_dtor1b()
+declare void @orig_dtor1c0()
+declare void @orig_dtor1c1a()
+declare void @orig_dtor1c1b()
+declare void @orig_dtor65536()
+declare void @after_the_null()
+
+ at associated1c0 = external global i8
+ at associated1c1 = external global i8
+
+ at llvm.global_ctors = appending global
+[1 x { i32, void ()*, i8* }]
+[
+  { i32, void ()*, i8* } { i32 200, void ()* @orig_ctor, i8* null }
+]
+
+ at llvm.global_dtors = appending global
+[9 x { i32, void ()*, i8* }]
+[
+  { i32, void ()*, i8* } { i32 0, void ()* @orig_dtor0, i8* null },
+  { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1a, i8* null },
+  { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1b, i8* null },
+  { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c0, i8* @associated1c0 },
+  { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1a, i8* @associated1c1 },
+  { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1b, i8* @associated1c1 },
+  { i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65536, i8* null },
+  { i32, void ()*, i8* } { i32 65535, void ()* null, i8* null },
+  { i32, void ()*, i8* } { i32 65535, void ()* @after_the_null, i8* null }
+]
+
+; CHECK-LABEL: .Lcall_dtors.0:
+; CHECK-NEXT: .param          i32{{$}}
+; CHECK-NEXT: call            orig_dtor0 at FUNCTION{{$}}
+
+; CHECK-LABEL: .Lregister_call_dtors.0:
+; CHECK-NEXT: block
+; CHECK-NEXT: i32.const       $push2=, .Lcall_dtors.0 at FUNCTION{{$}}
+; CHECK-NEXT: i32.const       $push1=, 0
+; CHECK-NEXT: i32.const       $push0=, __dso_handle
+; CHECK-NEXT: i32.call        $push3=, __cxa_atexit at FUNCTION, $pop2, $pop1, $pop0{{$}}
+; CHECK-NEXT: br_if           0, $pop3
+; CHECK-NEXT: return
+;      CHECK: end_block
+; CHECK-NEXT: unreachable
+
+; CHECK-LABEL: .Lcall_dtors.1:
+; CHECK-NEXT: .param          i32{{$}}
+; CHECK-NEXT: call            orig_dtor1a at FUNCTION{{$}}
+; CHECK-NEXT: call            orig_dtor1b at FUNCTION{{$}}
+
+; CHECK-LABEL: .Lregister_call_dtors.1:
+; CHECK-NEXT: block
+; CHECK-NEXT: i32.const       $push2=, .Lcall_dtors.1 at FUNCTION{{$}}
+; CHECK-NEXT: i32.const       $push1=, 0
+; CHECK-NEXT: i32.const       $push0=, __dso_handle
+; CHECK-NEXT: i32.call        $push3=, __cxa_atexit at FUNCTION, $pop2, $pop1, $pop0{{$}}
+; CHECK-NEXT: br_if           0, $pop3
+; CHECK-NEXT: return
+;      CHECK: end_block
+; CHECK-NEXT: unreachable
+
+; CHECK-LABEL: .Lcall_dtors.1.associated1c0:
+; CHECK-NEXT: .param          i32{{$}}
+; CHECK-NEXT: call            orig_dtor1c0 at FUNCTION{{$}}
+
+; CHECK-LABEL: .Lregister_call_dtors.1.associated1c0:
+; CHECK-NEXT: block
+; CHECK-NEXT: i32.const       $push2=, .Lcall_dtors.1.associated1c0 at FUNCTION{{$}}
+; CHECK-NEXT: i32.const       $push1=, 0
+; CHECK-NEXT: i32.const       $push0=, __dso_handle
+; CHECK-NEXT: i32.call        $push3=, __cxa_atexit at FUNCTION, $pop2, $pop1, $pop0{{$}}
+; CHECK-NEXT: br_if           0, $pop3
+; CHECK-NEXT: return
+;      CHECK: end_block
+; CHECK-NEXT: unreachable
+
+; CHECK-LABEL: .Lcall_dtors.1.associated1c1:
+; CHECK-NEXT: .param          i32{{$}}
+; CHECK-NEXT: call            orig_dtor1c1a at FUNCTION{{$}}
+; CHECK-NEXT: call            orig_dtor1c1b at FUNCTION{{$}}
+
+; CHECK-LABEL: .Lregister_call_dtors.1.associated1c1:
+; CHECK-NEXT: block
+; CHECK-NEXT: i32.const       $push2=, .Lcall_dtors.1.associated1c1 at FUNCTION{{$}}
+; CHECK-NEXT: i32.const       $push1=, 0
+; CHECK-NEXT: i32.const       $push0=, __dso_handle
+; CHECK-NEXT: i32.call        $push3=, __cxa_atexit at FUNCTION, $pop2, $pop1, $pop0{{$}}
+; CHECK-NEXT: br_if           0, $pop3
+; CHECK-NEXT: return
+;      CHECK: end_block
+; CHECK-NEXT: unreachable
+
+; CHECK-LABEL: .Lcall_dtors:
+; CHECK-NEXT: .param          i32{{$}}
+; CHECK-NEXT: call            orig_dtor65536 at FUNCTION{{$}}
+
+; CHECK-LABEL: .Lregister_call_dtors:
+; CHECK-NEXT: block
+; CHECK-NEXT: i32.const       $push2=, .Lcall_dtors at FUNCTION{{$}}
+; CHECK-NEXT: i32.const       $push1=, 0
+; CHECK-NEXT: i32.const       $push0=, __dso_handle
+; CHECK-NEXT: i32.call        $push3=, __cxa_atexit at FUNCTION, $pop2, $pop1, $pop0{{$}}
+; CHECK-NEXT: br_if           0, $pop3
+; CHECK-NEXT: return
+;      CHECK: end_block
+; CHECK-NEXT: unreachable
+
+; CHECK-LABEL: .section .init_array.0,"",@
+;      CHECK: .int32  .Lregister_call_dtors.0 at FUNCTION{{$}}
+; CHECK-LABEL: .section .init_array.1,"",@
+;      CHECK: .int32  .Lregister_call_dtors.1 at FUNCTION{{$}}
+; CHECK-LABEL: .section .init_array.200,"",@
+;      CHECK: .int32  orig_ctor at FUNCTION{{$}}
+; CHECK-LABEL: .section .init_array,"",@
+;      CHECK: .int32  .Lregister_call_dtors at FUNCTION{{$}}
+
+; CHECK-LABEL: .weak __dso_handle
+
+; CHECK-LABEL: .functype __cxa_atexit, i32, i32, i32, i32{{$}}
+
+; We shouldn't make use of a .fini_array section.
+
+; FINI-NOT: fini_array
+
+; This function is listed after the null terminator, so it should
+; be excluded.
+
+; NULL-NOT: after_the_null

Modified: llvm/trunk/test/MC/WebAssembly/init-fini-array.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/MC/WebAssembly/init-fini-array.ll?rev=320774&r1=320773&r2=320774&view=diff
==============================================================================
--- llvm/trunk/test/MC/WebAssembly/init-fini-array.ll (original)
+++ llvm/trunk/test/MC/WebAssembly/init-fini-array.ll Thu Dec 14 16:17:10 2017
@@ -2,13 +2,14 @@
 
 @global1 = global i32 1025, align 8
 
+declare void @func0()
 declare void @func1()
-
 declare void @func2()
+declare void @func3()
 
- at llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func1, i8* null }] 
+ at llvm.global_ctors = appending global [2 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func0, i8* null }, { i32, void ()*, i8* } { i32 42, void ()* @func1, i8* null }] 
 
- at llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func2, i8* null }]
+ at llvm.global_dtors = appending global [2 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func2, i8* null }, { i32, void ()*, i8* } { i32 42, void ()* @func3, i8* null }] 
   
 
 ; CHECK:        - Type:            IMPORT
@@ -16,23 +17,42 @@ declare void @func2()
 ; CHECK-NEXT:       - Module:          env
 ; CHECK-NEXT:         Field:           __linear_memory
 ; CHECK-NEXT:         Kind:            MEMORY
-; CHECK-NEXT:         Memory:
+; CHECK-NEXT:         Memory:          
 ; CHECK-NEXT:           Initial:         0x00000001
 ; CHECK-NEXT:       - Module:          env
 ; CHECK-NEXT:         Field:           __indirect_function_table
 ; CHECK-NEXT:         Kind:            TABLE
-; CHECK-NEXT:         Table:
+; CHECK-NEXT:         Table:           
 ; CHECK-NEXT:           ElemType:        ANYFUNC
-; CHECK-NEXT:           Limits:
+; CHECK-NEXT:           Limits:          
 ; CHECK-NEXT:             Initial:         0x00000002
 ; CHECK-NEXT:       - Module:          env
-; CHECK-NEXT:         Field:           func1
+; CHECK-NEXT:         Field:           func3
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         SigIndex:        1
+; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           __dso_handle
+; CHECK-NEXT:         Kind:            GLOBAL
+; CHECK-NEXT:         GlobalType:      I32
+; CHECK-NEXT:         GlobalMutable:   false
+; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           __cxa_atexit
 ; CHECK-NEXT:         Kind:            FUNCTION
-; CHECK-NEXT:         SigIndex:        0
+; CHECK-NEXT:         SigIndex:        2
 ; CHECK-NEXT:       - Module:          env
 ; CHECK-NEXT:         Field:           func2
 ; CHECK-NEXT:         Kind:            FUNCTION
-; CHECK-NEXT:         SigIndex:        0
+; CHECK-NEXT:         SigIndex:        1
+; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           func1
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         SigIndex:        1
+; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           func0
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         SigIndex:        1
+; CHECK-NEXT:   - Type:            FUNCTION
+; CHECK-NEXT:     FunctionTypes:   [ 0, 1, 0, 1 ]
 ; CHECK-NEXT:   - Type:            GLOBAL
 ; CHECK-NEXT:     Globals:         
 ; CHECK-NEXT:       - Type:            I32
@@ -42,23 +62,63 @@ declare void @func2()
 ; CHECK-NEXT:           Value:           0
 ; CHECK-NEXT:   - Type:            EXPORT
 ; CHECK-NEXT:     Exports:         
+; CHECK-NEXT:       - Name:            .Lcall_dtors.42
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Index:           5
+; CHECK-NEXT:       - Name:            .Lregister_call_dtors.42
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Index:           6
+; CHECK-NEXT:       - Name:            .Lcall_dtors
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Index:           7
+; CHECK-NEXT:       - Name:            .Lregister_call_dtors
+; CHECK-NEXT:         Kind:            FUNCTION
+; CHECK-NEXT:         Index:           8
 ; CHECK-NEXT:       - Name:            global1
 ; CHECK-NEXT:         Kind:            GLOBAL
-; CHECK-NEXT:         Index:           0
+; CHECK-NEXT:         Index:           1
 ; CHECK-NEXT:   - Type:            ELEM
 ; CHECK-NEXT:     Segments:        
 ; CHECK-NEXT:       - Offset:          
 ; CHECK-NEXT:           Opcode:          I32_CONST
 ; CHECK-NEXT:           Value:           0
-; CHECK-NEXT:         Functions:       [ 0, 1 ]
-; CHECK-NEXT:   - Type:            DATA
+; CHECK-NEXT:         Functions:       [ 5, 7 ]
+; CHECK-NEXT:   - Type:            CODE
 ; CHECK-NEXT:     Relocations:     
-; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_TABLE_INDEX_I32
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT:         Index:           0
+; CHECK-NEXT:         Offset:          0x00000004
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_TABLE_INDEX_SLEB
 ; CHECK-NEXT:         Index:           0
 ; CHECK-NEXT:         Offset:          0x0000000F
-; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_TABLE_INDEX_I32
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_MEMORY_ADDR_SLEB
+; CHECK-NEXT:         Index:           0
+; CHECK-NEXT:         Offset:          0x00000017
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT:         Index:           1
+; CHECK-NEXT:         Offset:          0x0000001D
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT:         Index:           2
+; CHECK-NEXT:         Offset:          0x0000002C
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_TABLE_INDEX_SLEB
 ; CHECK-NEXT:         Index:           1
-; CHECK-NEXT:         Offset:          0x00000018
+; CHECK-NEXT:         Offset:          0x00000037
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_MEMORY_ADDR_SLEB
+; CHECK-NEXT:         Index:           0
+; CHECK-NEXT:         Offset:          0x0000003F
+; CHECK-NEXT:       - Type:            R_WEBASSEMBLY_FUNCTION_INDEX_LEB
+; CHECK-NEXT:         Index:           1
+; CHECK-NEXT:         Offset:          0x00000045
+; CHECK-NEXT:     Functions:       
+; CHECK-NEXT:       - Locals:          
+; CHECK-NEXT:         Body:            1080808080000B
+; CHECK-NEXT:       - Locals:          
+; CHECK-NEXT:         Body:            0240418080808000410041FFFFFFFF7F1081808080000D000F0B00000B
+; CHECK-NEXT:       - Locals:          
+; CHECK-NEXT:         Body:            1082808080000B
+; CHECK-NEXT:       - Locals:          
+; CHECK-NEXT:         Body:            0240418180808000410041FFFFFFFF7F1081808080000D000F0B00000B
+; CHECK-NEXT:   - Type:            DATA
 ; CHECK-NEXT:     Segments:        
 ; CHECK-NEXT:       - SectionOffset:   6
 ; CHECK-NEXT:         MemoryIndex:     0
@@ -66,40 +126,53 @@ declare void @func2()
 ; CHECK-NEXT:           Opcode:          I32_CONST
 ; CHECK-NEXT:           Value:           0
 ; CHECK-NEXT:         Content:         '01040000'
-; CHECK-NEXT:       - SectionOffset:   15
-; CHECK-NEXT:         MemoryIndex:     0
-; CHECK-NEXT:         Offset:          
-; CHECK-NEXT:           Opcode:          I32_CONST
-; CHECK-NEXT:           Value:           4
-; CHECK-NEXT:         Content:         '00000000'
-; CHECK-NEXT:       - SectionOffset:   24
-; CHECK-NEXT:         MemoryIndex:     0
-; CHECK-NEXT:         Offset:          
-; CHECK-NEXT:           Opcode:          I32_CONST
-; CHECK-NEXT:           Value:           8
-; CHECK-NEXT:         Content:         '01000000'
 ; CHECK-NEXT:   - Type:            CUSTOM
 ; CHECK-NEXT:     Name:            name
 ; CHECK-NEXT:     FunctionNames:   
 ; CHECK-NEXT:       - Index:           0
-; CHECK-NEXT:         Name:            func1
+; CHECK-NEXT:         Name:            func3
 ; CHECK-NEXT:       - Index:           1
+; CHECK-NEXT:         Name:            __cxa_atexit
+; CHECK-NEXT:       - Index:           2
 ; CHECK-NEXT:         Name:            func2
+; CHECK-NEXT:       - Index:           3
+; CHECK-NEXT:         Name:            func1
+; CHECK-NEXT:       - Index:           4
+; CHECK-NEXT:         Name:            func0
+; CHECK-NEXT:       - Index:           5
+; CHECK-NEXT:         Name:            .Lcall_dtors.42
+; CHECK-NEXT:       - Index:           6
+; CHECK-NEXT:         Name:            .Lregister_call_dtors.42
+; CHECK-NEXT:       - Index:           7
+; CHECK-NEXT:         Name:            .Lcall_dtors
+; CHECK-NEXT:       - Index:           8
+; CHECK-NEXT:         Name:            .Lregister_call_dtors
 ; CHECK-NEXT:   - Type:            CUSTOM
 ; CHECK-NEXT:     Name:            linking
-; CHECK-NEXT:     DataSize:        12
-; CHECK-NEXT:     SegmentInfo:
+; CHECK-NEXT:     DataSize:        4
+; CHECK-NEXT:     SymbolInfo:      
+; CHECK-NEXT:       - Name:            __dso_handle
+; CHECK-NEXT:         Flags:           [ BINDING_WEAK, VISIBILITY_HIDDEN ]
+; CHECK-NEXT:       - Name:            .Lcall_dtors.42
+; CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+; CHECK-NEXT:       - Name:            .Lregister_call_dtors.42
+; CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+; CHECK-NEXT:       - Name:            .Lcall_dtors
+; CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+; CHECK-NEXT:       - Name:            .Lregister_call_dtors
+; CHECK-NEXT:         Flags:           [ BINDING_LOCAL ]
+; CHECK-NEXT:     SegmentInfo:     
 ; CHECK-NEXT:       - Index:           0
 ; CHECK-NEXT:         Name:            .data.global1
 ; CHECK-NEXT:         Alignment:       8
 ; CHECK-NEXT:         Flags:           [ ]
-; CHECK-NEXT:       - Index:           1
-; CHECK-NEXT:         Name:            .init_array
-; CHECK-NEXT:         Alignment:       4
-; CHECK-NEXT:         Flags:           [ ]
-; CHECK-NEXT:       - Index:           2
-; CHECK-NEXT:         Name:            .fini_array
-; CHECK-NEXT:         Alignment:       4
-; CHECK-NEXT:         Flags:           [ ]
+; CHECK-NEXT:     InitFunctions:     
+; CHECK-NEXT:       - Priority: 42
+; CHECK-NEXT:         FunctionIndex: 3
+; CHECK-NEXT:       - Priority: 42
+; CHECK-NEXT:         FunctionIndex: 6
+; CHECK-NEXT:       - Priority: 65535
+; CHECK-NEXT:         FunctionIndex: 4
+; CHECK-NEXT:       - Priority: 65535
+; CHECK-NEXT:         FunctionIndex: 8
 ; CHECK-NEXT: ...
-




More information about the llvm-commits mailing list