gambas-source-code/main/gbx/gbx_jit.c
gambas 85ded796c9 Fix release of return value of JIT methods called from non-JIT methods.
[INTERPRETER]
* BUG: Fix release of return value of JIT methods called from non-JIT methods.
2020-01-09 04:48:22 +01:00

408 lines
7.7 KiB
C

/***************************************************************************
gbx_jit.c
(c) 2018 Benoît Minisini <g4mba5@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
***************************************************************************/
#define __GBX_JIT_C
#include "gb_common.h"
#include "gb_common_buffer.h"
#include "gb_common_case.h"
#include "gbx_component.h"
#include "gbx_exec.h"
#include "gbx_object.h"
#include "gbx_api.h"
#include "gbx_jit.h"
typedef
struct {
JIT_FUNC addr;
PCODE *code;
}
JIT_FUNCTION;
bool JIT_disabled = FALSE;
static bool _component_loaded = FALSE;
static GB_FUNCTION _jit_compile_func;
static GB_FUNCTION _jit_wait_func;
static char _jit_state = JIT_NOT_COMPILED;
static void *_jit_library = NULL;
static JIT_FUNCTION *_jit_func = NULL;
static bool _debug = FALSE;
void JIT_exit(void)
{
ARRAY_delete(&_jit_func);
}
void JIT_abort(void)
{
static GB_FUNCTION _func;
if (!_component_loaded || JIT_disabled)
return;
if (GB_GetFunction(&_func, CLASS_find_global("Jit"), "_Abort", NULL, NULL))
ERROR_panic("Unable to find JIT._Abort() method");
GB_Call(&_func, 0, FALSE);
}
static int get_state(ARCHIVE *arch)
{
return arch ? arch->jit_state : _jit_state;
}
bool JIT_can_compile(ARCHIVE *arch)
{
return get_state(arch) == JIT_NOT_COMPILED;
}
void JIT_compile(ARCHIVE *arch)
{
COMPONENT *current;
if (JIT_disabled)
return;
if (arch ? arch->jit_library : _jit_library)
return;
if (!_component_loaded)
{
char *var;
_component_loaded = TRUE;
var = getenv("GB_NO_JIT");
if (var && var[0] && !(var[0] == '0' && var[1] == 0))
{
JIT_disabled = TRUE;
return;
}
var = getenv("GB_JIT_DEBUG");
if (var && var[0] && !(var[0] == '0' && var[1] == 0))
_debug = TRUE;
if (_debug)
fprintf(stderr, "gbx3: loading gb.jit component\n");
COMPONENT_load(COMPONENT_create("gb.jit"));
if (GB_GetFunction(&_jit_compile_func, CLASS_find_global("Jit"), "_Compile", "s", "b"))
ERROR_panic("Unable to find JIT._Compile() method");
if (GB_GetFunction(&_jit_wait_func, CLASS_find_global("Jit"), "_Wait", "s", "s"))
ERROR_panic("Unable to find JIT._Wait() method");
}
arch ? (arch->jit_state = JIT_COMPILING) : (_jit_state = JIT_COMPILING);
current = COMPONENT_current;
COMPONENT_current = NULL;
GB_Push(1, T_STRING, arch ? arch->name : "", -1);
GB_Call(&_jit_compile_func, 1, FALSE);
COMPONENT_current = current;
}
bool wait_for_compilation(ARCHIVE *arch)
{
COMPONENT *current;
void *lib;
void **iface;
GB_VALUE *ret;
char *path;
if (JIT_disabled)
return TRUE;
if (arch ? arch->jit_library : _jit_library)
return FALSE;
current = COMPONENT_current;
COMPONENT_current = NULL;
GB_Push(1, T_STRING, arch ? arch->name : "", -1);
ret = GB_Call(&_jit_wait_func, 1, FALSE);
COMPONENT_current = current;
arch ? (arch->jit_state = JIT_COMPILED) : (_jit_state = JIT_COMPILED);
path = GB_ToZeroString((GB_STRING *)ret);
if (!*path)
ERROR_panic("Unable to compile JIT source file");
//fprintf(stderr, "gbx3: shared jit library is: %s\n", path);
lib = dlopen(path, RTLD_NOW);
if (!lib)
ERROR_panic("Unable to load JIT library: %s", dlerror());
if (arch)
arch->jit_library = lib;
else
_jit_library = lib;
iface = dlsym(lib, "GB_PTR");
if (iface) *((void **)iface) = &GAMBAS_Api;
iface = dlsym(lib, "JIT_PTR");
if (iface) *((void **)iface) = &GAMBAS_JitApi;
return FALSE;
}
static bool create_function(CLASS *class, int index)
{
ARCHIVE *arch;
FUNCTION *func;
JIT_FUNCTION *jit;
void *lib;
void *addr;
int i;
int len;
char *name;
int jit_index;
arch = class->component ? class->component->archive : NULL;
if (!class->loaded)
return TRUE;
/*if (get_state(arch) == JIT_NOT_COMPILED) // don't use JIT if we are compiling the corresponding archive
return TRUE;*/
if (wait_for_compilation(arch))
{
for (i = 0; i < class->load->n_func; i++)
class->load->func[i].fast = FALSE;
return TRUE;
}
func = &class->load->func[index];
func->fast_linked = TRUE;
if (!arch)
lib = _jit_library;
else
lib = arch->jit_library;
name = class->name;
while (*name == '^')
name++;
len = sprintf(COMMON_buffer, "jit_%s_%d", name, index);
for (i = 0; i < len; i++)
COMMON_buffer[i] = tolower(COMMON_buffer[i]);
addr = dlsym(lib, COMMON_buffer);
if (!addr)
{
func->fast = FALSE;
return TRUE;
}
if (_debug && func->debug)
fprintf(stderr, "gbx3: loading jit function: %s.%s\n", class->name, func->debug->name);
if (!_jit_func)
ARRAY_create(&_jit_func);
jit_index = ARRAY_count(_jit_func);
jit = (JIT_FUNCTION *)ARRAY_add(&_jit_func);
jit->addr = addr;
jit->code = func->code;
func->code = (PCODE *)(intptr_t)jit_index;
return FALSE;
}
bool JIT_exec(bool ret_on_stack)
{
VALUE *sp = SP;
JIT_FUNCTION *jit;
CLASS *class = EXEC.class;
void *object = EXEC.object;
char nparam = EXEC.nparam;
VALUE ret;
FUNCTION *func = EXEC.func;
if (UNLIKELY(nparam < func->npmin))
THROW(E_NEPARAM);
else if (UNLIKELY(nparam > func->n_param && !func->vararg))
THROW(E_TMPARAM);
if (!func->fast_linked)
{
if (create_function(class, EXEC.index))
return TRUE;
}
STACK_push_frame(&EXEC_current, func->stack_usage);
CP = class;
OP = object;
FP = func;
EC = NULL;
jit = &_jit_func[(intptr_t)func->code];
PROFILE_ENTER_FUNCTION();
TRY
{
(*(jit->addr))(nparam);
}
CATCH
{
PROFILE_LEAVE_FUNCTION();
if (SP != sp)
ERROR_panic("Stack mismatch in JIT function (SP %+ld)\n", SP - sp);
RELEASE_MANY(SP, nparam);
STACK_pop_frame(&EXEC_current);
PROPAGATE();
}
END_TRY
PROFILE_LEAVE_FUNCTION();
if (SP != sp)
ERROR_panic("Stack mismatch in JIT function (SP %+ld)\n", SP - sp);
if (func->type != T_VOID)
{
ret = TEMP;
BORROW(&ret);
}
else
ret.type = T_VOID;
RELEASE_MANY(SP, nparam);
STACK_pop_frame(&EXEC_current);
if (ret_on_stack)
{
if (SP[-1].type == T_FUNCTION)
{
SP--;
OBJECT_UNREF(SP->_function.object);
}
*SP++ = ret;
ret.type = T_VOID;
}
else
RET = ret;
return FALSE;
}
PCODE *JIT_get_code(FUNCTION *func)
{
if (func->fast_linked)
return _jit_func[(intptr_t)func->code].code;
else
return func->code;
}
void *JIT_get_class_ref(int index)
{
return CP->load->class_ref[index];
}
CLASS_CONST *JIT_get_constant(int index)
{
return &CP->load->cst[index];
}
void JIT_debug(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
typedef
struct {
PCODE *pc;
VALUE **psp;
PCODE code;
}
JIT_call_unknown_ERROR;
static void error_JIT_call_unknown(JIT_call_unknown_ERROR *save)
{
save->pc[1] = (PCODE)save->code;
*save->psp = SP;
}
void JIT_call_unknown(PCODE *pc, VALUE **psp)
{
JIT_call_unknown_ERROR save;
PC = pc;
SP = *psp;
save.pc = pc;
save.psp = psp;
save.code = pc[1];
pc[1] = 0x140B;
ON_ERROR_1(error_JIT_call_unknown, &save)
{
EXEC_function_loop();
}
END_ERROR
error_JIT_call_unknown(&save);
}
void JIT_load_class(CLASS *class)
{
CLASS_load(class);
}
void JIT_load_class_without_init(CLASS *class)
{
TRY
{
CLASS_load_without_init(class);
}
CATCH
{
class->error = FALSE;
}
END_TRY
}