diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -546,6 +546,8 @@ Options.BinutilsVersion = llvm::TargetMachine::parseBinutilsVersion(CodeGenOpts.BinutilsVersion); Options.UseInitArray = CodeGenOpts.UseInitArray; + Options.LowerGlobalDtorsViaCxaAtExit = + CodeGenOpts.RegisterGlobalDtorsWithAtExit; Options.DisableIntegratedAS = CodeGenOpts.DisableIntegratedAS; Options.CompressDebugSections = CodeGenOpts.getCompressDebugSections(); Options.RelaxELFRelocations = CodeGenOpts.RelaxELFRelocations; 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/CommandFlags.h b/llvm/include/llvm/CodeGen/CommandFlags.h --- a/llvm/include/llvm/CodeGen/CommandFlags.h +++ b/llvm/include/llvm/CodeGen/CommandFlags.h @@ -95,6 +95,8 @@ bool getUseCtors(); +bool getLowerGlobalDtorsViaCxaAtExit(); + bool getRelaxELFRelocations(); bool getDataSections(); 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/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/llvm/include/llvm/Target/TargetOptions.h @@ -130,12 +130,13 @@ HonorSignDependentRoundingFPMathOption(false), NoZerosInBSS(false), GuaranteedTailCallOpt(false), StackSymbolOrdering(true), EnableFastISel(false), EnableGlobalISel(false), UseInitArray(false), - DisableIntegratedAS(false), RelaxELFRelocations(false), - FunctionSections(false), DataSections(false), - IgnoreXCOFFVisibility(false), XCOFFTracebackTable(true), - UniqueSectionNames(true), UniqueBasicBlockSectionNames(false), - TrapUnreachable(false), NoTrapAfterNoreturn(false), TLSSize(0), - EmulatedTLS(false), ExplicitEmulatedTLS(false), EnableIPRA(false), + LowerGlobalDtorsViaCxaAtExit(false), DisableIntegratedAS(false), + RelaxELFRelocations(false), FunctionSections(false), + DataSections(false), IgnoreXCOFFVisibility(false), + XCOFFTracebackTable(true), UniqueSectionNames(true), + UniqueBasicBlockSectionNames(false), TrapUnreachable(false), + NoTrapAfterNoreturn(false), TLSSize(0), EmulatedTLS(false), + ExplicitEmulatedTLS(false), EnableIPRA(false), EmitStackSizeSection(false), EnableMachineOutliner(false), EnableMachineFunctionSplitter(false), SupportsDefaultOutlining(false), EmitAddrsig(false), EmitCallSiteInfo(false), @@ -245,6 +246,10 @@ /// constructors. unsigned UseInitArray : 1; + /// Use __cxa_atexit to register global destructors; determines how + /// llvm.global_dtors is lowered. + unsigned LowerGlobalDtorsViaCxaAtExit : 1; + /// Disable the integrated assembler. unsigned DisableIntegratedAS : 1; 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/CommandFlags.cpp b/llvm/lib/CodeGen/CommandFlags.cpp --- a/llvm/lib/CodeGen/CommandFlags.cpp +++ b/llvm/lib/CodeGen/CommandFlags.cpp @@ -74,6 +74,7 @@ CGOPT(bool, StackRealign) CGOPT(std::string, TrapFuncName) CGOPT(bool, UseCtors) +CGOPT(bool, LowerGlobalDtorsViaCxaAtExit) CGOPT(bool, RelaxELFRelocations) CGOPT_EXP(bool, DataSections) CGOPT_EXP(bool, FunctionSections) @@ -341,6 +342,12 @@ cl::init(false)); CGBINDOPT(UseCtors); + static cl::opt LowerGlobalDtorsViaCxaAtExit( + "lower-global-dtors-via-cxa-atexit", + cl::desc("Lower llvm.global_dtors (global destructors) via __cxa_atexit"), + cl::init(true)); + CGBINDOPT(LowerGlobalDtorsViaCxaAtExit); + static cl::opt RelaxELFRelocations( "relax-elf-relocations", cl::desc( @@ -524,6 +531,7 @@ Options.GuaranteedTailCallOpt = getEnableGuaranteedTailCallOpt(); Options.StackSymbolOrdering = getStackSymbolOrdering(); Options.UseInitArray = !getUseCtors(); + Options.LowerGlobalDtorsViaCxaAtExit = getLowerGlobalDtorsViaCxaAtExit(); Options.RelaxELFRelocations = getRelaxELFRelocations(); Options.DataSections = getExplicitDataSections().getValueOr(TheTriple.hasDefaultDataSections()); 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 @@ -1176,6 +1176,14 @@ dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4; } +MCSection *TargetLoweringObjectFileMachO::getStaticDtorSection( + unsigned Priority, const MCSymbol *KeySym) const { + // TODO(yln): Remove -lower-global-dtors-via-cxa-atexit fallback flag + // (LowerGlobalDtorsViaCxaAtExit) and issue a fatal error here. + // report_fatal_error("@llvm.global_dtors should have been lowered already"); + return StaticDtorSection; +} + void TargetLoweringObjectFileMachO::emitModuleMetadata(MCStreamer &Streamer, Module &M) const { // Emit the linker options if present. @@ -2175,8 +2183,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,12 @@ 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() && + TM->Options.LowerGlobalDtorsViaCxaAtExit) + 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 @@ -230,6 +230,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 @@ -1,9 +1,15 @@ ; RUN: llc < %s -mtriple=arm-apple-darwin | FileCheck %s -check-prefix=DARWIN +; RUN: llc < %s -mtriple=arm-apple-darwin -lower-global-dtors-via-cxa-atexit=false | FileCheck %s -check-prefix=DARWIN-OLD ; 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: __mod_term_func + +; DARWIN-OLD: .section __DATA,__mod_init_func,mod_init_funcs +; DARWIN-OLD: .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