Index: include/llvm/BinaryFormat/Wasm.h =================================================================== --- include/llvm/BinaryFormat/Wasm.h +++ include/llvm/BinaryFormat/Wasm.h @@ -186,6 +186,7 @@ WASM_DATA_SIZE = 0x3, WASM_DATA_ALIGNMENT = 0x4, WASM_SEGMENT_INFO = 0x5, + WASM_START_FUNCS = 0x6, }; const unsigned WASM_SYMBOL_BINDING_MASK = 0x3; Index: include/llvm/CodeGen/TargetLoweringObjectFileImpl.h =================================================================== --- include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -182,6 +182,10 @@ 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, Index: lib/CodeGen/TargetLoweringObjectFileImpl.cpp =================================================================== --- lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -1348,6 +1348,18 @@ 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; } Index: lib/MC/WasmObjectWriter.cpp =================================================================== --- lib/MC/WasmObjectWriter.cpp +++ lib/MC/WasmObjectWriter.cpp @@ -275,6 +275,7 @@ void writeFunctionSection(ArrayRef Functions); void writeGlobalSection(); void writeExportSection(ArrayRef Exports); + void writeStartSection(Optional StartFunc); void writeElemSection(ArrayRef TableElems); void writeCodeSection(const MCAssembler &Asm, const MCAsmLayout &Layout, ArrayRef Functions); @@ -286,7 +287,8 @@ void writeDataRelocSection(); void writeLinkingMetaDataSection( ArrayRef Segments, uint32_t DataSize, - SmallVector, 4> SymbolFlags); + const SmallVector, 4> &SymbolFlags, + const SmallVector, 2> &ExtraStartFuncs); uint32_t getProvisionalValue(const WasmRelocationEntry &RelEntry); void applyRelocations(ArrayRef Relocations, @@ -368,6 +370,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"); @@ -761,6 +767,18 @@ endSection(Section); } +void WasmObjectWriter::writeStartSection(Optional StartFunc) { + if (!StartFunc.hasValue()) + return; + + SectionBookkeeping Section; + startSection(Section, wasm::WASM_SEC_START); + + encodeULEB128(StartFunc.getValue(), getStream()); + + endSection(Section); +} + void WasmObjectWriter::writeElemSection(ArrayRef TableElems) { if (TableElems.empty()) return; @@ -907,7 +925,8 @@ void WasmObjectWriter::writeLinkingMetaDataSection( ArrayRef Segments, uint32_t DataSize, - SmallVector, 4> SymbolFlags) { + const SmallVector, 4> &SymbolFlags, + const SmallVector, 2> &ExtraStartFuncs) { SectionBookkeeping Section; startSection(Section, wasm::WASM_SEC_CUSTOM, "linking"); SectionBookkeeping SubSection; @@ -939,6 +958,16 @@ endSection(SubSection); } + if (!ExtraStartFuncs.empty()) { + startSection(SubSection, wasm::WASM_START_FUNCS); + encodeULEB128(ExtraStartFuncs.size(), getStream()); + for (auto &StartFunc : ExtraStartFuncs) { + encodeULEB128(StartFunc.first, getStream()); // priority + encodeULEB128(StartFunc.second, getStream()); // function index + } + endSection(SubSection); + } + endSection(Section); } @@ -979,6 +1008,8 @@ SmallVector Imports; SmallVector Exports; SmallVector, 4> SymbolFlags; + Optional StartFunc; + SmallVector, 2> ExtraStartFuncs; SmallPtrSet IsAddressTaken; unsigned NumFuncImports = 0; SmallVector DataSegments; @@ -1134,6 +1165,10 @@ if (!Section.isWasmData()) continue; + // .init_array sections are handled specially elsewhere. + if (cast(Sec).getSectionName().startswith(".init_array")) + continue; + DataSize = alignTo(DataSize, Section.getAlignment()); DataSegments.emplace_back(); WasmDataSegment &Segment = DataSegments.back(); @@ -1293,6 +1328,59 @@ registerFunctionType(*Fixup.Symbol); } + // Translate .init_array section contents into start functions. + for (const MCSection &S : Asm) { + const auto &WS = static_cast(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(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(Frag); + const SmallVectorImpl &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(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(&Sym->getSymbol())); + if (I == SymbolIndices.end()) + report_fatal_error("symbols in .init_array should be defined"); + uint32_t Index = I->second; + if (!StartFunc.hasValue() && Priority == UINT16_MAX) + StartFunc = Index; + else + ExtraStartFuncs.push_back(std::make_pair(Priority, Index)); + } + } + // Write out the Wasm header. writeHeader(Asm); @@ -1303,14 +1391,15 @@ // Skip the "memory" section; we import the memory instead. writeGlobalSection(); writeExportSection(Exports); - // TODO: Start Section + writeStartSection(StartFunc); writeElemSection(TableElems); writeCodeSection(Asm, Layout, Functions); writeDataSection(DataSegments); writeNameSection(Functions, Imports, NumFuncImports); writeCodeRelocSection(); writeDataRelocSection(); - writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags); + writeLinkingMetaDataSection(DataSegments, DataSize, SymbolFlags, + ExtraStartFuncs); // TODO: Translate the .comment section to the output. // TODO: Translate debug sections to the output. Index: lib/Object/WasmObjectFile.cpp =================================================================== --- lib/Object/WasmObjectFile.cpp +++ lib/Object/WasmObjectFile.cpp @@ -658,7 +658,7 @@ Error WasmObjectFile::parseStartSection(const uint8_t *Ptr, const uint8_t *End) { StartFunction = readVaruint32(Ptr); - if (StartFunction >= FunctionTypes.size()) + if (StartFunction >= FunctionTypes.size() + NumImportedFunctions) return make_error("Invalid start function", object_error::parse_failed); return Error::success(); Index: lib/Target/WebAssembly/CMakeLists.txt =================================================================== --- lib/Target/WebAssembly/CMakeLists.txt +++ lib/Target/WebAssembly/CMakeLists.txt @@ -25,6 +25,7 @@ WebAssemblyInstrInfo.cpp WebAssemblyLowerBrUnless.cpp WebAssemblyLowerEmscriptenEHSjLj.cpp + WebAssemblyLowerGlobalDtors.cpp WebAssemblyMachineFunctionInfo.cpp WebAssemblyMCInstLower.cpp WebAssemblyOptimizeLiveIntervals.cpp Index: lib/Target/WebAssembly/WebAssembly.h =================================================================== --- lib/Target/WebAssembly/WebAssembly.h +++ lib/Target/WebAssembly/WebAssembly.h @@ -28,6 +28,7 @@ // LLVM IR passes. ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(bool DoEH, bool DoSjLj); void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &); +ModulePass *createWebAssemblyLowerGlobalDtors(); ModulePass *createWebAssemblyFixFunctionBitcasts(); FunctionPass *createWebAssemblyOptimizeReturned(); Index: lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp +++ lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp @@ -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(GV->getInitializer()); + if (!InitList) + return false; + + // Sanity-check @llvm.global_dtor's type. + StructType *ETy = dyn_cast(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 > > DtorFuncs; + for (Value *O : InitList->operands()) { + ConstantStruct *CS = dyn_cast(O); + if (!CS) continue; // Malformed. + + ConstantInt *Priority = dyn_cast(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(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; +} Index: lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -175,6 +175,9 @@ // 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()); Index: test/CodeGen/WebAssembly/lower-global-dtors.ll =================================================================== --- test/CodeGen/WebAssembly/lower-global-dtors.ll +++ test/CodeGen/WebAssembly/lower-global-dtors.ll @@ -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() + +@associated1c0 = external global i8 +@associated1c1 = external global i8 + +@llvm.global_ctors = appending global +[1 x { i32, void ()*, i8* }] +[ + { i32, void ()*, i8* } { i32 200, void ()* @orig_ctor, i8* null } +] + +@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@FUNCTION{{$}} + +; CHECK-LABEL: .Lregister_call_dtors.0: +; CHECK-NEXT: block +; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.0@FUNCTION{{$}} +; CHECK-NEXT: i32.const $push1=, 0 +; CHECK-NEXT: i32.const $push0=, __dso_handle +; CHECK-NEXT: i32.call $push3=, __cxa_atexit@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@FUNCTION{{$}} +; CHECK-NEXT: call orig_dtor1b@FUNCTION{{$}} + +; CHECK-LABEL: .Lregister_call_dtors.1: +; CHECK-NEXT: block +; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1@FUNCTION{{$}} +; CHECK-NEXT: i32.const $push1=, 0 +; CHECK-NEXT: i32.const $push0=, __dso_handle +; CHECK-NEXT: i32.call $push3=, __cxa_atexit@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@FUNCTION{{$}} + +; CHECK-LABEL: .Lregister_call_dtors.1.associated1c0: +; CHECK-NEXT: block +; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c0@FUNCTION{{$}} +; CHECK-NEXT: i32.const $push1=, 0 +; CHECK-NEXT: i32.const $push0=, __dso_handle +; CHECK-NEXT: i32.call $push3=, __cxa_atexit@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@FUNCTION{{$}} +; CHECK-NEXT: call orig_dtor1c1b@FUNCTION{{$}} + +; CHECK-LABEL: .Lregister_call_dtors.1.associated1c1: +; CHECK-NEXT: block +; CHECK-NEXT: i32.const $push2=, .Lcall_dtors.1.associated1c1@FUNCTION{{$}} +; CHECK-NEXT: i32.const $push1=, 0 +; CHECK-NEXT: i32.const $push0=, __dso_handle +; CHECK-NEXT: i32.call $push3=, __cxa_atexit@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@FUNCTION{{$}} + +; CHECK-LABEL: .Lregister_call_dtors: +; CHECK-NEXT: block +; CHECK-NEXT: i32.const $push2=, .Lcall_dtors@FUNCTION{{$}} +; CHECK-NEXT: i32.const $push1=, 0 +; CHECK-NEXT: i32.const $push0=, __dso_handle +; CHECK-NEXT: i32.call $push3=, __cxa_atexit@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@FUNCTION{{$}} +; CHECK-LABEL: .section .init_array.1,"",@ +; CHECK: .int32 .Lregister_call_dtors.1@FUNCTION{{$}} +; CHECK-LABEL: .section .init_array.200,"",@ +; CHECK: .int32 orig_ctor@FUNCTION{{$}} +; CHECK-LABEL: .section .init_array,"",@ +; CHECK: .int32 .Lregister_call_dtors@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 Index: test/MC/WebAssembly/init-fini-array.ll =================================================================== --- test/MC/WebAssembly/init-fini-array.ll +++ test/MC/WebAssembly/init-fini-array.ll @@ -2,13 +2,14 @@ @global1 = global i32 1025, align 8 +declare void @func0() declare void @func1() - declare void @func2() +declare void @func3() -@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func1, i8* null }] +@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 }] -@llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @func2, i8* null }] +@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 @@ ; 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,65 @@ ; 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: START +; CHECK-NEXT: StartFunction: 4 ; 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 +128,44 @@ ; 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: 5 +; CHECK-NEXT: - Name: .Lcall_dtors.42 +; CHECK-NEXT: Flags: 2 +; CHECK-NEXT: - Name: .Lregister_call_dtors.42 +; CHECK-NEXT: Flags: 2 +; CHECK-NEXT: - Name: .Lcall_dtors +; CHECK-NEXT: Flags: 2 +; CHECK-NEXT: - Name: .Lregister_call_dtors +; CHECK-NEXT: Flags: 2 +; CHECK-NEXT: SegmentInfo: ; CHECK-NEXT: - Index: 0 ; CHECK-NEXT: Name: .data.global1 ; CHECK-NEXT: Alignment: 8 ; CHECK-NEXT: Flags: 0 -; CHECK-NEXT: - Index: 1 -; CHECK-NEXT: Name: .init_array -; CHECK-NEXT: Alignment: 4 -; CHECK-NEXT: Flags: 0 -; CHECK-NEXT: - Index: 2 -; CHECK-NEXT: Name: .fini_array -; CHECK-NEXT: Alignment: 4 -; CHECK-NEXT: Flags: 0 ; CHECK-NEXT: ... -