/*************************************************************************** gbx_jit.c (c) 2018 BenoƮt Minisini 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); RET = ret; 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; } 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 }