start work on Lua LLVM bindings

pull/81/head
Dibyendu Majumdar 9 years ago
parent e479658d58
commit b828dc43dc

@ -113,7 +113,8 @@ if (LLVM_JIT)
src/ravi_llvmcodegen.cpp src/ravi_llvmforprep.cpp src/ravi_llvmcomp.cpp
src/ravi_llvmreturn.cpp src/ravi_llvmload.cpp src/ravi_llvmforloop.cpp
src/ravi_llvmarith1.cpp src/ravi_llvmcall.cpp src/ravi_llvmtable.cpp
src/ravi_llvmarith2.cpp src/ravi_llvmtforcall.cpp src/ravi_llvmrest.cpp)
src/ravi_llvmarith2.cpp src/ravi_llvmtforcall.cpp src/ravi_llvmrest.cpp
src/ravi_llvmluaapi.cpp)
endif()
if (GCC_JIT)
set (GCC_JIT_SRCS src/ravi_gccjit.c src/ravi_gcctypes.c
@ -147,11 +148,8 @@ elseif(NOT WIN32)
set(EXTRA_LIBRARIES m dl readline)
endif()
#llvm_map_components_to_libnames(llvm_libs all)
#llvm_map_components_to_libnames(llvm_libs Support Core CodeGen AsmPrinter BitReader TransformUtils
# InstCombine ScalarOpts Analysis ipa Object ExecutionEngine MC MCParser MCJIT ProfileData ipo
# RuntimeDyld Target X86CodeGen X86AsmParser X86Disassembler X86AsmPrinter X86Desc X86Info X86Utils AsmParser)
# Enable minimal required LLVM components so that the
# the size of the resulting binary is manageable
if (LLVM_JIT)
llvm_map_components_to_libnames(llvm_libs
Analysis
@ -194,9 +192,6 @@ if (WIN32)
endif()
target_link_libraries(ravi ${EXTRA_LIBRARIES} ${llvm_libs} ${GCCJIT_LIBRARIES})
#add_library(ravis ${RAVI_HEADERS} ${LUA_LIB_SRCS} ${LUA_CORE_SRCS})
#target_link_libraries(ravis ${EXTRA_LIBRARIES} ${llvm_libs})
add_executable(lua src/lua.c)
target_link_libraries(lua ravi)
@ -213,12 +208,6 @@ if (LLVM_JIT)
target_link_libraries(test_llvm ravi)
endif()
if (GCC_JIT)
#GCC JIT playground
add_executable(test_gccjit tests/test_gccjit.c)
target_link_libraries(test_gccjit ravi)
endif()
add_executable(test_misc tests/test_misc.c)
if (LLVM_JIT)

@ -47,6 +47,11 @@ LUAMOD_API int (luaopen_package) (lua_State *L);
#define LUA_RAVILIBNAME "ravi"
LUAMOD_API int (raviopen_llvmjit)(lua_State *L);
#ifdef USE_LLVM
#define LUA_LLVMLIBNAME "llvm"
LUAMOD_API int (raviopen_llvmluaapi)(lua_State *L);
#endif
/* open all previous libraries */
LUALIB_API void (luaL_openlibs) (lua_State *L);

@ -992,6 +992,11 @@ private:
};
}
struct ravi_State {
ravi::RaviJITState *jit;
ravi::RaviCodeGenerator *code_generator;
};
#define RAVI_CODEGEN_FORPREP2 0
#endif

@ -0,0 +1,8 @@
LLVM Bindings for Lua/Ravi
==========================
As part of the Ravi Programming Language, it is my intention to provide a Lua 5.3 compatible LLVM binding.
This will allow Lua programmers to write their own JIT compilers in Lua!
Right now this is in early development so there is no documentation. But the Lua programs here
demonstrate the features available to date.

@ -0,0 +1,16 @@
assert(llvm)
context = llvm.context()
print(getmetatable(context).type)
irbuilder = llvm.irbuilder()
print(getmetatable(irbuilder).type)
types = llvm.types()
print 'Listing types'
for k,v in pairs(types) do
print('\t', k, getmetatable(v).type)
end

@ -129,6 +129,6 @@ If B > 0 then the number of values to be returned is simply B-1.
'``OP_RETURN``' calls `luaD_poscall() <http://www.lua.org/source/5.3/ldo.c.html#luaD_poscall>`_ which is responsible for copying return values to the caller - the first result is placed at the current ``closure``'s address. ``luaD_poscall()`` leaves ``L->top`` just past the last result that was copied.
Now if '``OP_RETURN``' is returning to a Lua function and if the number of return values expected was indeterminate - i.e. '``OP_CALL``' had operand C = 0, then ``L->top`` is left where ``luaD_poscall()`` placed it - at the top of the result list. This allows the '``OP_CALL``' instruction to figure out how many results were returned. If however '``OP_CALL``' had invoked with a value of C > 0 then the expected number of results is known, and in that case, ``L->top`` is reset to the calling function's ``C->top``.
If '``OP_RETURN``' is returning to a Lua function and if the number of return values expected was indeterminate - i.e. '``OP_CALL``' had operand C = 0, then ``L->top`` is left where ``luaD_poscall()`` placed it - just beyond the top of the result list. This allows the '``OP_CALL``' instruction to figure out how many results were returned. If however '``OP_CALL``' had invoked with a value of C > 0 then the expected number of results is known, and in that case, ``L->top`` is reset to the calling function's ``C->top``.
On the other hand, if ``luaV_execute()`` was called externally - then '``OP_RETURN``' leaves ``L->top`` unchanged - so it will continue to point to the top of the results list. This is probably because luaV_execute() does not have a way of informing callers how many values were returned; so presumably the caller can determine the number of results by inspecting ``L->top``.
If ``luaV_execute()`` was called externally then '``OP_RETURN``' leaves ``L->top`` unchanged - so it will continue to be just past the top of the results list. This is probably because luaV_execute() does not have a way of informing callers how many values were returned; so presumably the caller can determine the number of results by inspecting ``L->top``.

@ -51,6 +51,9 @@ static const luaL_Reg loadedlibs[] = {
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_DBLIBNAME, luaopen_debug},
{LUA_RAVILIBNAME, raviopen_llvmjit},
#ifdef USE_LLVM
{LUA_LLVMLIBNAME, raviopen_llvmluaapi},
#endif
#if defined(LUA_COMPAT_BITLIB)
{LUA_BITLIBNAME, luaopen_bit32},
#endif

@ -383,11 +383,6 @@ std::unique_ptr<RaviJITState> RaviJITStateFactory::newJITState() {
}
}
struct ravi_State {
ravi::RaviJITState *jit;
ravi::RaviCodeGenerator *code_generator;
};
// Initialize the JIT State and attach it to the
// Global Lua State
// If a JIT State already exists then this function

@ -0,0 +1,269 @@
/******************************************************************************
* 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.
******************************************************************************/
/*
This will hopefully eventually contain a Lua binding for LLVM.
*/
#include <ravijit.h>
#include "ravi_llvmcodegen.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "lauxlib.h"
#if LUA_VERSION_NUM < 502
// Forward compatibility with Lua 5.2
// Following is not defined in 5.1
#define lua_absindex(L, i) \
((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
#endif
#if (LUA_VERSION_NUM >= 502)
// backward compatibility with Lua 5.1
#undef lua_equal
#define lua_equal(L, idx1, idx2) lua_compare(L, (idx1), (idx2), LUA_OPEQ)
#undef lua_objlen
#define lua_objlen lua_rawlen
#endif
// The normal Lua metatable functions in C use string
// keys - these are expensive as the key needs to be
// converted to Lua string, hash code computed etc.
// Following implementations are taken from a post in
// Lua mailing list (http://lua-users.org/lists/lua-l/2010-11/msg00151.html)
// They use lightuserdata instead of strings to speed
// things up
// meta_key is the key assigned to the meta
// table of the userdata
static int l_newmetatable(lua_State *L, const char *meta_key) {
lua_pushlightuserdata(L, (void *)meta_key);
lua_rawget(L, LUA_REGISTRYINDEX);
if (!lua_isnil(L, -1)) // name already in use?
return 0; // leave previous value on top, but return 0
lua_pop(L, 1); // pop the nil value
lua_newtable(L); // create metatable
lua_pushlightuserdata(L, (void *)meta_key); // meta_key
lua_pushvalue(L, -2); // table
lua_rawset(L, LUA_REGISTRYINDEX); // assign table to meta_key in the registry
return 1;
}
// meta_key is the key assigned to the meta table of the userdata
static void l_getmetatable(lua_State *L, const char *meta_key) {
lua_pushlightuserdata(L, (void *)meta_key); // meta_key
lua_rawget(L, LUA_REGISTRYINDEX); // obtain the value associated with
// meta_key from registry
}
// arg_index is the position of userdata argument on the stack
// meta_key is the key assigned to the meta table of the userdata
static void *l_testudata(lua_State *L, int arg_index, const char *meta_key) {
void *p = lua_touserdata(L, arg_index);
if (p != NULL) { // value is a userdata?
if (lua_getmetatable(L, arg_index)) { // does it have a metatable?
lua_pushlightuserdata(L, (void *)meta_key); // meta_key
lua_rawget(
L,
LUA_REGISTRYINDEX); // get correct metatable associated with meta_key
if (lua_rawequal(L, -1, -2)) { // compare: does it have the correct mt?
lua_pop(L, 2); // remove both metatables
return p; // test ok
}
}
}
return NULL; /* to avoid warnings */
}
// arg_index is the position of userdata argument on the stack
// meta_key is the key assigned to the meta table of the userdata
static void *l_checkudata(lua_State *L, int arg_index, const char *meta_key) {
void *p = l_testudata(L, arg_index, meta_key);
if (p == NULL)
luaL_argerror(L, arg_index, meta_key);
return p;
}
// We need fixed pointer values for metatable keys
static const char *LLVM_context = "LLVMcontext";
static const char *LLVM_irbuilder = "LLVMirbuilder";
static const char *LLVM_module = "LLVMmodule";
static const char *LLVM_function = "LLVMfunction";
static const char *LLVM_type = "LLVMtype";
static const char *LLVM_value = "LLVMvalue";
static const char *LLVM_structtype = "LLVMstructtype";
static const char *LLVM_pointertype = "LLVMpointertype";
#define test_LLVM_irbuilder(L, idx) \
((IRBuilderHolder *)l_testudata(L, idx, LLVM_irbuilder))
#define check_LLVM_irbuilder(L, idx) \
((IRBuilderHolder *)l_checkudata(L, idx, LLVM_irbuilder))
#define test_LLVM_type(L, idx) ((TypeHolder *)l_testudata(L, idx, LLVM_type))
#define check_LLVM_type(L, idx) ((TypeHolder *)l_checkudata(L, idx, LLVM_type))
#define test_LLVM_context(L, idx) \
((ContextHolder *)l_testudata(L, idx, LLVM_context))
#define check_LLVM_context(L, idx) \
((ContextHolder *)l_checkudata(L, idx, LLVM_context))
struct ContextHolder {
llvm::LLVMContext *context;
};
struct IRBuilderHolder {
llvm::IRBuilder<> *builder;
};
struct TypeHolder {
llvm::Type *type;
};
struct ValueHolder {
llvm::Value *value;
};
static int alloc_LLVM_irbuilder(lua_State *L) {
global_State *G = G(L);
if (!G->ravi_state)
return 0;
ravi::RaviJITStateImpl *jit = (ravi::RaviJITStateImpl *)G->ravi_state->jit;
if (!jit)
return 0;
IRBuilderHolder *builder =
(IRBuilderHolder *)lua_newuserdata(L, sizeof(IRBuilderHolder));
builder->builder = new llvm::IRBuilder<>(jit->context());
l_getmetatable(L, LLVM_irbuilder);
lua_setmetatable(L, -2);
return 1;
}
static int collect_LLVM_irbuilder(lua_State *L) {
global_State *G = G(L);
if (!G->ravi_state)
return 0;
ravi::RaviJITStateImpl *jit = (ravi::RaviJITStateImpl *)G->ravi_state->jit;
if (!jit)
return 0;
IRBuilderHolder *builder = check_LLVM_irbuilder(L, 1);
if (builder->builder) {
delete builder->builder;
builder->builder = nullptr;
fprintf(stderr, "collected IRBuilder\n");
}
return 0;
}
static void alloc_LLVM_type(lua_State *L, ravi::RaviJITStateImpl *jit,
llvm::Type *t) {
TypeHolder *tt = (TypeHolder *)lua_newuserdata(L, sizeof(TypeHolder));
l_getmetatable(L, LLVM_type);
lua_setmetatable(L, -2);
tt->type = t;
}
static void alloc_LLVM_context(lua_State *L, ravi::RaviJITStateImpl *jit,
llvm::LLVMContext *c) {
ContextHolder *h = (ContextHolder *)lua_newuserdata(L, sizeof(ContextHolder));
l_getmetatable(L, LLVM_context);
lua_setmetatable(L, -2);
h->context = c;
}
static int get_standard_types(lua_State *L) {
global_State *G = G(L);
if (!G->ravi_state)
return 0;
ravi::RaviJITStateImpl *jit = (ravi::RaviJITStateImpl *)G->ravi_state->jit;
if (!jit)
return 0;
lua_newtable(L);
alloc_LLVM_type(L, jit, jit->types()->C_intT);
lua_setfield(L, -2, "int");
alloc_LLVM_type(L, jit, jit->types()->lua_NumberT);
lua_setfield(L, -2, "lua_Number");
alloc_LLVM_type(L, jit, jit->types()->lua_IntegerT);
lua_setfield(L, -2, "lua_Integer");
alloc_LLVM_type(L, jit, jit->types()->lu_byteT);
lua_setfield(L, -2, "lu_byte");
return 1;
}
static int get_llvm_context(lua_State *L) {
global_State *G = G(L);
if (!G->ravi_state)
return 0;
ravi::RaviJITStateImpl *jit = (ravi::RaviJITStateImpl *)G->ravi_state->jit;
if (!jit)
return 0;
alloc_LLVM_context(L, jit, &jit->context());
return 1;
}
static const luaL_Reg llvmlib[] = {{"types", get_standard_types},
{"context", get_llvm_context},
{"irbuilder", alloc_LLVM_irbuilder},
{NULL, NULL}};
LUAMOD_API int raviopen_llvmluaapi(lua_State *L) {
l_newmetatable(L, LLVM_irbuilder);
lua_pushstring(L, LLVM_irbuilder);
lua_setfield(L, -2, "type");
lua_pushcfunction(L, collect_LLVM_irbuilder);
lua_setfield(L, -2, "__gc");
lua_pop(L, 1);
l_newmetatable(L, LLVM_type);
lua_pushstring(L, LLVM_type);
lua_setfield(L, -2, "type");
lua_pop(L, 1);
l_newmetatable(L, LLVM_context);
lua_pushstring(L, LLVM_context);
lua_setfield(L, -2, "type");
lua_pop(L, 1);
luaL_newlib(L, llvmlib);
return 1;
}
#ifdef __cplusplus
}
#endif
Loading…
Cancel
Save