forked from OSchip/llvm-project
[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. llvm-svn: 233885
This commit is contained in:
parent
768ccc4bfa
commit
b1cd98a18d
|
@ -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
|
|
@ -1,5 +1,6 @@
|
|||
add_llvm_library(LLVMOrcJIT
|
||||
CloneSubModule.cpp
|
||||
ExecutionUtils.cpp
|
||||
IndirectionUtils.cpp
|
||||
OrcMCJITReplacement.cpp
|
||||
OrcTargetSupport.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<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.
|
|
@ -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)
|
|
@ -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
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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[]);
|
||||
|
|
Loading…
Reference in New Issue