You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
431 lines
13 KiB
431 lines
13 KiB
/******************************************************************************
|
|
* Copyright (C) 2015 Dibyendu Majumdar
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
******************************************************************************/
|
|
#include "ravi_llvmcodegen.h"
|
|
|
|
/*
|
|
* Implementation Notes:
|
|
* Each Lua function is compiled into an LLVM Module/Function
|
|
* This strategy allows functions to be garbage collected as normal by Lua
|
|
*/
|
|
|
|
namespace ravi {
|
|
|
|
// This is just to avoid initailizing LLVM repeatedly -
|
|
// see below
|
|
static std::atomic_int init;
|
|
|
|
RaviJITState *RaviJITFunctionImpl::owner() const { return owner_; }
|
|
|
|
RaviJITStateImpl::RaviJITStateImpl()
|
|
: context_(llvm::getGlobalContext()), auto_(false), enabled_(true),
|
|
opt_level_(2), size_level_(0) {
|
|
// LLVM needs to be initialized else
|
|
// ExecutionEngine cannot be created
|
|
// This should ideally be an atomic check but because LLVM docs
|
|
// say that it is okay to call these functions more than once we
|
|
// do not bother
|
|
if (init == 0) {
|
|
llvm::InitializeNativeTarget();
|
|
llvm::InitializeNativeTargetAsmPrinter();
|
|
llvm::InitializeNativeTargetAsmParser();
|
|
init++;
|
|
}
|
|
triple_ = llvm::sys::getProcessTriple();
|
|
#ifdef _WIN32
|
|
// On Windows we get compilation error saying incompatible object format
|
|
// Reading posts on mailing lists I found that the issue is that COEFF
|
|
// format is not supported and therefore we need to set -elf as the object
|
|
// format
|
|
triple_ += "-elf";
|
|
#endif
|
|
types_ = new LuaLLVMTypes(context_);
|
|
}
|
|
|
|
RaviJITStateImpl::~RaviJITStateImpl() {
|
|
std::vector<RaviJITFunction *> todelete;
|
|
for (auto f = std::begin(functions_); f != std::end(functions_); f++) {
|
|
todelete.push_back(f->second);
|
|
}
|
|
// delete all the compiled objects
|
|
for (int i = 0; i < todelete.size(); i++) {
|
|
delete todelete[i];
|
|
}
|
|
delete types_;
|
|
}
|
|
|
|
void RaviJITStateImpl::addGlobalSymbol(const std::string &name, void *address) {
|
|
llvm::sys::DynamicLibrary::AddSymbol(name, address);
|
|
}
|
|
|
|
void RaviJITStateImpl::dump() {
|
|
types_->dump();
|
|
for (auto f : functions_) {
|
|
f.second->dump();
|
|
}
|
|
}
|
|
|
|
RaviJITFunction *
|
|
RaviJITStateImpl::createFunction(llvm::FunctionType *type,
|
|
llvm::GlobalValue::LinkageTypes linkage,
|
|
const std::string &name) {
|
|
RaviJITFunction *f = new RaviJITFunctionImpl(this, type, linkage, name);
|
|
functions_[name] = f;
|
|
return f;
|
|
}
|
|
|
|
void RaviJITStateImpl::deleteFunction(const std::string &name) {
|
|
functions_.erase(name);
|
|
// This is called when RaviJITFunction is deleted
|
|
}
|
|
|
|
RaviJITFunctionImpl::RaviJITFunctionImpl(
|
|
RaviJITStateImpl *owner, llvm::FunctionType *type,
|
|
llvm::GlobalValue::LinkageTypes linkage, const std::string &name)
|
|
: owner_(owner), name_(name), engine_(nullptr), module_(nullptr),
|
|
function_(nullptr), ptr_(nullptr) {
|
|
// MCJIT treats each module as a compilation unit
|
|
// To enable function level life cycle we create a
|
|
// module per function
|
|
std::string moduleName = "ravi_module_" + name;
|
|
module_ = new llvm::Module(moduleName, owner->context());
|
|
#if defined(_WIN32)
|
|
// On Windows we get error saying incompatible object format
|
|
// Reading posts on mailining lists I found that the issue is that COEFF
|
|
// format is not supported and therefore we need to set
|
|
// -elf as the object format
|
|
module_->setTargetTriple(owner->triple());
|
|
#endif
|
|
|
|
function_ = llvm::Function::Create(type, linkage, name, module_);
|
|
std::string errStr;
|
|
#if LLVM_VERSION_MINOR > 5
|
|
// LLVM 3.6.0 change
|
|
std::unique_ptr<llvm::Module> module(module_);
|
|
engine_ = llvm::EngineBuilder(std::move(module))
|
|
.setEngineKind(llvm::EngineKind::JIT)
|
|
.setErrorStr(&errStr)
|
|
.create();
|
|
#else
|
|
engine_ = llvm::EngineBuilder(module_)
|
|
.setEngineKind(llvm::EngineKind::JIT)
|
|
.setUseMCJIT(true)
|
|
.setErrorStr(&errStr)
|
|
.create();
|
|
#endif
|
|
if (!engine_) {
|
|
fprintf(stderr, "Could not create ExecutionEngine: %s\n", errStr.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
RaviJITFunctionImpl::~RaviJITFunctionImpl() {
|
|
// Remove this function from parent
|
|
owner_->deleteFunction(name_);
|
|
if (engine_)
|
|
delete engine_;
|
|
else if (module_)
|
|
// if engine was created then we don't need to delete the
|
|
// module as it would have been deleted by the engine
|
|
delete module_;
|
|
}
|
|
|
|
void *RaviJITFunctionImpl::compile() {
|
|
|
|
// We use the PassManagerBuilder to setup optimization
|
|
// passes - the PassManagerBuilder allows easy configuration of
|
|
// typical C/C++ passes corresponding to O0, O1, O2, and O3 compiler options
|
|
llvm::PassManagerBuilder pmb;
|
|
pmb.OptLevel = owner_->get_optlevel();
|
|
pmb.SizeLevel = owner_->get_sizelevel();
|
|
|
|
{
|
|
// Create a function pass manager for this engine
|
|
std::unique_ptr<llvm::FunctionPassManager> FPM(
|
|
new llvm::FunctionPassManager(module_));
|
|
|
|
// Set up the optimizer pipeline. Start with registering info about how the
|
|
// target lays out data structures.
|
|
#if LLVM_VERSION_MINOR > 5
|
|
// LLVM 3.6.0 change
|
|
module_->setDataLayout(engine_->getDataLayout());
|
|
FPM->add(new llvm::DataLayoutPass());
|
|
#else
|
|
// LLVM 3.5.0
|
|
auto target_layout = engine_->getTargetMachine()->getDataLayout();
|
|
module_->setDataLayout(target_layout);
|
|
FPM->add(new llvm::DataLayoutPass(*engine_->getDataLayout()));
|
|
#endif
|
|
pmb.populateFunctionPassManager(*FPM);
|
|
FPM->doInitialization();
|
|
FPM->run(*function_);
|
|
}
|
|
|
|
{
|
|
std::unique_ptr<llvm::PassManager> MPM(new llvm::PassManager());
|
|
pmb.populateModulePassManager(*MPM);
|
|
MPM->run(*module_);
|
|
}
|
|
|
|
if (ptr_)
|
|
return ptr_;
|
|
if (!function_ || !engine_)
|
|
return NULL;
|
|
|
|
// Upon creation, MCJIT holds a pointer to the Module object
|
|
// that it received from EngineBuilder but it does not immediately
|
|
// generate code for this module. Code generation is deferred
|
|
// until either the MCJIT::finalizeObject method is called
|
|
// explicitly or a function such as MCJIT::getPointerToFunction
|
|
// is called which requires the code to have been generated.
|
|
engine_->finalizeObject();
|
|
ptr_ = engine_->getPointerToFunction(function_);
|
|
return ptr_;
|
|
}
|
|
|
|
llvm::Constant *
|
|
RaviJITFunctionImpl::addExternFunction(llvm::FunctionType *type, void *address,
|
|
const std::string &name) {
|
|
llvm::Function *f = llvm::Function::Create(
|
|
type, llvm::Function::ExternalLinkage, name, module_);
|
|
// We should have been able to call
|
|
// engine_->addGlobalMapping() but this doesn't work
|
|
// See http://lists.cs.uiuc.edu/pipermail/llvmdev/2014-April/071856.html
|
|
// See bug report http://llvm.org/bugs/show_bug.cgi?id=20656
|
|
// following will call DynamicLibrary::AddSymbol
|
|
owner_->addGlobalSymbol(name, address);
|
|
return f;
|
|
}
|
|
|
|
void RaviJITFunctionImpl::dump() { module_->dump(); }
|
|
|
|
std::unique_ptr<RaviJITState> RaviJITStateFactory::newJITState() {
|
|
return std::unique_ptr<RaviJITState>(new RaviJITStateImpl());
|
|
}
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include "lualib.h"
|
|
#include "lauxlib.h"
|
|
|
|
struct ravi_State {
|
|
ravi::RaviJITState *jit;
|
|
ravi::RaviCodeGenerator *code_generator;
|
|
};
|
|
|
|
// Initialize the JIT State and attach is to the
|
|
// Global Lua State
|
|
// If a JIT State already exists then this function
|
|
// will return -1
|
|
int raviV_initjit(struct lua_State *L) {
|
|
global_State *G = G(L);
|
|
if (G->ravi_state != NULL)
|
|
return -1;
|
|
ravi_State *jit = (ravi_State *)calloc(1, sizeof(ravi_State));
|
|
jit->jit = new ravi::RaviJITStateImpl();
|
|
jit->code_generator =
|
|
new ravi::RaviCodeGenerator((ravi::RaviJITStateImpl *)jit->jit);
|
|
G->ravi_state = jit;
|
|
return 0;
|
|
}
|
|
|
|
// Free up the JIT State
|
|
void raviV_close(struct lua_State *L) {
|
|
global_State *G = G(L);
|
|
if (G->ravi_state == NULL)
|
|
return;
|
|
delete G->ravi_state->code_generator;
|
|
delete G->ravi_state->jit;
|
|
free(G->ravi_state);
|
|
}
|
|
|
|
// Compile a Lua function
|
|
// If JIT is turned off then compilation is skipped
|
|
// Compilation occurs if either auto compilation is ON or
|
|
// a manual compilation request was made
|
|
// Returns true if compilation was successful
|
|
int raviV_compile(struct lua_State *L, struct Proto *p, int manual_request) {
|
|
global_State *G = G(L);
|
|
if (G->ravi_state == NULL)
|
|
return 0;
|
|
if (!G->ravi_state->jit->is_enabled()) {
|
|
return 0;
|
|
}
|
|
if (G->ravi_state->jit->is_auto() || manual_request)
|
|
G->ravi_state->code_generator->compile(L, p);
|
|
return p->ravi_jit.jit_status == 2;
|
|
}
|
|
|
|
// Free the JIT compiled function
|
|
// Note that this is called by the garbage collector
|
|
void raviV_freeproto(struct lua_State *L, struct Proto *p) {
|
|
if (p->ravi_jit.jit_status == 2) /* compiled */ {
|
|
ravi::RaviJITFunction *f =
|
|
reinterpret_cast<ravi::RaviJITFunction *>(p->ravi_jit.jit_data);
|
|
if (f)
|
|
delete f;
|
|
p->ravi_jit.jit_status = 3;
|
|
p->ravi_jit.jit_function = NULL;
|
|
p->ravi_jit.jit_data = NULL;
|
|
}
|
|
}
|
|
|
|
// Dump the LLVM IR
|
|
void raviV_dumpllvmir(struct lua_State *L, struct Proto *p) {
|
|
if (p->ravi_jit.jit_status == 2) /* compiled */ {
|
|
ravi::RaviJITFunction *f =
|
|
reinterpret_cast<ravi::RaviJITFunction *>(p->ravi_jit.jit_data);
|
|
if (f)
|
|
f->dump();
|
|
}
|
|
}
|
|
|
|
// Test if the given function is compiled
|
|
static int ravi_is_compiled(lua_State *L) {
|
|
int n = lua_gettop(L);
|
|
luaL_argcheck(L, n == 1, 1, "1 argument expected");
|
|
luaL_checktype(L, 1, LUA_TLCL);
|
|
void *p = (void *)lua_topointer(L, 1);
|
|
LClosure *l = (LClosure *)p;
|
|
lua_pushboolean(L, l->p->ravi_jit.jit_status == 2);
|
|
return 1;
|
|
}
|
|
|
|
// Try to JIT compile the given function
|
|
static int ravi_compile(lua_State *L) {
|
|
int n = lua_gettop(L);
|
|
luaL_argcheck(L, n == 1, 1, "1 argument expected");
|
|
luaL_checktype(L, 1, LUA_TLCL);
|
|
void *p = (void *)lua_topointer(L, 1);
|
|
LClosure *l = (LClosure *)p;
|
|
int result = raviV_compile(L, l->p, 1);
|
|
lua_pushboolean(L, result);
|
|
return 1;
|
|
}
|
|
|
|
// Dump Lua bytecode of the supplied function
|
|
static int ravi_dump_luacode(lua_State *L) {
|
|
int n = lua_gettop(L);
|
|
luaL_argcheck(L, n == 1, 1, "1 argument expected");
|
|
luaL_checktype(L, 1, LUA_TLCL);
|
|
ravi_dump_function(L);
|
|
return 0;
|
|
}
|
|
|
|
// Dump LLVM IR of the supplied function
|
|
// if it has been compiled
|
|
static int ravi_dump_llvmir(lua_State *L) {
|
|
int n = lua_gettop(L);
|
|
luaL_argcheck(L, n == 1, 1, "1 argument expected");
|
|
luaL_checktype(L, 1, LUA_TLCL);
|
|
void *p = (void *)lua_topointer(L, 1);
|
|
LClosure *l = (LClosure *)p;
|
|
raviV_dumpllvmir(L, l->p);
|
|
return 0;
|
|
}
|
|
|
|
// Turn on/off auto JIT compilation
|
|
static int ravi_auto(lua_State *L) {
|
|
global_State *G = G(L);
|
|
int n = lua_gettop(L);
|
|
bool value = false;
|
|
if (n == 1)
|
|
value = lua_toboolean(L, 1);
|
|
if (G->ravi_state == NULL)
|
|
lua_pushboolean(L, 0);
|
|
else
|
|
lua_pushboolean(L, G->ravi_state->jit->is_auto());
|
|
if (n == 1 && G->ravi_state)
|
|
G->ravi_state->jit->set_auto(value);
|
|
return 1;
|
|
}
|
|
|
|
// Turn on/off the JIT compiler
|
|
static int ravi_jitenable(lua_State *L) {
|
|
global_State *G = G(L);
|
|
int n = lua_gettop(L);
|
|
bool value = false;
|
|
if (n == 1)
|
|
value = lua_toboolean(L, 1);
|
|
if (G->ravi_state == NULL)
|
|
lua_pushboolean(L, 0);
|
|
else
|
|
lua_pushboolean(L, G->ravi_state->jit->is_enabled());
|
|
if (n == 1 && G->ravi_state)
|
|
G->ravi_state->jit->set_enabled(value);
|
|
return 1;
|
|
}
|
|
|
|
// Set LLVM optimization level
|
|
static int ravi_optlevel(lua_State *L) {
|
|
global_State *G = G(L);
|
|
int n = lua_gettop(L);
|
|
int value = 1;
|
|
if (n == 1)
|
|
value = lua_tointeger(L, 1);
|
|
if (G->ravi_state == NULL)
|
|
lua_pushinteger(L, 0);
|
|
else
|
|
lua_pushinteger(L, G->ravi_state->jit->get_optlevel());
|
|
if (n == 1 && G->ravi_state)
|
|
G->ravi_state->jit->set_optlevel(value);
|
|
return 1;
|
|
}
|
|
|
|
// Set LLVM code size level
|
|
static int ravi_sizelevel(lua_State *L) {
|
|
global_State *G = G(L);
|
|
int n = lua_gettop(L);
|
|
int value = 0;
|
|
if (n == 1)
|
|
value = lua_tointeger(L, 1);
|
|
if (G->ravi_state == NULL)
|
|
lua_pushinteger(L, 0);
|
|
else
|
|
lua_pushinteger(L, G->ravi_state->jit->get_sizelevel());
|
|
if (n == 1 && G->ravi_state)
|
|
G->ravi_state->jit->set_sizelevel(value);
|
|
return 1;
|
|
}
|
|
|
|
static const luaL_Reg ravilib[] = {{"iscompiled", ravi_is_compiled},
|
|
{"compile", ravi_compile},
|
|
{"dumplua", ravi_dump_luacode},
|
|
{"dumpllvm", ravi_dump_llvmir},
|
|
{"auto", ravi_auto},
|
|
{"jit", ravi_jitenable},
|
|
{"optlevel", ravi_optlevel},
|
|
{"sizelevel", ravi_sizelevel},
|
|
{NULL, NULL}};
|
|
|
|
LUAMOD_API int raviopen_llvmjit(lua_State *L) {
|
|
luaL_newlib(L, ravilib);
|
|
return 1;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|