From: Lang Hames Date: Thu, 2 Apr 2015 04:34:45 +0000 (+0000) Subject: [Orc] Add support classes for inspecting and running C++ static ctor/dtors, and X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=14ef491582a945b98f1c16648e008335fdf5952c [Orc] Add support classes for inspecting and running C++ static ctor/dtors, and use these to add support for C++ static ctors/dtors to the Orc-lazy JIT in LLI. Replace the trivial_retval_1 regression test - the new 'hello' test is covering strictly more code. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@233885 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h b/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h new file mode 100644 index 00000000000..c10508cc8a6 --- /dev/null +++ b/include/llvm/ExecutionEngine/Orc/ExecutionUtils.h @@ -0,0 +1,182 @@ +//===-- ExecutionUtils.h - Utilities for executing code in Orc --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Contains utilities for executing code in Orc. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_EXECUTIONUTILS_H +#define LLVM_EXECUTIONENGINE_ORC_EXECUTIONUTILS_H + +#include "JITSymbol.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ExecutionEngine/RuntimeDyld.h" +#include + +namespace llvm { + +class ConstantArray; +class GlobalVariable; +class Function; +class Module; +class Value; + +namespace orc { + +/// @brief This iterator provides a convenient way to iterate over the elements +/// of an llvm.global_ctors/llvm.global_dtors instance. +/// +/// The easiest way to get hold of instances of this class is to use the +/// getConstructors/getDestructors functions. +class CtorDtorIterator { +public: + + /// @brief Accessor for an element of the global_ctors/global_dtors array. + /// + /// This class provides a read-only view of the element with any casts on + /// the function stripped away. + struct Element { + Element(unsigned Priority, const Function *Func, const Value *Data) + : Priority(Priority), Func(Func), Data(Data) {} + + unsigned Priority; + const Function *Func; + const Value *Data; + }; + + /// @brief Construct an iterator instance. If End is true then this iterator + /// acts as the end of the range, otherwise it is the beginning. + CtorDtorIterator(const GlobalVariable *GV, bool End); + + /// @brief Test iterators for equality. + bool operator==(const CtorDtorIterator &Other) const; + + /// @brief Test iterators for inequality. + bool operator!=(const CtorDtorIterator &Other) const; + + /// @brief Pre-increment iterator. + CtorDtorIterator& operator++(); + + /// @brief Post-increment iterator. + CtorDtorIterator operator++(int); + + /// @brief Dereference iterator. The resulting value provides a read-only view + /// of this element of the global_ctors/global_dtors list. + Element operator*() const; + +private: + const ConstantArray *InitList; + unsigned I; +}; + +/// @brief Create an iterator range over the entries of the llvm.global_ctors +/// array. +iterator_range getConstructors(const Module &M); + +/// @brief Create an iterator range over the entries of the llvm.global_ctors +/// array. +iterator_range getDestructors(const Module &M); + +/// @brief Convenience class for recording constructor/destructor names for +/// later execution. +template +class CtorDtorRunner { +public: + + /// @brief Construct a CtorDtorRunner for the given range using the given + /// name mangling function. + CtorDtorRunner(std::vector CtorDtorNames, + typename JITLayerT::ModuleSetHandleT H) + : CtorDtorNames(std::move(CtorDtorNames)), H(H) {} + + /// @brief Run the recorded constructors/destructors through the given JIT + /// layer. + bool runViaLayer(JITLayerT &JITLayer) const { + typedef void (*CtorDtorTy)(); + + bool Error = false; + for (const auto &CtorDtorName : CtorDtorNames) + if (auto CtorDtorSym = JITLayer.findSymbolIn(H, CtorDtorName, false)) { + CtorDtorTy CtorDtor = + reinterpret_cast( + static_cast(CtorDtorSym.getAddress())); + CtorDtor(); + } else + Error = true; + return !Error; + } + +private: + std::vector CtorDtorNames; + typename JITLayerT::ModuleSetHandleT H; +}; + +/// @brief Support class for static dtor execution. For hosted (in-process) JITs +/// only! +/// +/// If a __cxa_atexit function isn't found C++ programs that use static +/// destructors will fail to link. However, we don't want to use the host +/// process's __cxa_atexit, because it will schedule JIT'd destructors to run +/// after the JIT has been torn down, which is no good. This class makes it easy +/// to override __cxa_atexit (and the related __dso_handle). +/// +/// To use, clients should manually call searchOverrides from their symbol +/// resolver. This should generally be done after attempting symbol resolution +/// inside the JIT, but before searching the host process's symbol table. When +/// the client determines that destructors should be run (generally at JIT +/// teardown or after a return from main), the runDestructors method should be +/// called. +class LocalCXXRuntimeOverrides { +public: + + /// Create a runtime-overrides class. + template + LocalCXXRuntimeOverrides(const MangleFtorT &Mangle) { + addOverride(Mangle("__dso_handle"), toTargetAddress(&DSOHandleOverride)); + addOverride(Mangle("__cxa_atexit"), toTargetAddress(&CXAAtExitOverride)); + } + + /// Search overrided symbols. + RuntimeDyld::SymbolInfo searchOverrides(const std::string &Name) { + auto I = CXXRuntimeOverrides.find(Name); + if (I != CXXRuntimeOverrides.end()) + return RuntimeDyld::SymbolInfo(I->second, JITSymbolFlags::Exported); + return nullptr; + } + + /// Run any destructors recorded by the overriden __cxa_atexit function + /// (CXAAtExitOverride). + void runDestructors(); + +private: + + template + TargetAddress toTargetAddress(PtrTy* P) { + return static_cast(reinterpret_cast(P)); + } + + void addOverride(const std::string &Name, TargetAddress Addr) { + CXXRuntimeOverrides.insert(std::make_pair(Name, Addr)); + } + + StringMap CXXRuntimeOverrides; + + typedef void (*DestructorPtr)(void*); + typedef std::pair CXXDestructorDataPair; + typedef std::vector CXXDestructorDataPairList; + CXXDestructorDataPairList DSOHandleOverride; + static int CXAAtExitOverride(DestructorPtr Destructor, void *Arg, + void *DSOHandle); +}; + +} // End namespace orc. +} // End namespace llvm. + +#endif // LLVM_EXECUTIONENGINE_ORC_EXECUTIONUTILS_H diff --git a/lib/ExecutionEngine/Orc/CMakeLists.txt b/lib/ExecutionEngine/Orc/CMakeLists.txt index b0a8445c84d..b38b4594057 100644 --- a/lib/ExecutionEngine/Orc/CMakeLists.txt +++ b/lib/ExecutionEngine/Orc/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_library(LLVMOrcJIT CloneSubModule.cpp + ExecutionUtils.cpp IndirectionUtils.cpp OrcMCJITReplacement.cpp OrcTargetSupport.cpp diff --git a/lib/ExecutionEngine/Orc/ExecutionUtils.cpp b/lib/ExecutionEngine/Orc/ExecutionUtils.cpp new file mode 100644 index 00000000000..b7220dba88e --- /dev/null +++ b/lib/ExecutionEngine/Orc/ExecutionUtils.cpp @@ -0,0 +1,102 @@ +//===---- ExecutionUtils.cpp - Utilities for executing functions in Orc ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" + +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Module.h" + +namespace llvm { +namespace orc { + +CtorDtorIterator::CtorDtorIterator(const GlobalVariable *GV, bool End) + : InitList( + GV ? dyn_cast_or_null(GV->getInitializer()) : nullptr), + I((InitList && End) ? InitList->getNumOperands() : 0) { +} + +bool CtorDtorIterator::operator==(const CtorDtorIterator &Other) const { + assert(InitList == Other.InitList && "Incomparable iterators."); + return I == Other.I; +} + +bool CtorDtorIterator::operator!=(const CtorDtorIterator &Other) const { + return !(*this == Other); +} + +CtorDtorIterator& CtorDtorIterator::operator++() { + ++I; + return *this; +} + +CtorDtorIterator CtorDtorIterator::operator++(int) { + CtorDtorIterator Temp = *this; + ++I; + return Temp; +} + +CtorDtorIterator::Element CtorDtorIterator::operator*() const { + ConstantStruct *CS = dyn_cast(InitList->getOperand(I)); + assert(CS && "Unrecognized type in llvm.global_ctors/llvm.global_dtors"); + + Constant *FuncC = CS->getOperand(1); + Function *Func = nullptr; + + // Extract function pointer, pulling off any casts. + while (FuncC) { + if (Function *F = dyn_cast_or_null(FuncC)) { + Func = F; + break; + } else if (ConstantExpr *CE = dyn_cast_or_null(FuncC)) { + if (CE->isCast()) + FuncC = dyn_cast_or_null(CE->getOperand(0)); + else + break; + } else { + // This isn't anything we recognize. Bail out with Func left set to null. + break; + } + } + + ConstantInt *Priority = dyn_cast(CS->getOperand(0)); + Value *Data = CS->getOperand(2); + return Element(Priority->getZExtValue(), Func, Data); +} + +iterator_range getConstructors(const Module &M) { + const GlobalVariable *CtorsList = M.getNamedGlobal("llvm.global_ctors"); + return make_range(CtorDtorIterator(CtorsList, false), + CtorDtorIterator(CtorsList, true)); +} + +iterator_range getDestructors(const Module &M) { + const GlobalVariable *DtorsList = M.getNamedGlobal("llvm.global_dtors"); + return make_range(CtorDtorIterator(DtorsList, false), + CtorDtorIterator(DtorsList, true)); +} + +void LocalCXXRuntimeOverrides::runDestructors() { + auto& CXXDestructorDataPairs = DSOHandleOverride; + for (auto &P : CXXDestructorDataPairs) + P.first(P.second); + CXXDestructorDataPairs.clear(); +} + +int LocalCXXRuntimeOverrides::CXAAtExitOverride(DestructorPtr Destructor, + void *Arg, void *DSOHandle) { + auto& CXXDestructorDataPairs = + *reinterpret_cast(DSOHandle); + CXXDestructorDataPairs.push_back(std::make_pair(Destructor, Arg)); + return 0; +} + +} // End namespace orc. +} // End namespace llvm. diff --git a/test/ExecutionEngine/OrcLazy/hello.ll b/test/ExecutionEngine/OrcLazy/hello.ll new file mode 100644 index 00000000000..dcc6da06386 --- /dev/null +++ b/test/ExecutionEngine/OrcLazy/hello.ll @@ -0,0 +1,34 @@ +; RUN: lli -jit-kind=orc-lazy %s | FileCheck %s +; +; CHECK: Hello +; CHECK-NEXT: Goodbye + +%class.Foo = type { i8 } + +@f = global %class.Foo zeroinitializer, align 1 +@__dso_handle = external global i8 +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @_GLOBAL__sub_I_hello.cpp, i8* null }] +@str = private unnamed_addr constant [6 x i8] c"Hello\00" +@str2 = private unnamed_addr constant [8 x i8] c"Goodbye\00" + +define linkonce_odr void @_ZN3FooD1Ev(%class.Foo* nocapture readnone %this) unnamed_addr align 2 { +entry: + %puts.i = tail call i32 @puts(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @str2, i64 0, i64 0)) + ret void +} + +declare i32 @__cxa_atexit(void (i8*)*, i8*, i8*) + +define i32 @main(i32 %argc, i8** nocapture readnone %argv) { +entry: + ret i32 0 +} + +define internal void @_GLOBAL__sub_I_hello.cpp() { +entry: + %puts.i.i.i = tail call i32 @puts(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @str, i64 0, i64 0)) + %0 = tail call i32 @__cxa_atexit(void (i8*)* bitcast (void (%class.Foo*)* @_ZN3FooD1Ev to void (i8*)*), i8* getelementptr inbounds (%class.Foo, %class.Foo* @f, i64 0, i32 0), i8* @__dso_handle) + ret void +} + +declare i32 @puts(i8* nocapture readonly) diff --git a/test/ExecutionEngine/OrcLazy/trivial_retval_1.ll b/test/ExecutionEngine/OrcLazy/trivial_retval_1.ll deleted file mode 100644 index 701f22cd390..00000000000 --- a/test/ExecutionEngine/OrcLazy/trivial_retval_1.ll +++ /dev/null @@ -1,26 +0,0 @@ -; RUN: sh -c 'lli -jit-kind=orc-lazy %s; echo $?' | FileCheck %s -; CHECK: {{^30$}} -define i32 @baz() { -entry: - ret i32 2 -} - -define i32 @bar() { -entry: - %call = call i32 @baz() - %mul = mul nsw i32 3, %call - ret i32 %mul -} - -define i32 @foo() { -entry: - %call = call i32 @bar() - %mul = mul nsw i32 5, %call - ret i32 %mul -} - -define i32 @main(i32 %argc, i8** %argv) { -entry: - %call = call i32 @foo() - ret i32 %call -} diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp index d08008da6e0..236de7c31d2 100644 --- a/tools/lli/OrcLazyJIT.cpp +++ b/tools/lli/OrcLazyJIT.cpp @@ -64,8 +64,6 @@ int llvm::runOrcLazyJIT(std::unique_ptr M, int ArgC, char* ArgV[]) { } typedef int (*MainFnPtr)(int, char*[]); - auto Main = reinterpret_cast( - static_cast(MainSym.getAddress())); - + auto Main = OrcLazyJIT::fromTargetAddress(MainSym.getAddress()); return Main(ArgC, ArgV); } diff --git a/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h index e323714646b..8e6f400f0c5 100644 --- a/tools/lli/OrcLazyJIT.h +++ b/tools/lli/OrcLazyJIT.h @@ -18,6 +18,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" #include "llvm/ExecutionEngine/Orc/CompileUtils.h" +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" #include "llvm/ExecutionEngine/Orc/LazyEmittingLayer.h" #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" @@ -53,22 +54,68 @@ public: CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)), LazyEmitLayer(CompileLayer), CCMgr(BuildCallbackMgr(CompileLayer, CCMgrMemMgr, Context)), - CODLayer(LazyEmitLayer, *CCMgr) { } + CODLayer(LazyEmitLayer, *CCMgr), + CXXRuntimeOverrides([this](const std::string &S) { return mangle(S); }) {} + + ~OrcLazyJIT() { + // Run any destructors registered with __cxa_atexit. + CXXRuntimeOverrides.runDestructors(); + // Run any IR destructors. + for (auto &DtorRunner : IRStaticDestructorRunners) + DtorRunner.runViaLayer(CODLayer); + } + + template + static PtrTy fromTargetAddress(orc::TargetAddress Addr) { + return reinterpret_cast(static_cast(Addr)); + } ModuleHandleT addModule(std::unique_ptr M) { // Attach a data-layout if one isn't already present. if (M->getDataLayout().isDefault()) M->setDataLayout(*TM->getDataLayout()); - std::vector> S; - S.push_back(std::move(M)); + // Record the static constructors and destructors. We have to do this before + // we hand over ownership of the module to the JIT. + std::vector CtorNames, DtorNames; + for (auto Ctor : orc::getConstructors(*M)) + CtorNames.push_back(mangle(Ctor.Func->getName())); + for (auto Dtor : orc::getDestructors(*M)) + DtorNames.push_back(mangle(Dtor.Func->getName())); + + // Symbol resolution order: + // 1) Search the JIT symbols. + // 2) Check for C++ runtime overrides. + // 3) Search the host process (LLI)'s symbol table. auto FallbackLookup = - [](const std::string &Name) { + [this](const std::string &Name) { + + if (auto Sym = CODLayer.findSymbol(Name, true)) + return RuntimeDyld::SymbolInfo(Sym.getAddress(), Sym.getFlags()); + + if (auto Sym = CXXRuntimeOverrides.searchOverrides(Name)) + return Sym; + if (auto Addr = RTDyldMemoryManager::getSymbolAddressInProcess(Name)) return RuntimeDyld::SymbolInfo(Addr, JITSymbolFlags::Exported); + return RuntimeDyld::SymbolInfo(nullptr); }; - return CODLayer.addModuleSet(std::move(S), std::move(FallbackLookup)); + + // Add the module to the JIT. + std::vector> S; + S.push_back(std::move(M)); + auto H = CODLayer.addModuleSet(std::move(S), std::move(FallbackLookup)); + + // Run the static constructors, and save the static destructor runner for + // execution when the JIT is torn down. + orc::CtorDtorRunner CtorRunner(std::move(CtorNames), H); + CtorRunner.runViaLayer(CODLayer); + + IRStaticDestructorRunners.push_back( + orc::CtorDtorRunner(std::move(DtorNames), H)); + + return H; } orc::JITSymbol findSymbol(const std::string &Name) { @@ -99,6 +146,9 @@ private: LazyEmitLayerT LazyEmitLayer; std::unique_ptr CCMgr; CODLayerT CODLayer; + + orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides; + std::vector> IRStaticDestructorRunners; }; int runOrcLazyJIT(std::unique_ptr M, int ArgC, char* ArgV[]);