[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
This commit is contained in:
Lang Hames 2015-04-02 04:34:45 +00:00
parent 0a4630d104
commit 14ef491582
7 changed files with 375 additions and 34 deletions

View File

@ -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 <vector>
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<CtorDtorIterator> getConstructors(const Module &M);
/// @brief Create an iterator range over the entries of the llvm.global_ctors
/// array.
iterator_range<CtorDtorIterator> getDestructors(const Module &M);
/// @brief Convenience class for recording constructor/destructor names for
/// later execution.
template <typename JITLayerT>
class CtorDtorRunner {
public:
/// @brief Construct a CtorDtorRunner for the given range using the given
/// name mangling function.
CtorDtorRunner(std::vector<std::string> 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<CtorDtorTy>(
static_cast<uintptr_t>(CtorDtorSym.getAddress()));
CtorDtor();
} else
Error = true;
return !Error;
}
private:
std::vector<std::string> 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 <typename MangleFtorT>
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 <typename PtrTy>
TargetAddress toTargetAddress(PtrTy* P) {
return static_cast<TargetAddress>(reinterpret_cast<uintptr_t>(P));
}
void addOverride(const std::string &Name, TargetAddress Addr) {
CXXRuntimeOverrides.insert(std::make_pair(Name, Addr));
}
StringMap<TargetAddress> CXXRuntimeOverrides;
typedef void (*DestructorPtr)(void*);
typedef std::pair<DestructorPtr, void*> CXXDestructorDataPair;
typedef std::vector<CXXDestructorDataPair> CXXDestructorDataPairList;
CXXDestructorDataPairList DSOHandleOverride;
static int CXAAtExitOverride(DestructorPtr Destructor, void *Arg,
void *DSOHandle);
};
} // End namespace orc.
} // End namespace llvm.
#endif // LLVM_EXECUTIONENGINE_ORC_EXECUTIONUTILS_H

View File

@ -1,5 +1,6 @@
add_llvm_library(LLVMOrcJIT
CloneSubModule.cpp
ExecutionUtils.cpp
IndirectionUtils.cpp
OrcMCJITReplacement.cpp
OrcTargetSupport.cpp

View File

@ -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<ConstantArray>(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<ConstantStruct>(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<Function>(FuncC)) {
Func = F;
break;
} else if (ConstantExpr *CE = dyn_cast_or_null<ConstantExpr>(FuncC)) {
if (CE->isCast())
FuncC = dyn_cast_or_null<ConstantExpr>(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<ConstantInt>(CS->getOperand(0));
Value *Data = CS->getOperand(2);
return Element(Priority->getZExtValue(), Func, Data);
}
iterator_range<CtorDtorIterator> getConstructors(const Module &M) {
const GlobalVariable *CtorsList = M.getNamedGlobal("llvm.global_ctors");
return make_range(CtorDtorIterator(CtorsList, false),
CtorDtorIterator(CtorsList, true));
}
iterator_range<CtorDtorIterator> 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<CXXDestructorDataPairList*>(DSOHandle);
CXXDestructorDataPairs.push_back(std::make_pair(Destructor, Arg));
return 0;
}
} // End namespace orc.
} // End namespace llvm.

View File

@ -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)

View File

@ -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
}

View File

@ -64,8 +64,6 @@ int llvm::runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]) {
}
typedef int (*MainFnPtr)(int, char*[]);
auto Main = reinterpret_cast<MainFnPtr>(
static_cast<uintptr_t>(MainSym.getAddress()));
auto Main = OrcLazyJIT::fromTargetAddress<MainFnPtr>(MainSym.getAddress());
return Main(ArgC, ArgV);
}

View File

@ -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 <typename PtrTy>
static PtrTy fromTargetAddress(orc::TargetAddress Addr) {
return reinterpret_cast<PtrTy>(static_cast<uintptr_t>(Addr));
}
ModuleHandleT addModule(std::unique_ptr<Module> M) {
// Attach a data-layout if one isn't already present.
if (M->getDataLayout().isDefault())
M->setDataLayout(*TM->getDataLayout());
std::vector<std::unique_ptr<Module>> 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<std::string> 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<std::unique_ptr<Module>> 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<CODLayerT> CtorRunner(std::move(CtorNames), H);
CtorRunner.runViaLayer(CODLayer);
IRStaticDestructorRunners.push_back(
orc::CtorDtorRunner<CODLayerT>(std::move(DtorNames), H));
return H;
}
orc::JITSymbol findSymbol(const std::string &Name) {
@ -99,6 +146,9 @@ private:
LazyEmitLayerT LazyEmitLayer;
std::unique_ptr<CompileCallbackMgr> CCMgr;
CODLayerT CODLayer;
orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides;
std::vector<orc::CtorDtorRunner<CODLayerT>> IRStaticDestructorRunners;
};
int runOrcLazyJIT(std::unique_ptr<Module> M, int ArgC, char* ArgV[]);