diff --git a/llvm/docs/Passes.rst b/llvm/docs/Passes.rst --- a/llvm/docs/Passes.rst +++ b/llvm/docs/Passes.rst @@ -876,6 +876,14 @@ invariant conditions out of the loop, to make the unswitching opportunity obvious. +``-lower-global-dtors``: Lower global destructors +------------------------------------------------------------ + +This pass lowers global module destructors (``llvm.global_dtors``) by creating +wrapper functions that are registered as global constructors in +``llvm.global_ctors`` and which contain a call to ``__cxa_atexit`` to register +their destructor functions. + ``-loweratomic``: Lower atomic intrinsics to non-atomic form ------------------------------------------------------------ diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h --- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -119,6 +119,9 @@ void Initialize(MCContext &Ctx, const TargetMachine &TM) override; + MCSection *getStaticDtorSection(unsigned Priority, + const MCSymbol *KeySym) const override; + /// Emit the module flags that specify the garbage collection information. void emitModuleMetadata(MCStreamer &Streamer, Module &M) const override; diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -274,6 +274,7 @@ void initializeLowerConstantIntrinsicsPass(PassRegistry&); void initializeLowerEmuTLSPass(PassRegistry&); void initializeLowerExpectIntrinsicPass(PassRegistry&); +void initializeLowerGlobalDtorsLegacyPassPass(PassRegistry &); void initializeLowerGuardIntrinsicLegacyPassPass(PassRegistry&); void initializeLowerWidenableConditionLegacyPassPass(PassRegistry&); void initializeLowerIntrinsicsPass(PassRegistry&); diff --git a/llvm/include/llvm/LinkAllPasses.h b/llvm/include/llvm/LinkAllPasses.h --- a/llvm/include/llvm/LinkAllPasses.h +++ b/llvm/include/llvm/LinkAllPasses.h @@ -145,6 +145,7 @@ (void) llvm::createLoopRotatePass(); (void) llvm::createLowerConstantIntrinsicsPass(); (void) llvm::createLowerExpectIntrinsicPass(); + (void) llvm::createLowerGlobalDtorsLegacyPass(); (void) llvm::createLowerInvokePass(); (void) llvm::createLowerSwitchPass(); (void) llvm::createNaryReassociatePass(); diff --git a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h --- a/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h +++ b/llvm/include/llvm/Transforms/Instrumentation/AddressSanitizerOptions.h @@ -17,7 +17,6 @@ None, ///< Do not emit any destructors for ASan Global, ///< Append to llvm.global_dtors Invalid, ///< Not a valid destructor Kind. - // TODO(dliew): Add more more kinds. }; /// Mode of ASan detect stack use after return diff --git a/llvm/include/llvm/Transforms/Utils.h b/llvm/include/llvm/Transforms/Utils.h --- a/llvm/include/llvm/Transforms/Utils.h +++ b/llvm/include/llvm/Transforms/Utils.h @@ -155,6 +155,12 @@ // don't block SCEV. // Pass *createCanonicalizeFreezeInLoopsPass(); + +//===----------------------------------------------------------------------===// +// LowerGlobalDtorsLegacy - Lower @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. +ModulePass *createLowerGlobalDtorsLegacyPass(); } // namespace llvm #endif diff --git a/llvm/include/llvm/Transforms/Utils/LowerGlobalDtors.h b/llvm/include/llvm/Transforms/Utils/LowerGlobalDtors.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/Transforms/Utils/LowerGlobalDtors.h @@ -0,0 +1,28 @@ +//===- LowerGlobalDtors.h - Lower @llvm.global_dtors ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass lowers @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. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_UTILS_LOWERGLOBALDTORS_H +#define LLVM_TRANSFORMS_UTILS_LOWERGLOBALDTORS_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +class LowerGlobalDtorsPass : public PassInfoMixin { +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_UTILS_LOWERGLOBALDTORS_H diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -58,6 +58,7 @@ initializeLiveStacksPass(Registry); initializeLiveVariablesPass(Registry); initializeLocalStackSlotPassPass(Registry); + initializeLowerGlobalDtorsLegacyPassPass(Registry); initializeLowerIntrinsicsPass(Registry); initializeMIRAddFSDiscriminatorsPass(Registry); initializeMIRCanonicalizerPass(Registry); diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -1158,15 +1158,10 @@ if (TM.getRelocationModel() == Reloc::Static) { StaticCtorSection = Ctx.getMachOSection("__TEXT", "__constructor", 0, SectionKind::getData()); - StaticDtorSection = Ctx.getMachOSection("__TEXT", "__destructor", 0, - SectionKind::getData()); } else { StaticCtorSection = Ctx.getMachOSection("__DATA", "__mod_init_func", MachO::S_MOD_INIT_FUNC_POINTERS, SectionKind::getData()); - StaticDtorSection = Ctx.getMachOSection("__DATA", "__mod_term_func", - MachO::S_MOD_TERM_FUNC_POINTERS, - SectionKind::getData()); } PersonalityEncoding = @@ -1176,6 +1171,11 @@ dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; } +MCSection *TargetLoweringObjectFileMachO::getStaticDtorSection( + unsigned Priority, const MCSymbol *KeySym) const { + report_fatal_error("@llvm.global_dtors should have been lowered already"); +} + void TargetLoweringObjectFileMachO::emitModuleMetadata(MCStreamer &Streamer, Module &M) const { // Emit the linker options if present. @@ -2175,8 +2175,7 @@ MCSection *TargetLoweringObjectFileWasm::getStaticDtorSection( unsigned Priority, const MCSymbol *KeySym) const { - llvm_unreachable("@llvm.global_dtors should have been lowered already"); - return nullptr; + report_fatal_error("@llvm.global_dtors should have been lowered already"); } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -895,6 +895,11 @@ addPass(&ShadowStackGCLoweringID); addPass(createLowerConstantIntrinsicsPass()); + // For MachO, lower .llvm.global_dtors into .llvm_global_ctors with + // __cxa_atexit calls to avoid emitting the deprecated __mod_term_func. + if (TM->getTargetTriple().isOSBinFormatMachO()) + addPass(createLowerGlobalDtorsLegacyPass()); + // Make sure that no unreachable blocks are instruction selected. addPass(createUnreachableBlockEliminationPass()); diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -229,6 +229,7 @@ #include "llvm/Transforms/Utils/LibCallsShrinkWrap.h" #include "llvm/Transforms/Utils/LoopSimplify.h" #include "llvm/Transforms/Utils/LoopVersioning.h" +#include "llvm/Transforms/Utils/LowerGlobalDtors.h" #include "llvm/Transforms/Utils/LowerInvoke.h" #include "llvm/Transforms/Utils/LowerSwitch.h" #include "llvm/Transforms/Utils/Mem2Reg.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -76,6 +76,7 @@ MODULE_PASS("ipsccp", IPSCCPPass()) MODULE_PASS("iroutliner", IROutlinerPass()) MODULE_PASS("print-ir-similarity", IRSimilarityAnalysisPrinterPass(dbgs())) +MODULE_PASS("lower-global-dtors", LowerGlobalDtorsPass()) MODULE_PASS("lowertypetests", LowerTypeTestsPass()) MODULE_PASS("metarenamer", MetaRenamerPass()) MODULE_PASS("mergefunc", MergeFunctionsPass()) diff --git a/llvm/lib/Target/WebAssembly/CMakeLists.txt b/llvm/lib/Target/WebAssembly/CMakeLists.txt --- a/llvm/lib/Target/WebAssembly/CMakeLists.txt +++ b/llvm/lib/Target/WebAssembly/CMakeLists.txt @@ -35,7 +35,6 @@ WebAssemblyInstrInfo.cpp WebAssemblyLowerBrUnless.cpp WebAssemblyLowerEmscriptenEHSjLj.cpp - WebAssemblyLowerGlobalDtors.cpp WebAssemblyLowerRefTypesIntPtrConv.cpp WebAssemblyMachineFunctionInfo.cpp WebAssemblyMCInstLower.cpp diff --git a/llvm/lib/Target/WebAssembly/WebAssembly.h b/llvm/lib/Target/WebAssembly/WebAssembly.h --- a/llvm/lib/Target/WebAssembly/WebAssembly.h +++ b/llvm/lib/Target/WebAssembly/WebAssembly.h @@ -26,7 +26,6 @@ // LLVM IR passes. ModulePass *createWebAssemblyLowerEmscriptenEHSjLj(); -ModulePass *createWebAssemblyLowerGlobalDtors(); ModulePass *createWebAssemblyAddMissingPrototypes(); ModulePass *createWebAssemblyFixFunctionBitcasts(); FunctionPass *createWebAssemblyOptimizeReturned(); @@ -61,7 +60,6 @@ // PassRegistry initialization declarations. void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &); void initializeWebAssemblyLowerEmscriptenEHSjLjPass(PassRegistry &); -void initializeLowerGlobalDtorsPass(PassRegistry &); void initializeFixFunctionBitcastsPass(PassRegistry &); void initializeOptimizeReturnedPass(PassRegistry &); void initializeWebAssemblyArgumentMovePass(PassRegistry &); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyTargetMachine.cpp @@ -25,6 +25,7 @@ #include "llvm/CodeGen/RegAllocRegistry.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/Function.h" +#include "llvm/InitializePasses.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Target/TargetOptions.h" @@ -56,7 +57,7 @@ auto &PR = *PassRegistry::getPassRegistry(); initializeWebAssemblyAddMissingPrototypesPass(PR); initializeWebAssemblyLowerEmscriptenEHSjLjPass(PR); - initializeLowerGlobalDtorsPass(PR); + initializeLowerGlobalDtorsLegacyPassPass(PR); initializeFixFunctionBitcastsPass(PR); initializeOptimizeReturnedPass(PR); initializeWebAssemblyArgumentMovePass(PR); @@ -412,7 +413,7 @@ addPass(createWebAssemblyAddMissingPrototypes()); // Lower .llvm.global_dtors into .llvm_global_ctors with __cxa_atexit calls. - addPass(createWebAssemblyLowerGlobalDtors()); + addPass(createLowerGlobalDtorsLegacyPass()); // Fix function bitcasts, as WebAssembly requires caller and callee signatures // to match. diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt --- a/llvm/lib/Transforms/Utils/CMakeLists.txt +++ b/llvm/lib/Transforms/Utils/CMakeLists.txt @@ -44,6 +44,7 @@ LoopUnrollRuntime.cpp LoopUtils.cpp LoopVersioning.cpp + LowerGlobalDtors.cpp LowerInvoke.cpp LowerMemIntrinsics.cpp LowerSwitch.cpp diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp b/llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp rename from llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp rename to llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp +++ b/llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp @@ -1,4 +1,4 @@ -//===-- WebAssemblyLowerGlobalDtors.cpp - Lower @llvm.global_dtors --------===// +//===-- LowerGlobalDtors.cpp - Lower @llvm.global_dtors -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,33 +9,31 @@ /// \file /// 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/ADT/MapVector.h" +#include "llvm/Transforms/Utils/LowerGlobalDtors.h" + #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" -#include "llvm/IR/Module.h" +#include "llvm/InitializePasses.h" #include "llvm/Pass.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include using namespace llvm; -#define DEBUG_TYPE "wasm-lower-global-dtors" +#define DEBUG_TYPE "lower-global-dtors" namespace { -class LowerGlobalDtors final : public ModulePass { +class LowerGlobalDtorsLegacyPass final : public ModulePass { StringRef getPassName() const override { - return "WebAssembly Lower @llvm.global_dtors"; + return "Lower @llvm.global_dtors via `__cxa_atexit`"; } void getAnalysisUsage(AnalysisUsage &AU) const override { @@ -47,21 +45,33 @@ public: static char ID; - LowerGlobalDtors() : ModulePass(ID) {} + LowerGlobalDtorsLegacyPass() : ModulePass(ID) {} }; } // End anonymous namespace -char LowerGlobalDtors::ID = 0; -INITIALIZE_PASS(LowerGlobalDtors, DEBUG_TYPE, - "Lower @llvm.global_dtors for WebAssembly", false, false) +char LowerGlobalDtorsLegacyPass::ID = 0; +INITIALIZE_PASS(LowerGlobalDtorsLegacyPass, DEBUG_TYPE, + "Lower @llvm.global_dtors via `__cxa_atexit`", false, false) -ModulePass *llvm::createWebAssemblyLowerGlobalDtors() { - return new LowerGlobalDtors(); +ModulePass *llvm::createLowerGlobalDtorsLegacyPass() { + return new LowerGlobalDtorsLegacyPass(); } -bool LowerGlobalDtors::runOnModule(Module &M) { - LLVM_DEBUG(dbgs() << "********** Lower Global Destructors **********\n"); +static bool runImpl(Module &M); +bool LowerGlobalDtorsLegacyPass::runOnModule(Module &M) { return runImpl(M); } + +PreservedAnalyses LowerGlobalDtorsPass::run(Module &M, + ModuleAnalysisManager &AM) { + bool Changed = runImpl(M); + if (!Changed) + return PreservedAnalyses::all(); + + PreservedAnalyses PA; + PA.preserveSet(); + return PA; +} +static bool runImpl(Module &M) { GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors"); if (!GV || !GV->hasInitializer()) return false; diff --git a/llvm/test/CodeGen/ARM/ctors_dtors.ll b/llvm/test/CodeGen/ARM/ctors_dtors.ll --- a/llvm/test/CodeGen/ARM/ctors_dtors.ll +++ b/llvm/test/CodeGen/ARM/ctors_dtors.ll @@ -2,8 +2,10 @@ ; RUN: llc < %s -mtriple=arm-linux-gnu -target-abi=apcs | FileCheck %s -check-prefix=ELF ; RUN: llc < %s -mtriple=arm-linux-gnueabi | FileCheck %s -check-prefix=GNUEABI +; DARWIN: l_register_call_dtors: +; DARWIN: bl ___cxa_atexit ; DARWIN: .section __DATA,__mod_init_func,mod_init_funcs -; DARWIN: .section __DATA,__mod_term_func,mod_term_funcs +; DARWIN-NOT: .section __DATA,__mod_term_func,mod_term_funcs ; ELF: .section .ctors,"aw",%progbits ; ELF: .section .dtors,"aw",%progbits diff --git a/llvm/test/CodeGen/X86/2011-08-29-InitOrder.ll b/llvm/test/CodeGen/X86/2011-08-29-InitOrder.ll --- a/llvm/test/CodeGen/X86/2011-08-29-InitOrder.ll +++ b/llvm/test/CodeGen/X86/2011-08-29-InitOrder.ll @@ -10,9 +10,13 @@ ; CHECK-DEFAULT: .section .ctors.64535,"aw",@progbits ; CHECK-DEFAULT: .long construct_1 -; CHECK-DARWIN: .long _construct_1 +; CHECK-DARWIN-LABEL: .section __DATA,__mod_init_func,mod_init_funcs +; CHECK-DARWIN: .long _construct_1 +; CHECK-DARWIN-NEXT: .long l_register_call_dtors.1000 ; CHECK-DARWIN-NEXT: .long _construct_2 +; CHECK-DARWIN-NEXT: .long l_register_call_dtors.2000 ; CHECK-DARWIN-NEXT: .long _construct_3 +; CHECK-DARWIN-NEXT: .long l_register_call_dtors.3000 @llvm.global_dtors = appending global [3 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 2000, void ()* @destruct_2, i8* null }, { i32, void ()*, i8* } { i32 1000, void ()* @destruct_1, i8* null }, { i32, void ()*, i8* } { i32 3000, void ()* @destruct_3, i8* null }] ; CHECK-DEFAULT: .section .dtors.62535,"aw",@progbits @@ -22,9 +26,7 @@ ; CHECK-DEFAULT: .section .dtors.64535,"aw",@progbits ; CHECK-DEFAULT: .long destruct_1 -; CHECK-DARWIN: .long _destruct_1 -; CHECK-DARWIN-NEXT: .long _destruct_2 -; CHECK-DARWIN-NEXT: .long _destruct_3 +; CHECK-DARWIN-NOT: mod_term_func declare void @construct_1() declare void @construct_2() diff --git a/llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors.ll b/llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/LowerGlobalDestructors/lower-global-dtors.ll @@ -0,0 +1,156 @@ +; RUN: opt -lower-global-dtors -S < %s | FileCheck %s --implicit-check-not=llvm.global_dtors +; RUN: opt -passes=lower-global-dtors -S < %s | FileCheck %s --implicit-check-not=llvm.global_dtors + +; 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_dtor1c2a() +declare void @orig_dtor1c2b() +declare void @orig_dtor1c3() +declare void @orig_dtor1d() +declare void @orig_dtor65535() +declare void @orig_dtor65535c0() +declare void @after_the_null() + +@associatedc0 = external global i8 +@associatedc1 = external global i8 +@associatedc2 = global i8 42 +@associatedc3 = global i8 84 + +@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 +[14 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* @associatedc0 }, + { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1a, i8* @associatedc1 }, + { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c1b, i8* @associatedc1 }, + { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c2a, i8* @associatedc2 }, + { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c2b, i8* @associatedc2 }, + { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1c3, i8* @associatedc3 }, + { i32, void ()*, i8* } { i32 1, void ()* @orig_dtor1d, i8* null }, + { i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65535c0, i8* @associatedc0 }, + { i32, void ()*, i8* } { i32 65535, void ()* @orig_dtor65535, i8* null }, + { i32, void ()*, i8* } { i32 65535, void ()* null, i8* null }, + { i32, void ()*, i8* } { i32 65535, void ()* @after_the_null, i8* null } +] + +; CHECK: @associatedc0 = external global i8 +; CHECK: @associatedc1 = external global i8 +; CHECK: @associatedc2 = global i8 42 +; CHECK: @associatedc3 = global i8 84 +; CHECK: @__dso_handle = extern_weak hidden constant i8 + +; CHECK-LABEL: @llvm.global_ctors = appending global [10 x { i32, void ()*, i8* }] [ +; CHECK-SAME: { i32, void ()*, i8* } { i32 200, void ()* @orig_ctor, i8* null }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 0, void ()* @register_call_dtors.0, i8* null }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 1, void ()* @"register_call_dtors.1$0", i8* null }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 1, void ()* @"register_call_dtors.1$1.associatedc0", i8* @associatedc0 }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 1, void ()* @"register_call_dtors.1$2.associatedc1", i8* @associatedc1 }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 1, void ()* @"register_call_dtors.1$3.associatedc2", i8* @associatedc2 }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 1, void ()* @"register_call_dtors.1$4.associatedc3", i8* @associatedc3 }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 1, void ()* @"register_call_dtors.1$5", i8* null }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 65535, void ()* @"register_call_dtors$0.associatedc0", i8* @associatedc0 }, +; CHECK-SAME: { i32, void ()*, i8* } { i32 65535, void ()* @"register_call_dtors$1", i8* null }] + +; CHECK: declare void @orig_ctor() +; CHECK: declare void @orig_dtor0() +; --- other dtors here --- +; CHECK: declare void @after_the_null() + +; CHECK: declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*) + +; CHECK-LABEL: define private void @call_dtors.0(i8* %0) +; CHECK: call void @orig_dtor0() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @register_call_dtors.0() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @call_dtors.0, i8* null, i8* @__dso_handle) +; CHECK-NEXT: %0 = icmp ne i32 %call, 0 +; CHECK-NEXT: br i1 %0, label %fail, label %return +; CHECK-EMPTY: +; CHECK-NEXT: fail: +; CHECK-NEXT: call void @llvm.trap() +; CHECK-NEXT: unreachable +; CHECK-EMPTY: +; CHECK-NEXT: return: +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"call_dtors.1$0"(i8* %0) +; CHECK: call void @orig_dtor1b() +; CHECK-NEXT: call void @orig_dtor1a() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors.1$0"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors.1$0", i8* null, i8* @__dso_handle) + +; CHECK-LABEL: define private void @"call_dtors.1$1.associatedc0"(i8* %0) +; CHECK: call void @orig_dtor1c0() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors.1$1.associatedc0"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors.1$1.associatedc0", i8* null, i8* @__dso_handle) + +; CHECK-LABEL: define private void @"call_dtors.1$2.associatedc1"(i8* %0) +; CHECK: call void @orig_dtor1c1b() +; CHECK-NEXT: call void @orig_dtor1c1a() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors.1$2.associatedc1"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors.1$2.associatedc1", i8* null, i8* @__dso_handle) + +; CHECK-LABEL: define private void @"call_dtors.1$3.associatedc2"(i8* %0) +; CHECK: call void @orig_dtor1c2b() +; CHECK-NEXT: call void @orig_dtor1c2a() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors.1$3.associatedc2"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors.1$3.associatedc2", i8* null, i8* @__dso_handle) + +; CHECK-LABEL: define private void @"call_dtors.1$4.associatedc3"(i8* %0) +; CHECK: call void @orig_dtor1c3() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors.1$4.associatedc3"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors.1$4.associatedc3", i8* null, i8* @__dso_handle) + +; CHECK-LABEL: define private void @"call_dtors.1$5"(i8* %0) +; CHECK: call void @orig_dtor1d() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors.1$5"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors.1$5", i8* null, i8* @__dso_handle) + +; CHECK-LABEL: define private void @"call_dtors$0.associatedc0"(i8* %0) +; CHECK: call void @orig_dtor65535c0() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors$0.associatedc0"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors$0.associatedc0", i8* null, i8* @__dso_handle) + +; CHECK-LABEL: define private void @"call_dtors$1"(i8* %0) +; CHECK: call void @orig_dtor65535() +; CHECK-NEXT: ret void + +; CHECK-LABEL: define private void @"register_call_dtors$1"() +; CHECK: %call = call i32 @__cxa_atexit(void (i8*)* @"call_dtors$1", i8* null, i8* @__dso_handle) + + +; This function is listed after the null terminator, so it should +; be excluded. + +; CHECK-NOT: after_the_null