gambas-source-code/main/lib/jit/jit_body.c
gambas 4729b9e60b JIT implementation of Left$(), Mid$() and Right$(). They are now twice faster.
[GB.JIT]
* NEW: JIT implementation of Left$(), Mid$() and Right$(). They are now twice faster.
2021-09-18 23:41:47 +02:00

3644 lines
75 KiB
C

/***************************************************************************
jit_body.c
(c) 2000-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 __JIT_BODY_C
#define _GNU_SOURCE
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "jit.h"
#define JIT_print JIT_print_body
#define MAX_STACK 256
typedef
struct {
TYPE type;
char *expr;
ushort func;
ushort pc;
int index;
TYPE call;
}
STACK_SLOT;
typedef
struct {
TYPE type;
char *expr;
}
CTRL_INFO;
enum {
CALL_SUBR = 0,
CALL_SUBR_CODE = 1,
CALL_SUBR_UNKNOWN = 2,
CALL_NEW = 3,
CALL_RETURN_UNKNOWN = 128
};
enum {
MATH_NEG,
MATH_ABS,
MATH_SGN,
MATH_INT,
MATH_FIX
};
static FUNCTION *_func;
static STACK_SLOT _stack[MAX_STACK];
static int _stack_current = 0;
static bool _decl_rs;
static bool _decl_ro;
static bool _decl_rv;
static bool _decl_tp;
static bool _decl_ra;
static bool _decl_as;
static ushort _pc;
static bool _no_release = FALSE;
static bool _no_release_but_borrow = FALSE;
static int _loop_count;
static TYPE *_dup_type;
enum { LOOP_UNKNOWN, LOOP_UP, LOOP_DOWN };
static int _loop_type;
static int *_ctrl_index;
static CTRL_INFO *_ctrl_info;
static bool _has_gosub;
static bool _has_finally;
static bool _has_catch;
static bool _try_finished;
static bool _has_just_dup;
static bool _unsafe;
static void enter_function(FUNCTION *func, int index)
{
_func = func;
_decl_rs = FALSE;
_decl_ro = FALSE;
_decl_rv = FALSE;
_decl_tp = FALSE;
_decl_ra = FALSE;
_decl_as = FALSE;
_has_gosub = FALSE;
_loop_count = 0;
_has_just_dup = FALSE;
_has_catch = FALSE;
_has_finally = func->error && (func->code[func->error - 1] != C_CATCH);
_unsafe = func->unsafe;
GB.NewArray((void **)&_dup_type, sizeof(TYPE), 0);
GB.NewArray((void **)&_ctrl_info, sizeof(CTRL_INFO), 0);
if (func->n_ctrl)
GB.AllocZero((void **)&_ctrl_index, sizeof(int) * func->n_ctrl);
else
_ctrl_index = NULL;
JIT_print_decl(" VALUE **psp = (VALUE **)%p;\n", JIT.sp);
JIT_print_decl(" VALUE *sp = SP;\n");
//JIT_print_decl(" VALUE *ep = sp;\n");
//JIT_print(" VALUE *sp = SP; fprintf(stderr, \"> %d: sp = %%p\\n\", sp);\n", index);
JIT_print_decl(" ushort *pc = (ushort *)%p;\n", JIT.get_code(func));
JIT_print_decl(" GB_VALUE_GOSUB *gp = 0;\n");
JIT_print_decl(" bool error = FALSE;\n");
if (func->vararg)
{
JIT_print(" VALUE *fp = FP, *pp = PP, *bp = BP;\n");
JIT_print(" FP = %p; PP = v; BP = sp;\n", func);
}
JIT_print(" VALUE *ssp = sp;\n"); // fprintf(stderr, \"bp = %%p\\n\", bp);\n");
JIT_print(" TRY {\n\n");
_try_finished = FALSE;
}
static void print_catch(void)
{
JIT_print("\n } CATCH {\n\n");
JIT_print(" CP = (void *)%p;\n", JIT_class);
JIT_print(" FP = (void *)%p;\n", _func);
if (_has_catch || _has_finally)
JIT_print(" JIT.error_set_last(FALSE); \n");
//JIT_print(" fprintf(stderr, \"EP = %%p SP = %%p sp = %%p\\n\", EP, SP, sp);\n");
JIT_print(" if (SP > sp) sp = SP; else SP = sp;\n");
JIT_print(" LEAVE_SUPER();\n");
//JIT_print(" if (sp > bp) { fprintf(stderr, \"sp = %%p bp = %%p release %%d\\n\", sp, bp, sp - bp); JIT.release_many(sp, sp - bp); SP = sp = bp; }\n");
JIT_print(" if (sp > ssp) { JIT.release_many(sp, sp - ssp); SP = sp = ssp; }\n");
//JIT_print(" PP = SP;\n");
JIT_print(" error = TRUE;\n");
JIT_print("\n } END_TRY\n\n");
JIT_print("__FINALLY:;\n");
_try_finished = TRUE;
}
#define RELEASE_FAST(_expr, _type, _index) ({ \
TYPE _t = (_type); \
switch(TYPEID(_t)) \
{ \
case T_STRING: case T_OBJECT: case T_VARIANT: \
JIT_print((_expr), JIT_get_type(_t), (_index)); \
} \
})
static bool leave_function(FUNCTION *func, int index)
{
int i;
STR_free_later(NULL);
JIT_print("\n__RETURN:;\n");
//JIT_print("__RETURN: fprintf(stderr, \"< %d: sp = %%p\\n\", sp);\n", ind);
if (_stack_current)
JIT_panic("Stack mismatch: stack is not void");
if (!_has_catch && !_has_finally)
print_catch();
JIT_print("__RELEASE:;\n");
if (func->vararg)
JIT_print(" FP = fp; BP = bp; PP = pp;\n");
JIT_print(" SP = sp;\n");
JIT_print(" RELEASE_GOSUB();\n");
for (i = 0; i < GB.Count(_ctrl_info); i++)
{
RELEASE_FAST(" RELEASE_FAST_%s(c%d);\n", _ctrl_info[i].type, i);
if (_ctrl_info[i].expr)
STR_free(_ctrl_info[i].expr);
}
for (i = 0; i < GB.Count(_dup_type); i++)
RELEASE_FAST(" RELEASE_FAST_%s(d%d);\n", _dup_type[i], i);
for (i = 0; i < func->n_local; i++)
RELEASE_FAST(" RELEASE_FAST_%s(l%d);\n", JIT_ctype_to_type(JIT_class, func->local[i].type), i);
for (i = 0; i < func->n_param; i++)
RELEASE_FAST(" RELEASE_FAST_%s(p%d);\n", func->param[i].type, i);
if (_decl_ra)
JIT_print(" GB.Unref(&ra);\n");
if (!_has_catch && !_has_finally)
{
JIT_print(" if (error) { ");
/*if (func->n_param)
JIT_print("SP -= %d; ", func->n_param);*/
JIT_print("GB.Propagate(); }\n");
}
GB.Free((void **)&_ctrl_index);
GB.FreeArray((void **)&_ctrl_info);
GB.FreeArray((void **)&_dup_type);
_func = NULL;
return FALSE;
}
static TYPE get_local_type(FUNCTION *func, int index)
{
TYPE type;
if (index < func->n_local)
type = JIT_ctype_to_type(JIT_class, func->local[index].type);
else
type = _ctrl_info[_ctrl_index[index - func->n_local]].type;
return type;
}
static void free_stack(int n)
{
if (n < 0) n += _stack_current;
STR_free(_stack[n].expr);
_stack[n].expr = NULL;
}
static void check_stack(int n)
{
if (_stack_current < n)
JIT_panic("Stack mismatch: stack is void");
}
static void pop_stack(int n)
{
int i;
for (i = 1; i <= n; i++)
free_stack(-i);
_stack_current -= n;
}
static void declare(bool *flag, const char *expr)
{
if (*flag)
return;
JIT_print_decl(" %s;\n", expr);
*flag = TRUE;
}
static void print_label(FUNCTION *func, ushort pc)
{
//JIT_print("__L%d:; fprintf(stderr, \"[%s]\\n\");\n", pc, JIT.get_position(JIT_class, func, &func->code[pc]));
JIT_print("__L%d:; // %s\n", pc, JIT.get_position(JIT_class, func, &func->code[pc]));
}
static void push_one(TYPE type, const char *fmt, va_list args)
{
if (_stack_current > MAX_STACK)
JIT_panic("Expression too complex");
CLEAR(&_stack[_stack_current]);
if (fmt)
STR_vadd(&_stack[_stack_current].expr, fmt, args);
_stack[_stack_current].type = type;
_stack[_stack_current].call = T_UNKNOWN;
_stack_current++;
}
static void push(TYPE type, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
push_one(type, fmt, args);
va_end(args);
}
static STACK_SLOT *get_stack(int n)
{
if (n < 0) n += _stack_current;
return &_stack[n];
}
static TYPE get_type(int n)
{
TYPE type;
type = get_stack(n)->type;
if (TYPE_is_pure_object(type))
JIT_load_class_without_init((CLASS *)type);
return type;
}
static char *get_expr(int n)
{
return get_stack(n)->expr;
}
static void set_expr(int n, char *expr)
{
if (n < 0) n += _stack_current;
_stack[n].expr = expr;
}
static CLASS *get_class(int n)
{
TYPE type = get_type(n);
if (type == T_CLASS)
{
sscanf(get_expr(n), "CLASS(%p)", (void **)&type);
if (type)
JIT_load_class_without_init((CLASS *)type);
}
else if (!TYPE_is_pure_object(type))
type = 0;
return (CLASS *)type;
}
static char *borrow_expr(char *expr, TYPE type)
{
const char *type_name = JIT_get_type(type);
int len;
char *new_expr;
len = strlen(expr);
if ((strncmp(&expr[len - 5], "();})", 5) == 0) && (strncmp(&expr[len - 10], "POP_", 4) == 0) && (expr[len - 6] == *type_name))
new_expr = STR_print("%.*sPOP_BORROW_%s();})", len - 10, expr, type_name);
else
new_expr = STR_print("BORROW_%s(%s)", type_name, expr);
STR_free(expr);
return new_expr;
}
static const char *get_conv_format(TYPE src, TYPE dest)
{
static char buffer[64];
if (src == T_VOID)
{
sprintf(buffer, "(THROW_PC(E_NRETURN, %d),%s)", _pc, JIT_get_default_value(TYPEID(dest)));
return buffer;
}
switch(dest)
{
case T_VOID:
return "((void)%s)";
case T_BOOLEAN:
switch(TYPEID(src))
{
case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER:
return "((%s)!=0)";
case T_OBJECT:
return "({ void *_addr = (%s).value; if (_addr) { GB.Ref(_addr); GB.Unref(&_addr); } (_addr) != 0; })";
}
break;
case T_BYTE:
switch(src)
{
case T_BOOLEAN:
return "((uchar)(%s)?255:0)";
case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT:
return "((uchar)(%s))";
}
break;
case T_SHORT:
switch(src)
{
case T_BOOLEAN:
return "((short)(%s)?-1:0)";
case T_BYTE: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT:
return "((short)(%s))";
}
break;
case T_INTEGER:
switch(src)
{
case T_BOOLEAN:
return "((int)(%s)?-1:0)";
case T_BYTE: case T_SHORT: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER:
return "((int)(%s))";
}
break;
case T_LONG:
switch(src)
{
case T_BOOLEAN:
return "((int64_t)(%s)?-1:0)";
case T_BYTE: case T_SHORT: case T_INTEGER: case T_SINGLE: case T_FLOAT: case T_POINTER:
return "((int64_t)(%s))";
}
break;
case T_SINGLE:
switch(src)
{
case T_BOOLEAN:
return "((float)(%s)?-1:0)";
case T_BYTE: case T_SHORT: case T_INTEGER:
return "((float)(%s))";
case T_LONG: case T_FLOAT:
if (_unsafe)
return "((float)(%s))";
else
return "(CHECK_FINITE((float)(%s)))";
}
break;
case T_FLOAT:
switch(src)
{
case T_BOOLEAN:
return "((double)(%s)?-1:0)";
case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE:
return "((double)(%s))";
}
break;
case T_STRING:
switch(src)
{
case T_CSTRING: return "%s";
case T_NULL: return "GET_NULL_s()";
}
break;
case T_CSTRING:
switch(src)
{
case T_STRING: return "%s";
case T_NULL: return "GET_NULL_s()";
}
break;
default:
if (src == T_NULL)
{
switch(dest)
{
case T_DATE:
case T_POINTER:
case T_VARIANT:
case T_OBJECT:
sprintf(buffer,"GET_NULL_%s()", JIT_get_type(dest));
return buffer;
default:
sprintf(buffer, "GET_OBJECT(NULL, CLASS(%p))", (CLASS *)dest);
return buffer;
}
}
if (TYPE_is_object(dest) && TYPE_is_object(src))
{
if (TYPE_is_pure_object(dest))
sprintf(buffer, "CONV_o_O(%%s, %p)", (CLASS *)dest);
else
sprintf(buffer, "CONV_o(%%s)");
return buffer;
}
break;
}
if (TYPE_is_pure_object(dest))
sprintf(buffer, "CONV(%%s, %s, %s, CLASS(%p))", JIT_get_type(src), JIT_get_type(dest), (CLASS *)dest);
else if (src == T_FUNCTION)
sprintf(buffer, "CONV(NULL, F, %s, %s)", JIT_get_type(dest), JIT_get_gtype(dest));
else
sprintf(buffer, "CONV(%%s, %s, %s, %s)", JIT_get_type(src), JIT_get_type(dest), JIT_get_gtype(dest));
return buffer;
}
static char *get_conv(TYPE src, TYPE dest, char *expr)
{
char *new_expr;
char *borrow;
if (dest == T_VOID)
{
switch (TYPEID(src))
{
case T_OBJECT:
case T_STRING:
case T_VARIANT:
borrow = borrow_expr(expr, src);
new_expr = STR_print("RELEASE_%s(%s)", JIT_get_type(src), borrow);
STR_free(borrow);
return new_expr;
}
}
if (src == T_VOID && !expr) // void method arguments
new_expr = STR_copy(JIT_get_default_value(TYPEID(dest)));
else
new_expr = STR_print(get_conv_format(src, dest), expr);
STR_free(expr);
return new_expr;
}
static char *peek(int n, TYPE conv)
{
STACK_SLOT *ss;
char *expr;
TYPE type;
ss = get_stack(n);
expr = ss->expr;
type = ss->type;
if (type == T_FUNCTION && !expr)
expr = ss->expr = STR_print("GET_FUNCTION(%d)", ss->pc);
if (type != conv)
ss->expr = expr = get_conv(type, conv, expr);
return expr;
}
static char *peek_pop(int n, TYPE conv, const char *fmt, va_list args)
{
char *dest = NULL;
char *expr;
TYPE type;
char *op;
if (n < 0) n += _stack_current;
expr = _stack[n].expr;
type = _stack[n].type;
if (fmt)
{
STR_vadd(&dest, fmt, args);
if (!_no_release)
{
switch (TYPEID(conv))
{
case T_STRING:
declare(&_decl_rs, "char *rs");
JIT_print(" if ((%s).type == GB_T_STRING) rs = (%s).value.addr; else rs = NULL;\n", dest, dest);
break;
case T_OBJECT:
declare(&_decl_ro, "void *ro");
JIT_print(" ro = (%s).value;\n", dest);
break;
case T_VARIANT:
declare(&_decl_rv, "GB_VARIANT rv");
JIT_print(" rv = (%s);\n", dest);
break;
}
}
}
if (type != conv)
_stack[n].expr = expr = get_conv(type, conv, expr);
if (fmt)
{
if (!_no_release || _no_release_but_borrow)
{
switch (TYPEID(conv))
{
case T_STRING:
case T_OBJECT:
case T_VARIANT:
_stack[n].expr = expr = borrow_expr(expr, conv);
break;
}
}
if (_no_release)
{
JIT_print(" ");
JIT_print(dest, expr);
JIT_print(";\n");
}
else
{
if (dest[strlen(dest) - 1] != '=')
op = " =";
else
op = "";
JIT_print(" %s%s %s;\n", dest, op, expr);
switch (TYPEID(conv))
{
case T_STRING: JIT_print(" GB.FreeString(&rs);\n"); break;
case T_OBJECT: JIT_print(" GB.Unref(&ro);\n"); break;
case T_VARIANT: JIT_print(" GB.ReleaseValue((GB_VALUE *)&rv);\n"); break;
}
}
STR_free(dest);
}
return expr;
}
static char *push_expr(int n, TYPE type)
{
const char *type_name;
char *expr;
char *new_expr;
int len;
type_name = JIT_get_type(type);
expr = peek(n, type);
if (type == T_VOID)
return "PUSH_V()";
if (type == T_FUNCTION)
{
new_expr = STR_print("CALL_UNKNOWN(%d)", get_stack(n)->pc);
}
else
{
len = strlen(expr);
if ((strncmp(&expr[len - 5], "();})", 5) == 0) && (strncmp(&expr[len - 10], "POP_", 4) == 0) && (expr[len - 6] == *type_name))
new_expr = STR_print("%.*s})", len - 10, expr);
else
new_expr = STR_print("PUSH_%s(%s)", type_name, expr);
}
STR_free(expr);
set_expr(n, new_expr);
//fprintf(stderr, "push_expr %s ===> %s\n", expr, new_expr);
return new_expr;
}
static void pop(TYPE type, const char *fmt, ...)
{
va_list args;
char *expr;
check_stack(1);
_stack_current--;
va_start(args, fmt);
expr = peek_pop(_stack_current, type, fmt, args);
va_end(args);
if (!fmt)
JIT_print(" %s;\n", expr);
free_stack(_stack_current);
}
static bool check_swap(TYPE type, const char *fmt, ...)
{
va_list args;
char *expr = NULL;
char *swap = NULL;
if (_has_just_dup)
{
_has_just_dup = FALSE;
return TRUE;
}
if (_stack_current < 2)
return TRUE;
STR_add(&expr, "({ %s _t = %s; ", JIT_get_ctype(type), peek(-2, type));
va_start(args, fmt);
STR_vadd(&swap, fmt, args);
va_end(args);
STR_add(&expr, swap, peek(-1, type));
STR_add(&expr, "; _t; })");
pop_stack(2);
push(type, "%s", expr);
STR_free(swap);
STR_free(expr);
return FALSE;
}
static int add_ctrl(int index, TYPE type, const char *expr)
{
int index_ctrl;
CTRL_INFO *info;
index_ctrl = GB.Count(_ctrl_info);
info = (CTRL_INFO *)GB.Add(&_ctrl_info);
info->type = type;
if (expr)
info->expr = STR_copy(expr);
else
info->expr = NULL;
if (index >= 0)
_ctrl_index[index] = index_ctrl;
//JIT_print_decl(" %s c%d;\n", JIT_get_ctype(type), index_ctrl);
JIT_declare(type, "c%d", index_ctrl);
return index_ctrl;
}
static void pop_ctrl(int index, TYPE type)
{
int index_ctrl;
char *expr;
if (type == T_VOID)
type = get_type(-1);
if (type == T_CLASS)
expr = get_expr(-1);
else
expr = NULL;
index_ctrl = add_ctrl(index, type, expr);
//_no_release = TRUE;
if (expr)
pop_stack(1);
else
pop(type, "c%d", index_ctrl);
//_no_release = FALSE;
}
static void push_constant(CLASS *class, int index)
{
CLASS_CONST *cc = &class->load->cst[index];
switch(cc->type)
{
case T_BOOLEAN: push(T_BOOLEAN, "(bool)%d", cc->_integer.value); break;
case T_BYTE: push(T_BYTE, "(uchar)%d", cc->_integer.value); break;
case T_SHORT: push(T_SHORT, "(short)%d", cc->_integer.value); break;
case T_INTEGER: push(T_INTEGER, "(int)%d", cc->_integer.value); break;
case T_LONG: push(T_LONG, "(int64_t)%" PRId64, cc->_long.value); break;
case T_SINGLE: push(T_SINGLE, "(*(float *)%p)", &cc->_single.value); break;
case T_FLOAT: push(T_FLOAT, "(*(double *)%p)", &cc->_float.value); break;
case T_STRING: push(T_CSTRING, "CONSTANT_s(%p, %d)", cc->_string.addr, cc->_string.len); break;
case T_CSTRING: push(T_CSTRING, "CONSTANT_t(%p, %d)", cc->_string.addr, 0); break;
case T_POINTER: push(T_POINTER, "(intptr_t)0"); break;
default: JIT_panic("unknown constant type");
}
}
static void push_function(int func, int index)
{
push(T_FUNCTION, NULL);
_stack[_stack_current - 1].func = func;
_stack[_stack_current - 1].index = index;
_stack[_stack_current - 1].pc = _pc;
}
static void push_static_variable(CLASS *class, CTYPE ctype, char *addr)
{
TYPE type = JIT_ctype_to_type(class, ctype);
const char *klass;
char buffer[32];
if (class == JIT_class)
klass = "CP";
else
{
sprintf(buffer, "CLASS(%p)", class);
klass = buffer;
}
switch(ctype.id)
{
case TC_STRUCT:
push(type, "GET_S(%s, %p, CLASS(%p))", klass, addr, (CLASS *)type);
break;
case TC_ARRAY:
//declare(&_decl_ra, "void *ra = NULL");
push(type, "GET_A(%s, %s, %p, CLASS(%p), %p)", klass, klass, addr, (CLASS *)type, class->load->array[ctype.value]);
break;
case T_OBJECT:
if (class == JIT_class)
{
if (TYPE_is_pure_object(type))
push(type, "GET_o(%p, CLASS(%p))", addr, (CLASS *)type);
else
push(type, "GET_o(%p, GB_T_OBJECT)", addr);
}
else
{
if (TYPE_is_pure_object(type))
push(type, "({ JIT.load_class(%p); GET_o(%p, CLASS(%p)); })", class, addr, (CLASS *)type);
else
push(type, "({ JIT.load_class(%p); GET_o(%p, GB_T_OBJECT); })", class, addr);
}
break;
default:
if (class == JIT_class)
push(type, "GET_%s(%p)", JIT_get_type(type), addr);
else
push(type, "({ JIT.load_class(%p); GET_%s(%p); })", class, JIT_get_type(type), addr);
}
}
static void push_dynamic_variable(CLASS *class, CTYPE ctype, int pos, char *addr)
{
TYPE type = JIT_ctype_to_type(class, ctype);
switch(ctype.id)
{
case TC_STRUCT:
push(type, "GET_S(%s, %s + %d, CLASS(%p))", addr, addr, pos, (CLASS *)type);
break;
case TC_ARRAY:
//declare(&_decl_ra, "void *ra = NULL");
push(type, "GET_A(%p, %s, %s + %d, CLASS(%p), %p)", class, addr, addr, pos, (CLASS *)type, class->load->array[ctype.value]);
break;
case T_OBJECT:
if (TYPE_is_pure_object(type))
push(type, "GET_o(%s + %d, CLASS(%p))", addr, pos, (CLASS *)type);
else
push(type, "GET_o(%s + %d, GB_T_OBJECT)", addr, pos);
break;
default:
push(type, "GET_%s(%s + %d)", JIT_get_type(type), addr, pos);
}
}
static void push_unknown(int index)
{
TYPE type = T_UNKNOWN;
TYPE call_type = T_UNKNOWN;
char *expr;
CLASS *class;
check_stack(1);
// TODO: Check object
// if (!class->is_simple && ...
// if (UNLIKELY(class->must_check && (*(class->check))(object)))
// THROW(E_IOBJECT);
class = get_class(-1);
if (class)
{
CLASS_DESC *desc;
void *addr;
int pos;
CTYPE ctype;
char *sym;
char *get_addr;
bool static_class;
static_class = get_type(-1) == T_CLASS;
sym = JIT_class->load->unknown[index];
if (class == (CLASS *)GB.FindClass("Param"))
{
if (!strcasecmp(sym, "Count"))
{
pop_stack(1);
push(T_INTEGER, _func->vararg ? "nv" : "0");
return;
}
else if (!strcasecmp(sym, "Max"))
{
pop_stack(1);
push(T_INTEGER, _func->vararg ? "(nv - 1)" : "-1");
return;
}
}
index = JIT_find_symbol(class, sym);
if (index != NO_SYMBOL)
{
desc = class->table[index].desc;
class = desc->method.class;
switch (CLASS_DESC_get_type(desc))
{
case CD_STATIC_VARIABLE:
pop_stack(1);
ctype = desc->variable.ctype;
addr = (char *)desc->variable.class->stat + desc->variable.offset;
push_static_variable(class, ctype, addr);
return;
case CD_VARIABLE:
// TODO: automatic class
ctype = desc->variable.ctype;
expr = peek(-1, (TYPE)class);
pos = desc->variable.offset;
if (_unsafe)
get_addr = STR_print("ADDR_UNSAFE(%s)", expr);
else if (class->must_check)
get_addr = STR_print("ADDR_CHECK(%p, %s)", class->check, expr);
else
get_addr = STR_print("ADDR(%s)", expr);
pop_stack(1);
push_dynamic_variable(class, ctype, pos, get_addr);
STR_free(get_addr);
return;
case CD_CONSTANT:
if (!static_class)
{
type = desc->constant.type;
break;
}
pop_stack(1);
switch(desc->constant.type)
{
case T_BOOLEAN: push(T_BOOLEAN, "(bool)%d", desc->constant.value._integer); break;
case T_BYTE: push(T_BYTE, "(uchar)%d", desc->constant.value._integer); break;
case T_SHORT: push(T_SHORT, "(short)%d", desc->constant.value._integer); break;
case T_INTEGER: push(T_INTEGER, "(int)%d", desc->constant.value._integer); break;
case T_LONG: push(T_LONG, "(int64_t)%" PRId64, desc->constant.value._long); break;
case T_SINGLE: push(T_SINGLE, "(*(float *)%p)", &desc->constant.value._single); break;
case T_FLOAT: push(T_FLOAT, "(*(double *)%p)", &desc->constant.value._float); break;
case T_POINTER: push(T_POINTER, "(intptr_t)%p", desc->constant.value._pointer); break;
case T_STRING: case T_CSTRING:
if (desc->constant.translate)
push(T_CSTRING, "CONSTANT_t(%p, %d)", desc->constant.value._string, strlen(desc->constant.value._string));
else
push(T_CSTRING, "CONSTANT_s(%p, %d)", desc->constant.value._string, strlen(desc->constant.value._string));
break;
default: JIT_panic("unknown constant type");
}
return;
case CD_PROPERTY:
case CD_STATIC_PROPERTY:
case CD_PROPERTY_READ:
case CD_STATIC_PROPERTY_READ:
type = desc->property.type;
break;
case CD_METHOD:
case CD_STATIC_METHOD:
call_type = desc->method.type;
break;
}
}
else
JIT_print(" // %s.%s ?\n", class->name, sym);
}
expr = STR_copy(push_expr(-1, get_type(-1)));
pop_stack(1);
push(type, "({%s;PUSH_UNKNOWN(%d);POP_%s();})", expr, _pc, JIT_get_type(type));
_stack[_stack_current - 1].call = call_type;
STR_free(expr);
}
static void pop_static_variable(CLASS *class, CTYPE ctype, char *addr)
{
TYPE type = JIT_ctype_to_type(class, ctype);
const char *klass;
char buffer[32];
if (class == JIT_class)
klass = "CP";
else
{
sprintf(buffer, "CLASS(%p)", class);
klass = buffer;
}
_no_release = TRUE;
switch(ctype.id)
{
case TC_STRUCT:
case TC_ARRAY:
if (check_swap(type, "SET_SA(%s, %p, %d, %%s)", klass, addr, ctype))
pop(type, "SET_SA(%s, %p, %d, %%s)", klass, addr, ctype);
break;
default:
if (check_swap(type, "SET_%s(%p, %%s)", JIT_get_type(type), addr))
pop(type, "SET_%s(%p, %%s)", JIT_get_type(type), addr);
}
_no_release = FALSE;
}
static void pop_dynamic_variable(CLASS *class, CTYPE ctype, int pos, char *addr)
{
TYPE type = JIT_ctype_to_type(class, ctype);
const char *klass;
char buffer[32];
if (class == JIT_class)
klass = "CP";
else
{
sprintf(buffer, "CLASS(%p)", class);
klass = buffer;
}
_no_release = TRUE;
switch(ctype.id)
{
case TC_STRUCT:
case TC_ARRAY:
if (check_swap(type, "SET_SA(%s, %s + %d, %d, %%s)", klass, addr, pos, ctype))
pop(type, "SET_SA(%s, %s + %d, %d, %%s)", klass, addr, pos, ctype);
break;
default:
if (check_swap(type, "SET_%s(%s + %d, %%s)", JIT_get_type(type), addr, pos))
pop(type, "SET_%s(%s + %d, %%s)", JIT_get_type(type), addr, pos);
}
_no_release = FALSE;
}
static void pop_unknown(int index)
{
CLASS *class;
char *expr = NULL;
char *arg;
check_stack(2);
class = get_class(-1);
if (class)
{
CLASS_DESC *desc;
void *addr;
int pos;
CTYPE ctype;
const char *sym;
char *get_addr;
sym = JIT_class->load->unknown[index];
index = JIT_find_symbol(class, sym);
if (index != NO_SYMBOL)
{
desc = class->table[index].desc;
switch (CLASS_DESC_get_type(desc))
{
case CD_STATIC_VARIABLE:
pop_stack(1);
ctype = desc->variable.ctype;
addr = (char *)desc->variable.class->stat + desc->variable.offset;
pop_static_variable(class, ctype, addr);
return;
case CD_VARIABLE:
// TODO: automatic class
ctype = desc->variable.ctype;
expr = peek(-1, (TYPE)class);
if (_unsafe)
get_addr = STR_print("ADDR_UNSAFE(%s)", expr);
else if (class->must_check)
get_addr = STR_print("ADDR_CHECK(%p, %s)", class->check, expr);
else
get_addr = STR_print("ADDR(%s)", expr);
pop_stack(1);
pos = desc->variable.offset;
pop_dynamic_variable(class, ctype, pos, get_addr);
STR_free(get_addr);
return;
}
}
else
JIT_print(" // %s.%s ?\n", class->name, sym);
}
arg = push_expr(-2, get_type(-2));
STR_add(&expr,"%s;", arg);
arg = push_expr(-1, get_type(-1));
STR_add(&expr, "%s;POP_UNKNOWN(%d);", arg, _pc);
pop_stack(2);
push(T_VOID, "({%s})", expr);
if (check_swap(T_UNKNOWN, "({%s})", expr))
pop(T_VOID, NULL);
STR_free(expr);
}
static void push_array(ushort code)
{
TYPE type;
int i, narg;
char *expr = NULL;
char *expr1, *expr2;
const char *unsafe = _unsafe ? "_UNSAFE" : "";
narg = code & 0x3F;
check_stack(narg);
type = get_type(-narg);
if (TYPE_is_pure_object(type))
{
CLASS *class = (CLASS *)type;
//JIT_print(" // %s %d\n", class->name, class->is_array);
if (class->is_array && !class->is_array_of_struct)
{
type = class->array_type;
if (narg == 2)
{
expr1 = peek(-2, get_type(-2));
expr2 = peek(-1, T_INTEGER);
if (TYPE_is_pure_object(type))
expr = STR_print("PUSH_ARRAY_O(%s,%s,CLASS(%p),%s)", expr1, expr2, (CLASS *)type, unsafe);
else
expr = STR_print("PUSH_ARRAY_%s(%s,%s,%s)", JIT_get_type(type), expr1, expr2, unsafe);
pop_stack(2);
push(type, "(%s)", expr);
STR_free(expr);
return;
}
}
else
type = T_UNKNOWN;
}
else
type = T_UNKNOWN;
//declare_sp();
for (i = _stack_current - narg; i < _stack_current; i++)
{
STR_add(&expr, "%s;", push_expr(i, get_type(i)));
free_stack(i);
}
_stack_current -= narg;
STR_add(&expr, "CALL_PUSH_ARRAY(%d, 0x%04X);POP_%s();", _pc, code, JIT_get_type(type));
push(type, "({%s})", expr);
STR_free(expr);
}
static void pop_array(ushort code)
{
TYPE type;
int i, narg;
char *expr = NULL;
char *expr1, *expr2;
const char *unsafe = _unsafe ? "_UNSAFE" : "";
narg = code & 0x3F;
//fprintf(stderr, "pop_array: %04X %d\n", code, narg);
check_stack(narg + 1);
type = get_type(-narg);
if (TYPE_is_pure_object(type))
{
CLASS *class = (CLASS *)type;
if (class->is_array && !class->is_array_of_struct)
{
type = class->array_type;
if (narg == 2)
{
expr1 = peek(-2, get_type(-2));
expr2 = peek(-1, T_INTEGER);
STR_add(&expr, "POP_ARRAY_%s(%s,%s,%s,%s);", JIT_get_type(type), expr1, expr2, peek(-3, type), unsafe);
pop_stack(3);
goto _CHECK_SWAP;
}
}
}
else
type = T_UNKNOWN;
//declare_sp();
narg++;
for (i = _stack_current - narg; i < _stack_current; i++)
{
STR_add(&expr, "%s;", push_expr(i, get_type(i)));
free_stack(i);
}
_stack_current -= narg;
STR_add(&expr, "CALL_POP_ARRAY(%d, 0x%04X);sp--;", _pc, code);
_CHECK_SWAP:
push(T_VOID, "({%s})", expr);
if (check_swap(type, "({%s})", expr))
pop(T_VOID, NULL);
STR_free(expr);
}
static void push_subr(char mode, ushort code)
{
const char *call;
TYPE type;
int i, narg;
char *expr = NULL;
ushort op;
bool rst = FALSE;
char type_id = 0;
void *addr;
//declare_sp();
//JIT_print(" static ushort s%d = 0x%04X;\n", _subr_count, code);
op = code >> 8;
switch(mode & 7)
{
case CALL_SUBR_CODE:
call = "CALL_SUBR_CODE(%d, %p, 0x%04X)";
addr = JIT.subr_table[op];
break;
case CALL_SUBR:
call = "CALL_SUBR(%d, %p)";
addr = JIT.subr_table[op];
break;
case CALL_SUBR_UNKNOWN:
call = "CALL_SUBR_UNKNOWN(%d)";
addr = NULL;
break;
case CALL_NEW:
call = "CALL_SUBR_CODE(%d, %p, 0x%04X)";
addr = JIT.new;
break;
default:
return;
}
if (op == (C_NEW >> 8))
{
narg = code & 0x3F;
type = get_type(-narg);
}
else if (op == (C_NEG >> 8))
{
narg = 1;
type = get_type(-1);
}
else if (op < CODE_FIRST_SUBR)
{
int index = RESERVED_get_from_opcode(code);
if (index < 0)
JIT_panic("Unknown operator");
if (RES_is_unary(index))
narg = 1;
else if (RES_is_binary(index))
narg = 2;
else
narg = code & 0x3F;
type_id = COMP_res_info[index].type;
rst = TRUE;
}
else
{
SUBR_INFO *info = SUBR_get_from_opcode(op - CODE_FIRST_SUBR, code & 0x3F);
if (!info)
JIT_panic("unknown subroutine");
if (info->min_param <= info->max_param)
narg = code & 0x3F;
else
narg = info->min_param;
type_id = info->type;
rst = TRUE;
}
check_stack(narg);
if (rst)
{
switch(type_id)
{
case RST_SAME:
case RST_BCLR:
type = get_type(-narg);
break;
case RST_MIN:
type = Max(get_type(-1), get_type(-2));
if (type > T_DATE && type != T_VARIANT)
type = T_UNKNOWN;
break;
case RST_COLLECTION:
type = GB.FindClass("Collection");
break;
case RST_EXEC:
i = atoi(get_expr(-2));
if ((i & PM_WAIT) && (i & PM_STRING))
type = T_STRING;
else
type = GB.FindClass("Process");
break;
case RST_READ:
type = get_type(-1);
if (type == T_INTEGER)
type = atoi(get_expr(-1));
else if (type == T_CLASS)
type = (TYPE)get_class(-1);
break;
default:
type = (type_id >= T_UNKNOWN) ? T_UNKNOWN : type_id;
}
}
if (op == (C_NEW >> 8))
{
if (type == T_CLASS)
type = (TYPE)get_class(-narg);
else
type = T_OBJECT;
}
else if (op == CODE_DEBUG)
{
STR_add(&expr, "FP=(void *)%p;PC = &pc[%d];", _func, _pc);
type = narg == 0 ? T_INTEGER : T_BOOLEAN;
}
else if (op == (C_CAT >> 8))
{
if (narg == 1)
{
narg = 2;
code = C_CAT | 2; // TODO: implement optimization of '&=' operator
}
}
if (narg > 0)
{
for (i = _stack_current - narg; i < _stack_current; i++)
{
STR_add(&expr, "%s;", push_expr(i, get_type(i)));
free_stack(i);
}
_stack_current -= narg;
}
STR_add(&expr, call, _pc, addr, code);
if (mode & CALL_RETURN_UNKNOWN)
type = T_UNKNOWN;
STR_add(&expr, ";POP_%s();", JIT_get_type(type));
push(type, "({%s})", expr);
STR_free(expr);
}
static void push_subr_add(ushort code, const char *op, const char *opb, bool allow_pointer)
{
char *expr;
char *expr1, *expr2;
TYPE type1, type2, type;
check_stack(2);
type1 = get_type(-2);
type2 = get_type(-1);
if (TYPEID(type1) > TYPEID(type2))
type = type1;
else
type = type2;
switch(type)
{
case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT:
break;
case T_DATE: case T_STRING: case T_CSTRING:
type = T_FLOAT;
break;
case T_POINTER:
if (allow_pointer)
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr1 = peek(-2, type);
expr2 = peek(-1, type);
if (type == T_BOOLEAN)
op = opb;
expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op);
pop_stack(2);
push(type, "(%s)", expr);
STR_free(expr);
}
static void push_subr_div(ushort code)
{
char *expr;
char *expr1, *expr2;
TYPE type1, type2, type;
check_stack(2);
type1 = get_type(-2);
type2 = get_type(-1);
if (TYPEID(type1) > TYPEID(type2))
type = type1;
else
type = type2;
switch(type)
{
case T_SINGLE: case T_FLOAT:
break;
case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG:
type = T_FLOAT;
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr1 = peek(-2, type);
expr2 = peek(-1, type);
if (_unsafe)
expr = STR_print("({%s _a = %s; %s _b = %s; _a /= _b; _a;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2);
else
expr = STR_print("({%s _a = %s; %s _b = %s; _a /= _b; if (!isfinite(_a)) THROW_PC(E_ZERO, %d); _a;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, _pc);
pop_stack(2);
push(type, "(%s)", expr);
STR_free(expr);
}
static void push_subr_arithmetic(char op, ushort code)
{
TYPE type;
char *expr;
const char *func;
check_stack(1);
type = get_type(-1);
switch (op)
{
case MATH_ABS: func = "MATH_ABS"; break;
case MATH_NEG: func = "- "; break;
case MATH_SGN: func = "MATH_SGN"; break;
}
switch(type)
{
case T_BOOLEAN:
if (op == MATH_NEG)
return;
break;
case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT:
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr = STR_copy(peek(-1, type));
pop_stack(1);
push(type, "(%s(%s))", func, expr);
STR_free(expr);
}
static void push_subr_float_arithmetic(char op, ushort code)
{
TYPE type;
char *expr;
const char *func;
check_stack(1);
type = get_type(-1);
switch(type)
{
case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG:
return;
case T_SINGLE:
func = op == MATH_FIX ? "MATH_FIX_g" : "floorf";
break;
case T_FLOAT:
func = op == MATH_FIX ? "MATH_FIX_f" : "floor";
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr = STR_copy(peek(-1, type));
pop_stack(1);
push(type, "(%s(%s))", func, expr);
STR_free(expr);
}
static void push_subr_quo(ushort code, const char *op)
{
char *expr;
char *expr1, *expr2;
TYPE type1, type2, type;
check_stack(2);
type1 = get_type(-2);
type2 = get_type(-1);
if (TYPEID(type1) > TYPEID(type2))
type = type1;
else
type = type2;
switch(type)
{
case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG:
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr1 = peek(-2, type);
expr2 = peek(-1, type);
if (_unsafe)
expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op);
else
expr = STR_print("({%s _a = %s; %s _b = %s; if (_b == 0) THROW_PC(E_ZERO, %d); _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, _pc, op);
pop_stack(2);
push(type, "(%s)", expr);
STR_free(expr);
}
static void push_subr_and(ushort code, const char *op)
{
char *expr;
char *expr1, *expr2;
TYPE type1, type2, type;
check_stack(2);
type1 = get_type(-2);
type2 = get_type(-1);
if (TYPEID(type1) > TYPEID(type2))
type = type1;
else
type = type2;
switch(type)
{
case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG:
break;
case T_DATE: case T_STRING: case T_CSTRING:
type = T_BOOLEAN;
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr1 = peek(-2, type);
expr2 = peek(-1, type);
expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op);
pop_stack(2);
push(type, "(%s)", expr);
STR_free(expr);
}
static void push_subr_not(ushort code)
{
TYPE type;
char *expr;
char *op;
check_stack(1);
type = get_type(-1);
switch(type)
{
case T_BOOLEAN:
op = "!";
break;
case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG:
op = "~";
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr = STR_print("%s%s", op, peek(-1, type));
pop_stack(1);
push(type, "(%s)", expr);
STR_free(expr);
}
static bool push_subr_cat(ushort code)
{
int index;
ushort code_pop;
TYPE type;
if ((code & 0x3F) >= 2)
goto _DEFAULT_SUBR;
_pc++;
code_pop = _func->code[_pc];
if (PCODE_is(code_pop, C_POP_LOCAL))
{
index = (signed char)code_pop;
type = get_local_type(_func, index);
}
else if (PCODE_is(code_pop, C_POP_PARAM))
{
index = _func->n_param + (signed char)code_pop;
type = _func->param[index].type;
}
else if (PCODE_is(code_pop, C_POP_STATIC))
{
index = (code_pop & 0x7FF);
type = JIT_ctype_to_type(JIT_class, JIT_class->load->stat[index].type);
}
else if (PCODE_is(code_pop, C_POP_DYNAMIC))
{
index = (code_pop & 0x7FF);
type = JIT_ctype_to_type(JIT_class, JIT_class->load->dyn[index].type);
}
else
goto _DEFAULT_SUBR;
if (type != T_STRING)
goto _DEFAULT_SUBR;
declare(&_decl_as, "GB_STRING as");
_no_release = TRUE;
_no_release_but_borrow = TRUE;
pop(T_STRING, "as = %%s");
_no_release_but_borrow = FALSE;
_no_release = FALSE;
pop_stack(1);
//expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op);
if (PCODE_is(code_pop, C_POP_LOCAL))
{
JIT_print(" JIT.add_string_local(&l%d, as);\n", index);
}
else if (PCODE_is(code_pop, C_POP_PARAM))
{
JIT_print(" JIT.add_string_local(&p%d, as);\n", index);
}
else if (PCODE_is(code_pop, C_POP_STATIC))
{
void *addr = &JIT_class->stat[JIT_class->load->stat[index].pos];
JIT_print(" JIT.add_string_global(%p, as);\n", addr);
}
else if (PCODE_is(code_pop, C_POP_DYNAMIC))
{
int pos = JIT_class->load->dyn[index].pos;
JIT_print(" JIT.add_string_global(&OP[%d], as);\n", pos);
}
return TRUE;
_DEFAULT_SUBR:
push_subr(CALL_SUBR_CODE, code);
return FALSE;
//JIT_print(" THROW_TYPE_PC(GB_T_STRING, %p, %d);", (void *)type, _pc);
}
static void push_subr_comp(ushort code)
{
char *op = NULL;
char *expr;
char *expr1, *expr2;
TYPE type1, type2, type;
check_stack(2);
type1 = get_type(-2);
type2 = get_type(-1);
if (TYPEID(type1) > TYPEID(type2))
type = type1;
else
type = type2;
switch(type)
{
case T_BOOLEAN: case T_BYTE: case T_SHORT: case T_INTEGER: case T_LONG: case T_SINGLE: case T_FLOAT: case T_POINTER:
switch(code & 0xFF00)
{
case C_EQ: op = "=="; break;
case C_NE: op = "!="; break;
case C_GT: op = ">"; break;
case C_LT: op = "<"; break;
case C_GE: op = ">="; break;
case C_LE: op = "<="; break;
}
break;
}
if (!op)
{
switch(code & 0xFF00)
{
case C_EQ: case C_NE:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
break;
case C_GT: case C_LT: case C_GE: case C_LE:
push_subr(CALL_SUBR_UNKNOWN + CALL_RETURN_UNKNOWN, code);
break;
}
return;
}
expr1 = peek(-2, type);
expr2 = peek(-1, type);
expr = STR_print("({%s _a = %s; %s _b = %s; _a %s _b;})", JIT_get_ctype(type), expr1, JIT_get_ctype(type), expr2, op);
pop_stack(2);
push(T_BOOLEAN, "(%s)", expr);
STR_free(expr);
}
static void push_subr_bit(ushort code)
{
static const char *_actions[] = {
NULL,
"_v &= ~((%s)1 << _b)", // BClr
"_v |= ((%s)1 << _b)", // BSet
"((_v & ((%s)1 << _b)) != 0)", // BTst
"_v ^= ((%s)1 << _b)", // BChg
"_v = (((%s)_v << _b) & 0x%s) | (((%s)_v) & 0x%s)", // Asl
"_v = (((%s)_v >> _b) & 0x%s) | (((%s)_v) & 0x%s)", // Asr
"_v = ((%s)_v << _b) | ((%s)_v >> (%d - _b))", // Rol
"_v = ((%s)_v >> _b) | ((%s)_v << (%d - _b))", // Ror
"_v = ((%s)_v << _b)", // Lsl
"_v = ((%s)_v >> _b)", // Lsr
};
const char *action;
char *expr;
char *expr1, *expr2;
TYPE type;
const char *ctype, *uctype, *mask, *mask2;
int nbits;
check_stack(2);
type = get_type(-2);
switch(type)
{
case T_BYTE:
ctype = "uchar"; uctype = "uchar";
mask = "7F"; mask2 = "80";
nbits = 8;
break;
case T_SHORT:
ctype = "short"; uctype= "ushort";
mask = "7FFF"; mask2 = "8000";
nbits = 16;
break;
case T_INTEGER:
ctype = "int"; uctype = "uint";
mask = "7FFFFFFF"; mask2 = "80000000";
nbits = 32;
break;
case T_LONG:
ctype = "int64_t"; uctype= "uint64_t";
mask = "7FFFFFFFFFFFFFFFLL"; mask2 = "8000000000000000LL";
nbits = 64;
break;
default:
push_subr(CALL_SUBR_CODE + CALL_RETURN_UNKNOWN, code);
return;
}
expr1 = peek(-2, type);
expr2 = peek(-1, T_INTEGER);
code &= 0x3F;
action = _actions[code];
if (_unsafe)
expr = STR_print("({ %s _v = %s; int _b = %s; ", ctype, expr1, expr2, nbits);
else
expr = STR_print("({ %s _v = %s; int _b = %s; if ((_b < 0) || (_b >= %d)) THROW_PC(E_ARG, %d); ", ctype, expr1, expr2, nbits, _pc);
switch(code)
{
case 1: case 2: case 3: case 4:
STR_add(&expr, action, uctype);
break;
case 5: case 6:
STR_add(&expr, action, uctype, mask, uctype, mask2);
break;
case 7: case 8:
STR_add(&expr, action, uctype, uctype, nbits);
break;
case 9: case 10:
STR_add(&expr, action, uctype);
break;
}
if (code == 3)
STR_add(&expr, "; })");
else
STR_add(&expr, "; _v; })");
pop_stack(2);
push(code == 3 ? T_BOOLEAN : type, "(%s)", expr);
STR_free(expr);
}
static void push_subr_conv(ushort code)
{
char *expr;
TYPE type;
TYPE conv = code & 0x3F;
check_stack(1);
type = get_type(-1);
if (type == conv)
return;
expr = STR_copy(peek(-1, conv));
pop_stack(1);
push(conv, "(%s)", expr);
STR_free(expr);
}
static void push_subr_len(ushort code)
{
char *expr;
check_stack(1);
expr = STR_copy(peek(-1, T_STRING));
pop_stack(1);
push(T_INTEGER, "((%s).value.len)", expr);
STR_free(expr);
}
static void push_subr_left_right(ushort code, const char *func)
{
uint narg = code & 0x3F;
TYPE type;
char *expr_str;
char *expr_len = NULL;
check_stack(narg);
if (narg == 2)
{
expr_len = STR_copy(peek(-1, T_INTEGER));
pop_stack(1);
}
type = get_type(-1);
expr_str = STR_copy(peek(-1, T_STRING));
pop_stack(1);
push(type, "%s(%s, %s)", func, expr_str, expr_len ? expr_len : "1");
STR_free(expr_len);
STR_free(expr_str);
}
static void push_subr_mid(ushort code)
{
uint narg = code & 0x3F;
char *expr_len = NULL;
char *expr_start, *expr_str;
TYPE type;
check_stack(narg);
if (narg == 3)
{
expr_len = STR_copy(peek(-1, T_INTEGER));
pop_stack(1);
}
expr_start = STR_copy(peek(-1, T_INTEGER));
pop_stack(1);
type = get_type(-1);
expr_str = STR_copy(peek(-1, T_STRING));
pop_stack(1);
if (expr_len)
push(type, "SUBR_MID(%s, %s, %s, %d)", expr_str, expr_start, expr_len, _pc);
else
push(type, "SUBR_MID_END(%s, %s, %d)", expr_str, expr_start, _pc);
STR_free(expr_len);
STR_free(expr_start);
STR_free(expr_str);
}
static void push_call(ushort code)
{
char *call = NULL;
const char *def;
int i, j;
int narg;
FUNCTION *func;
CLASS_EXTERN *ext;
int func_kind, func_index;
TYPE func_type;
int nopt, opt;
int nv;
narg = code & 0x3F;
if (_stack_current > narg && get_type(- narg - 1) == T_FUNCTION)
{
STACK_SLOT *s = &_stack[_stack_current - narg - 1];
func_kind = s->func;
func_index = s->index;
func_type = T_UNKNOWN;
}
else
{
STACK_SLOT *s = &_stack[_stack_current - narg - 1];
func_kind = CALL_UNKNOWN;
func_type = s->call;
}
switch (func_kind)
{
case CALL_PRIVATE:
func = &JIT_class->load->func[func_index];
if (func->fast)
{
if (narg < func->npmin)
{
pop_stack(narg + 1);
push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_NEPARAM, %d); temp; })", _pc);
}
else if (narg > func->n_param && !func->vararg)
{
pop_stack(narg + 1);
push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_TMPARAM, %d); temp; })", _pc);
}
else
{
nv = 0;
if (func->vararg && (narg > func->n_param))
{
if (func->type != T_VOID)
STR_add(&call, "%s _r;", JIT_get_ctype(func->type));
nv = narg - func->n_param;
for (i = 0; i < nv; i++)
STR_add(&call, "%s;", push_expr(i - nv, get_type(i - nv)));
}
STR_add(&call, "SP=sp;");
if (nv && func->type != T_VOID)
STR_add(&call, "_r=");
STR_add(&call, "jit_%s_%d_(", JIT_prefix, func_index);
for (i = 0; i < func->npmin; i++)
{
if (i) STR_add(&call, ",");
STR_add(&call, "%s", peek(i - narg, func->param[i].type));
}
nopt = 0;
for (; i < func->n_param; i++)
{
if (i) STR_add(&call, ",");
if (nopt == 0)
{
opt = 0;
for (j = 0; j < 8; j++)
{
if ((i + j) >= func->n_param)
break;
if (((i + j) >= narg) || get_type(i + j - narg) == T_VOID)
opt |= 1 << j;
}
STR_add(&call, "%d,", opt);
}
if (i < narg)
STR_add(&call, "%s", peek(i - narg, func->param[i].type));
else
{
def = JIT_get_default_value(func->param[i].type);
STR_add(&call, "({ %s temp = %s; temp; })", JIT_get_ctype(func->param[i].type), def);
}
nopt++;
if (nopt >= 8)
nopt = 0;
}
if (func->vararg)
{
if (func->n_param)
STR_add(&call, ",");
STR_add(&call, "%d,&sp[-%d]", nv, nv);
}
STR_add(&call, ");");
if (nv)
{
STR_add(&call, "JIT.release_many(sp,%d);sp -= %d;", nv ,nv);
if (func->type != T_VOID)
STR_add(&call, "_r;");
}
pop_stack(narg + 1);
push(func->type, "({%s})", call);
}
}
else
{
STR_add(&call, "PUSH_PRIVATE_FUNCTION(%d);", func_index);
for (i = 0; i < narg; i++)
STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg)));
pop_stack(narg + 1);
STR_add(&call, "CALL_UNKNOWN(%d);POP_%s();", _pc, JIT_get_type(func->type));
push(func->type, "({%s})", call);
}
break;
case CALL_UNKNOWN:
narg++;
for (i = 0; i < narg; i++)
STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg)));
pop_stack(narg);
STR_add(&call, "CALL_UNKNOWN(%d);POP_%s();", _pc, JIT_get_type(func_type));
push(func_type, "({%s})", call);
break;
case CALL_EVENT:
for (i = 0; i < narg; i++)
STR_add(&call, "%s;", push_expr(i - narg, get_type(i - narg)));
pop_stack(narg + 1);
if (func_index != NO_SYMBOL)
STR_add(&call, "RAISE_EVENT(%d,%d);", func_index, narg);
else
STR_add(&call, "RAISE_UNKNOWN_EVENT(%d);", _pc);
push(T_BOOLEAN, "({%s})", call);
break;
case CALL_EXTERN:
ext = &JIT_class->load->ext[func_index];
if (narg < ext->n_param)
{
pop_stack(narg + 1);
push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_NEPARAM, %d); temp })", _pc);
}
else if (narg > ext->n_param && !ext->vararg)
{
pop_stack(narg + 1);
push(T_UNKNOWN, "({ GB_VALUE temp; THROW_PC(E_TMPARAM, %d); temp })", _pc);
}
else
{
STR_add(&call,"SP = sp;(*(%s (*)())%p)(", JIT_get_ctype(ext->type), JIT.get_extern(ext));
for (i = 0; i < ext->n_param; i++)
{
if (i) STR_add(&call, ",");
STR_add(&call, "%s", peek(i - narg, ext->param[i].type));
}
STR_add(&call, ");");
pop_stack(narg + 1);
push(ext->type, "({%s})", call);
}
break;
default:
JIT_panic("Unsupported call");
}
STR_free(call);
}
static void push_subr_isnan(ushort code)
{
char *func;
char *expr;
check_stack(1);
switch (code & 0xFF)
{
case 1: // IsNan
func = "isnan";
break;
case 2: // IsInf
func = "isinf";
break;
default:
push_subr(CALL_SUBR_CODE, code);
return;
}
expr = STR_print("%s(%s) != 0", func, peek(-1, T_FLOAT));
pop_stack(1);
push(T_BOOLEAN, "(%s)", expr);
STR_free(expr);
}
static void push_subr_math(ushort code)
{
static const char *func[] = {
NULL, "frac(%s)", "__builtin_log(%s)", "__builtin_exp(%s)", "__builtin_sqrt(%s)", "__builtin_sin(%s)", "__builtin_cos(%s)", "__builtin_tan(%s)",
"__builtin_atan(%s)", "__builtin_asin(%s)", "__builtin_acos(%s)",
"((%s) * 180 / M_PI)", // deg()
"((%s) * M_PI / 180)", // rad()
"log10(%s)",
"__builtin_sinh(%s)", "__builtin_cosh(%s)", "__builtin_tanh(%s)", "__builtin_asinh(%s)", "__builtin_acosh(%s)", "__builtin_atanh(%s)",
#ifndef HAVE_EXP2
"pow(2, (%s))",
#else
"__builtin_exp2(%s)",
#endif
#ifndef HAVE_EXP10
"pow(10, (%s))",
#else
"__builtin_exp10(%s)",
#endif
#ifndef HAVE_LOG2
"(log(%s) / M_LN2)",
#else
"__builtin_log2(%s)",
#endif
"__builtin_cbrt(%s)", "__builtin_expm1(%s)", "__builtin_log1p(%s)", "__builtin_floor(%s)", "__builtin_ceil(%s)"
};
char *expr;
check_stack(1);
expr = STR_print(func[code & 0x1F], peek(-1, T_FLOAT));
pop_stack(1);
push(T_FLOAT, "%s(%s)", _unsafe ? "CALL_MATH_UNSAFE" : "CALL_MATH", expr);
STR_free(expr);
}
static void push_subr_pi(ushort code)
{
char *expr;
if ((code & 0xFF) == 0)
{
push(T_FLOAT, "M_PI");
return;
}
check_stack(1);
expr = STR_copy(peek(-1, T_FLOAT));
pop_stack(1);
push(T_FLOAT, "(M_PI*(%s))", expr);
STR_free(expr);
}
static void push_complex(void)
{
char *expr;
expr = STR_copy(peek(-1, T_FLOAT));
pop_stack(1);
push(T_OBJECT, "PUSH_COMPLEX(%s)", expr);
STR_free(expr);
}
static void push_event(bool unknown, int index)
{
CLASS_DESC *desc;
const char *name;
if (unknown)
{
name = JIT_class->load->unknown[index];
// The ':' is already in the name, thanks to the compiler.
index = JIT_find_symbol(JIT_class, name);
if (index != NO_SYMBOL)
{
desc = JIT_class->table[index].desc;
if (CLASS_DESC_get_type(desc) == CD_EVENT)
index = desc->event.index;
else
index = NO_SYMBOL;
}
}
else if (JIT_class->parent)
index += JIT_class->parent->n_event;
push_function(CALL_EVENT, index);
}
static void push_subr_varptr(ushort code)
{
ushort op;
char var[16];
int index;
TYPE type;
char *expr;
check_stack(1);
op = (ushort)atoi(get_expr(-1));
pop_stack(1);
if ((code & 0xFF) == 1) // IsMissing
{
push(T_BOOLEAN, "(o%d & %d)", op / 8, 1 << (op % 8));
return;
}
if ((op & 0xFF00) == C_PUSH_LOCAL || (op & 0xFF00) == C_PUSH_PARAM)
{
if ((op & 0xFF00) == C_PUSH_PARAM)
{
index = _func->n_param + (signed char)(op & 0xFF);
type = _func->param[index].type;
sprintf(var, "p%d", index);
}
else
{
index = op & 0xFF;
type = get_local_type(_func, index);
sprintf(var, "l%d", index);
}
switch(TYPEID(type))
{
case T_BOOLEAN:
case T_BYTE:
case T_SHORT:
case T_INTEGER:
case T_LONG:
case T_SINGLE:
case T_FLOAT:
case T_POINTER:
expr = STR_print("&%s", var);
break;
case T_DATE:
case T_OBJECT:
expr = STR_print("&%s.value", var);
break;
/*case T_STRING:
case T_CSTRING:
expr = STR_print("(%s.value.addr + %s.value.start)", var, var);
break;*/
case T_VARIANT:
expr = STR_print("(%s.value.type == GB_T_STRING ? %s.value.value._string : &%s.value.value.data)", var, var, var);
break;
default:
push(T_POINTER, "(THROW_PC(E_UTYPE, %d),(intptr_t)0)", _pc);
return;
}
}
else if ((op & 0xF800) == C_PUSH_DYNAMIC)
{
expr = STR_print("(&OP[%d])", JIT_class->load->dyn[op & 0x7FF].pos);
}
else if ((op & 0xF800) == C_PUSH_STATIC)
{
expr = STR_print("%p", JIT_class->stat + JIT_class->load->stat[op & 0x7FF].pos);
}
else
goto _ILLEGAL;
push(T_POINTER, "((intptr_t)%s)", expr);
STR_free(expr);
return;
_ILLEGAL:
JIT_panic("unsupported VarPtr()");
}
static void push_subr_ptr(ushort code)
{
char *expr;
TYPE type;
check_stack(1);
if (_unsafe)
{
type = get_type(-1);
switch (type)
{
case T_POINTER:
case T_STRING:
case T_CSTRING:
expr = STR_copy(peek(-1, type));
pop_stack(1);
code &= 0xF;
if (type == T_POINTER)
push(code, "*(%s *)(%s)", JIT_get_ctype(code), expr);
else
push(code, "*(%s *)GET_STRING_ADDR(%s)", JIT_get_ctype(code), expr);
STR_free(expr);
return;
}
}
push_subr(CALL_SUBR_CODE, code);
}
#define GET_XXX() (((signed short)(code << 4)) >> 4)
#define GET_UXX() (code & 0xFFF)
#define GET_7XX() (code & 0x7FF)
#define GET_XX() ((signed char)code)
#define GET_UX() ((unsigned char)code)
#define GET_3X() (code & 0x3F)
#define TEST_XX() (code & 1)
#define PC (&func->code[p])
bool JIT_translate_body(FUNCTION *func, int ind)
{
static const void *jump_table[256] =
{
/* 00 NOP */ &&_MAIN,
/* 01 PUSH LOCAL */ &&_PUSH_LOCAL,
/* 02 PUSH PARAM */ &&_PUSH_PARAM,
/* 03 PUSH ARRAY */ &&_PUSH_ARRAY,
/* 04 PUSH UNKNOWN */ &&_PUSH_UNKNOWN,
/* 05 PUSH EXTERN */ &&_PUSH_EXTERN,
/* 06 BYREF */ &&_BYREF,
/* 07 PUSH EVENT */ &&_PUSH_EVENT,
/* 08 QUIT */ &&_QUIT,
/* 09 POP LOCAL */ &&_POP_LOCAL,
/* 0A POP PARAM */ &&_POP_PARAM,
/* 0B POP ARRAY */ &&_POP_ARRAY,
/* 0C POP UNKNOWN */ &&_POP_UNKNOWN,
/* 0D POP OPTIONAL */ &&_POP_OPTIONAL,
/* 0E POP CTRL */ &&_POP_CTRL,
/* 0F BREAK */ &&_BREAK,
/* 10 RETURN */ &&_RETURN,
/* 11 PUSH SHORT */ &&_PUSH_SHORT,
/* 12 PUSH INTEGER */ &&_PUSH_INTEGER,
/* 13 PUSH CHAR */ &&_PUSH_CHAR,
/* 14 PUSH MISC */ &&_PUSH_MISC,
/* 15 PUSH ME */ &&_PUSH_ME,
/* 16 TRY */ &&_TRY,
/* 17 END TRY */ &&_END_TRY,
/* 18 CATCH */ &&_CATCH,
/* 19 DUP */ &&_DUP,
/* 1A DROP */ &&_DROP,
/* 1B NEW */ &&_NEW,
/* 1C CALL */ &&_CALL,
/* 1D CALL QUICK */ &&_CALL_QUICK,
/* 1E CALL EASY */ &&_CALL_SLOW,
/* 1F ON */ &&_ON_GOTO_GOSUB,
/* 20 JUMP */ &&_JUMP,
/* 21 JUMP IF TRUE */ &&_JUMP_IF_TRUE,
/* 22 JUMP IF FALSE */ &&_JUMP_IF_FALSE,
/* 23 GOSUB */ &&_GOSUB,
/* 24 JUMP FIRST */ &&_JUMP_FIRST,
/* 25 JUMP NEXT */ &&_JUMP_NEXT,
/* 26 FIRST */ &&_ENUM_FIRST,
/* 27 NEXT */ &&_ENUM_NEXT,
/* 28 = */ &&_SUBR_COMPE,
/* 29 <> */ &&_SUBR_COMPN,
/* 2A > */ &&_SUBR_COMPGT,
/* 2B <= */ &&_SUBR_COMPLE,
/* 2C < */ &&_SUBR_COMPLT,
/* 2D >= */ &&_SUBR_COMPGE,
/* 2E == */ &&_SUBR,
/* 2F CASE */ &&_SUBR_CODE,
/* 30 + */ &&_SUBR_ADD,
/* 31 - */ &&_SUBR_SUB,
/* 32 * */ &&_SUBR_MUL,
/* 33 / */ &&_SUBR_DIV,
/* 34 NEG */ &&_SUBR_NEG,
/* 35 \ */ &&_SUBR_QUO,
/* 36 MOD */ &&_SUBR_REM,
/* 37 ^ */ &&_SUBR_CODE,
/* 38 AND */ &&_SUBR_AND,
/* 39 OR */ &&_SUBR_OR,
/* 3A XOR */ &&_SUBR_XOR,
/* 3B NOT */ &&_SUBR_NOT,
/* 3C & */ &&_SUBR_CAT,
/* 3D LIKE */ &&_SUBR_CODE,
/* 3E &/ */ &&_SUBR_CODE,
/* 3F Is */ &&_SUBR_CODE,
/* 40 Left$ */ &&_SUBR_LEFT,
/* 41 Mid$ */ &&_SUBR_MID,
/* 42 Right$ */ &&_SUBR_RIGHT,
/* 43 Len */ &&_SUBR_LEN,
/* 44 Space$ */ &&_SUBR,
/* 45 String$ */ &&_SUBR,
/* 46 Trim$ */ &&_SUBR_CODE,
/* 47 UCase$ */ &&_SUBR_CODE,
/* 48 Oct$ */ &&_SUBR_CODE,
/* 49 Chr$ */ &&_SUBR,
/* 4A Asc */ &&_SUBR_CODE,
/* 4B InStr */ &&_SUBR_CODE,
/* 4C RInStr */ &&_SUBR_CODE,
/* 4D Subst$ */ &&_SUBR_CODE,
/* 4E Replace$ */ &&_SUBR_CODE,
/* 4F Split */ &&_SUBR_CODE,
/* 50 Scan */ &&_SUBR,
/* 51 Comp */ &&_SUBR_CODE,
/* 52 Conv */ &&_SUBR,
/* 53 DConv */ &&_SUBR_CODE,
/* 54 Abs */ &&_SUBR_ABS,
/* 55 Int */ &&_SUBR_INT,
/* 56 Fix */ &&_SUBR_FIX,
/* 57 Sgn */ &&_SUBR_SGN,
/* 58 Frac... */ &&_SUBR_MATH,
/* 59 Pi */ &&_SUBR_PI,
/* 5A Round */ &&_SUBR_CODE,
/* 5B Randomize */ &&_SUBR_CODE,
/* 5C Rnd */ &&_SUBR_CODE,
/* 5D Min */ &&_SUBR_CODE,
/* 5E Max */ &&_SUBR_CODE,
/* 5F IIf */ &&_SUBR_CODE,
/* 60 Choose */ &&_SUBR_CODE,
/* 61 Array */ &&_SUBR_CODE,
/* 62 ATan2... */ &&_SUBR_CODE,
/* 63 IsAscii... */ &&_SUBR_CODE,
/* 64 BClr... */ &&_SUBR_BIT,
/* 65 IsBoolean... */ &&_SUBR_CODE,
/* 66 TypeOf */ &&_SUBR_CODE,
/* 67 CBool... */ &&_SUBR_CONV,
/* 68 Bin$ */ &&_SUBR_CODE,
/* 69 Hex$ */ &&_SUBR_CODE,
/* 6A Val */ &&_SUBR,
/* 6B Str */ &&_SUBR,
/* 6C Format */ &&_SUBR_CODE,
/* 6D Timer */ &&_SUBR,
/* 6E Now */ &&_SUBR,
/* 6F Year... */ &&_SUBR_CODE,
/* 70 Week */ &&_SUBR_CODE,
/* 71 Date */ &&_SUBR_CODE,
/* 72 Time... */ &&_SUBR_CODE,
/* 73 DateAdd... */ &&_SUBR_CODE,
/* 74 Eval */ &&_SUBR_CODE,
/* 75 Error */ &&_SUBR,
/* 76 Debug */ &&_SUBR_CODE,
/* 77 Wait */ &&_SUBR_CODE,
/* 78 Open */ &&_SUBR_CODE,
/* 79 Close */ &&_SUBR,
/* 7A Input */ &&_SUBR_CODE,
/* 7B LineInput */ &&_SUBR,
/* 7C Print */ &&_SUBR_CODE,
/* 7D Read */ &&_SUBR_CODE,
/* 7E Write */ &&_SUBR_CODE,
/* 7F Flush */ &&_SUBR,
/* 80 Lock... */ &&_SUBR_CODE,
/* 81 InputFrom... */ &&_SUBR_CODE,
/* 82 Eof */ &&_SUBR_CODE,
/* 83 Lof */ &&_SUBR_CODE,
/* 84 Seek */ &&_SUBR_CODE,
/* 85 Kill */ &&_SUBR_CODE,
/* 86 Mkdir */ &&_SUBR_CODE,
/* 87 Rmdir */ &&_SUBR_CODE,
/* 88 Move */ &&_SUBR_CODE,
/* 89 Copy */ &&_SUBR_CODE,
/* 8A Link */ &&_SUBR_ISNAN,
/* 8B Exist */ &&_SUBR_CODE,
/* 8C Access */ &&_SUBR_CODE,
/* 8D Stat */ &&_SUBR_CODE,
/* 8E Dfree */ &&_SUBR,
/* 8F Temp$ */ &&_SUBR_CODE,
/* 90 IsDir */ &&_SUBR,
/* 91 Dir */ &&_SUBR_CODE,
/* 92 RDir */ &&_SUBR_CODE,
/* 93 Exec... */ &&_SUBR_CODE,
/* 94 Alloc */ &&_SUBR_CODE,
/* 95 Free */ &&_SUBR,
/* 96 Realloc */ &&_SUBR_CODE,
/* 97 StrPtr */ &&_SUBR_CODE,
/* 98 Sleep... */ &&_SUBR_CODE,
/* 99 VarPtr */ &&_SUBR_VARPTR,
/* 9A Collection */ &&_SUBR_CODE,
/* 9B Tr$ */ &&_SUBR,
/* 9C Quote$... */ &&_SUBR_CODE,
/* 9D Unquote$... */ &&_SUBR_CODE,
/* 9E MkInt$... */ &&_SUBR_CODE,
/* 9F Byte@... */ &&_SUBR_PTR,
/* A0 ADD QUICK */ &&_ADD_QUICK,
/* A1 ADD QUICK */ &&_ADD_QUICK,
/* A2 ADD QUICK */ &&_ADD_QUICK,
/* A3 ADD QUICK */ &&_ADD_QUICK,
/* A4 ADD QUICK */ &&_ADD_QUICK,
/* A5 ADD QUICK */ &&_ADD_QUICK,
/* A6 ADD QUICK */ &&_ADD_QUICK,
/* A7 ADD QUICK */ &&_ADD_QUICK,
/* A8 ADD QUICK */ &&_ADD_QUICK,
/* A9 ADD QUICK */ &&_ADD_QUICK,
/* AA ADD QUICK */ &&_ADD_QUICK,
/* AB ADD QUICK */ &&_ADD_QUICK,
/* AC ADD QUICK */ &&_ADD_QUICK,
/* AD ADD QUICK */ &&_ADD_QUICK,
/* AE ADD QUICK */ &&_ADD_QUICK,
/* AF ADD QUICK */ &&_ADD_QUICK,
/* B0 PUSH CLASS */ &&_PUSH_CLASS,
/* B1 PUSH CLASS */ &&_PUSH_CLASS,
/* B2 PUSH CLASS */ &&_PUSH_CLASS,
/* B3 PUSH CLASS */ &&_PUSH_CLASS,
/* B4 PUSH CLASS */ &&_PUSH_CLASS,
/* B5 PUSH CLASS */ &&_PUSH_CLASS,
/* B6 PUSH CLASS */ &&_PUSH_CLASS,
/* B7 PUSH CLASS */ &&_PUSH_CLASS,
/* B8 PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* B9 PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* BA PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* BB PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* BC PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* BD PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* BE PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* BF PUSH FUNCTION */ &&_PUSH_FUNCTION,
/* C0 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C1 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C2 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C3 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C4 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C5 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C6 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C7 PUSH DYNAMIC */ &&_PUSH_DYNAMIC,
/* C8 PUSH STATIC */ &&_PUSH_STATIC,
/* C9 PUSH STATIC */ &&_PUSH_STATIC,
/* CA PUSH STATIC */ &&_PUSH_STATIC,
/* CB PUSH STATIC */ &&_PUSH_STATIC,
/* CC PUSH STATIC */ &&_PUSH_STATIC,
/* CD PUSH STATIC */ &&_PUSH_STATIC,
/* CE PUSH STATIC */ &&_PUSH_STATIC,
/* CF PUSH STATIC */ &&_PUSH_STATIC,
/* D0 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D1 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D2 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D3 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D4 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D5 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D6 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D7 POP DYNAMIC */ &&_POP_DYNAMIC,
/* D8 POP STATIC */ &&_POP_STATIC,
/* D9 POP STATIC */ &&_POP_STATIC,
/* DA POP STATIC */ &&_POP_STATIC,
/* DB POP STATIC */ &&_POP_STATIC,
/* DC POP STATIC */ &&_POP_STATIC,
/* DD POP STATIC */ &&_POP_STATIC,
/* DE POP STATIC */ &&_POP_STATIC,
/* DF POP STATIC */ &&_POP_STATIC,
/* E0 PUSH CONST */ &&_PUSH_CONST,
/* E1 PUSH CONST */ &&_PUSH_CONST,
/* E2 PUSH CONST */ &&_PUSH_CONST,
/* E3 PUSH CONST */ &&_PUSH_CONST,
/* E4 PUSH CONST */ &&_PUSH_CONST,
/* E5 PUSH CONST */ &&_PUSH_CONST,
/* E6 PUSH CONST */ &&_PUSH_CONST,
/* E7 PUSH CONST */ &&_PUSH_CONST,
/* E8 PUSH CONST */ &&_PUSH_CONST,
/* E9 PUSH CONST */ &&_PUSH_CONST,
/* EA PUSH CONST */ &&_PUSH_CONST,
/* EB PUSH CONST */ &&_PUSH_CONST,
/* EC PUSH CONST */ &&_PUSH_CONST,
/* ED PUSH CONST */ &&_PUSH_CONST,
/* EE PUSH CONST */ &&_PUSH_CONST,
/* EF PUSH CONST */ &&_PUSH_CONST_EX,
/* F0 PUSH QUICK */ &&_PUSH_QUICK,
/* F1 PUSH QUICK */ &&_PUSH_QUICK,
/* F2 PUSH QUICK */ &&_PUSH_QUICK,
/* F3 PUSH QUICK */ &&_PUSH_QUICK,
/* F4 PUSH QUICK */ &&_PUSH_QUICK,
/* F5 PUSH QUICK */ &&_PUSH_QUICK,
/* F6 PUSH QUICK */ &&_PUSH_QUICK,
/* F7 PUSH QUICK */ &&_PUSH_QUICK,
/* F8 PUSH QUICK */ &&_PUSH_QUICK,
/* F9 PUSH QUICK */ &&_PUSH_QUICK,
/* FA PUSH QUICK */ &&_PUSH_QUICK,
/* FB PUSH QUICK */ &&_PUSH_QUICK,
/* FC PUSH QUICK */ &&_PUSH_QUICK,
/* FD PUSH QUICK */ &&_PUSH_QUICK,
/* FE PUSH QUICK */ &&_PUSH_QUICK,
/* FF PUSH QUICK */ &&_PUSH_QUICK
};
CLASS *class = JIT_class;
TYPE type;
CTYPE ctype;
uint p = 0;
ushort code;
int index;
int size = JIT_get_code_size(func);
void *addr;
int pos;
int i;
enter_function(func, ind);
//JIT_print(" JIT.debug(\"SP = %%p\\n\", SP);\n");
goto _MAIN;
_MAIN:
//fprintf(stderr, "[%d] %d\n", p, _stack_current);
if (_has_finally && p == func->error)
print_catch();
if (!JIT_last_print_is_label)
print_label(func, p);
if (p >= (size - 1)) // ignore the last opcode which is RETURN
return leave_function(func, ind);
_pc = p;
code = func->code[p++];
//fprintf(stderr, "--------—-------------------- %d / %04X\n", _stack_current, code);
goto *jump_table[code >> 8];
_DUP:
check_stack(1);
_has_just_dup = TRUE;
index = GB.Count(_dup_type);
*(TYPE *)GB.Add(&_dup_type) = type = get_type(-1);
//JIT_print_decl(" %s d%d;\n", JIT_get_ctype(type), index);
JIT_declare(type, "d%d", index);
//_no_release = TRUE;
pop(type, "d%d", index);
//_no_release = FALSE;
push(type, "d%d", index);
push(type, "d%d", index);
goto _MAIN;
_PUSH_LOCAL:
index = GET_XX();
type = get_local_type(func, index);
if (index >= func->n_local)
{
index = _ctrl_index[index - func->n_local];
CTRL_INFO *info = &_ctrl_info[index];
if (info->expr)
push(type, info->expr);
else
push(type, "c%d", index);
}
else
push(type, "l%d", index);
goto _MAIN;
_POP_LOCAL:
index = GET_XX();
type = get_local_type(func, index);
if (index >= func->n_local)
{
pop(type, "c%d", _ctrl_index[index - func->n_local]);
}
else
{
if (check_swap(type, "l%d = %%s", index))
pop(type, "l%d", index);
}
goto _MAIN;
_POP_CTRL:
pop_ctrl(GET_XX() - func->n_local, T_VOID);
goto _MAIN;
_PUSH_PARAM:
index = func->n_param + GET_XX();
push(func->param[index].type, "p%d", index);
goto _MAIN;
_POP_PARAM:
index = func->n_param + GET_XX();
type = func->param[index].type;
if (check_swap(type, "p%d = %%s", index))
pop(type, "p%d", index);
goto _MAIN;
_PUSH_QUICK:
push(T_INTEGER, "%d", GET_XXX());
goto _MAIN;
_PUSH_SHORT:
push(T_INTEGER, "%d", (short)PC[0]);
p++;
goto _MAIN;
_PUSH_INTEGER:
push(T_INTEGER, "%d", PC[0] | ((uint)PC[1] << 16));
p += 2;
goto _MAIN;
_PUSH_STATIC:
index = GET_7XX();
ctype = class->load->stat[index].type;
addr = &class->stat[class->load->stat[index].pos];
push_static_variable(JIT_class, ctype, addr);
goto _MAIN;
_POP_STATIC:
index = GET_7XX();
ctype = class->load->stat[index].type;
addr = &class->stat[class->load->stat[index].pos];
pop_static_variable(JIT_class, ctype, addr);
goto _MAIN;
_PUSH_DYNAMIC:
index = GET_7XX();
pos = class->load->dyn[index].pos;
ctype = class->load->dyn[index].type;
push_dynamic_variable(JIT_class, ctype, pos, "OP");
goto _MAIN;
_POP_DYNAMIC:
index = GET_7XX();
pos = class->load->dyn[index].pos;
ctype = class->load->dyn[index].type;
pop_dynamic_variable(JIT_class, ctype, pos, "OP");
goto _MAIN;
_PUSH_MISC:
switch (GET_UX())
{
case 0:
push(T_NULL, "NULL");
break;
case 1:
push(T_VOID, NULL);
break;
case 2:
push(T_BOOLEAN, "0");
break;
case 3:
push(T_BOOLEAN, "(-1)");
break;
case 4:
push(T_OBJECT, "GET_LAST()");
break;
case 5:
push(T_CSTRING, "GET_CSTRING(\"\", 0, 0)");
break;
case 6:
push(T_FLOAT, "INFINITY");
break;
case 7:
push(T_FLOAT, "-INFINITY");
break;
case 8:
push_complex();
break;
/*case 9:
EXEC_push_vargs();
break;
case 10:
EXEC_drop_vargs();
break;
case 11:
EXEC_end_vargs();
break;
*/
default:
goto _ILLEGAL;
}
goto _MAIN;
_PUSH_CHAR:
push(T_CSTRING, "GET_CHAR(%d)", GET_UX());
goto _MAIN;
_POP_OPTIONAL:
check_stack(1);
if (get_type(-1) == T_VOID)
{
pop_stack(1);
JIT_last_print_is_label = FALSE;
}
else
{
index = func->n_param + GET_XX() - func->npmin;
JIT_print(" if (o%d & %d)\n ", index / 8, (1 << (index % 8)));
index = func->n_param + GET_XX();
type = func->param[index].type;
_no_release = TRUE;
_no_release_but_borrow = TRUE;
pop(type, "p%d = %%s", index);
_no_release_but_borrow = FALSE;
_no_release = FALSE;
}
goto _MAIN;
_PUSH_CLASS:
index = GET_7XX();
type = (TYPE)class->load->class_ref[index];
push(T_CLASS, "CLASS(%p)", (CLASS *)type);
goto _MAIN;
_PUSH_FUNCTION:
push_function(CALL_PRIVATE, GET_7XX());
goto _MAIN;
_PUSH_ME:
index = GET_UX();
if (index & 2)
push((TYPE)(JIT_class->parent), "GET_SUPER(%p)", JIT_class->parent);
else
push((TYPE)JIT_class, "GET_ME(%p)", JIT_class);
goto _MAIN;
/*if (GET_UX() & 2)
{
// The used class must be in the stack, because it is tested by exec_push && exec_pop
if (LIKELY(OP != NULL))
{
SP->_object.class = SP->_object.class->parent;
SP->_object.super = EXEC_super;
}
else
{
SP->_class.class = SP->_class.class->parent;
SP->_class.super = EXEC_super;
}
EXEC_super = SP;
//fprintf(stderr, "%s\n", DEBUG_get_current_position());
//BREAKPOINT();
}
PUSH();
goto _NEXT;*/
_PUSH_UNKNOWN:
push_unknown(PC[0]);
p++;
goto _MAIN;
_POP_UNKNOWN:
pop_unknown(PC[0]);
p++;
goto _MAIN;
_CALL:
push_call(code);
goto _MAIN;
_SUBR:
push_subr(CALL_SUBR, code);
goto _MAIN;
_SUBR_CODE:
push_subr(CALL_SUBR_CODE, code);
goto _MAIN;
_DROP:
pop(T_VOID, NULL);
goto _MAIN;
_NEW:
push_subr(CALL_NEW, code);
goto _MAIN;
_RETURN:
switch(code & 0xFF)
{
case 0:
if (_try_finished)
JIT_print(" RETURN();\n");
else
JIT_print(" RETURN_LEAVE_TRY();\n");
break;
case 1:
pop(func->type, "r");
if (!_try_finished) JIT_print(" LEAVE_TRY();\n");
JIT_print(" goto __RETURN;\n");
break;
case 2:
if (!_try_finished) JIT_print(" LEAVE_TRY();\n");
JIT_print(" goto __RETURN;\n");
break;
default:
goto _ILLEGAL;
}
goto _MAIN;
_GOSUB:
JIT_print(" PUSH_GOSUB(__L%d); goto __L%d;\n", p + 1, p + (signed short)PC[0] + 1);
JIT_print("__L%d:;\n", p + 1);
p++;
goto _MAIN;
_JUMP:
JIT_print(" goto __L%d;\n", p + (signed short)PC[0] + 1);
p++;
goto _MAIN;
_JUMP_IF_TRUE:
JIT_print(" if (%s) goto __L%d;\n", peek(-1, T_BOOLEAN), p + (signed short)PC[0] + 1);
pop_stack(1);
p++;
goto _MAIN;
_JUMP_IF_FALSE:
JIT_print(" if (!(%s)) goto __L%d;\n", peek(-1, T_BOOLEAN), p + (signed short)PC[0] + 1);
pop_stack(1);
p++;
goto _MAIN;
_JUMP_FIRST:
index = PC[2] & 0xFF;
type = get_local_type(func, index);
if (strcmp(get_expr(-1), "1") == 0)
_loop_type = LOOP_UP;
else if (strcmp(get_expr(-1), "-1") == 0)
_loop_type = LOOP_DOWN;
else
_loop_type = LOOP_UNKNOWN;
pop(type, "const %s i%d", JIT_get_ctype(type), _loop_count);
pop(type, "const %s e%d", JIT_get_ctype(type), _loop_count);
JIT_print(" goto __L%ds;\n", p + 1);
goto _MAIN;
_JUMP_NEXT:
index = PC[1] & 0xFF;
JIT_print(" l%d += i%d;\n", index, _loop_count);
JIT_print("__L%ds:\n", p);
switch(_loop_type)
{
case LOOP_UP:
JIT_print(" if (l%d > e%d) goto __L%d;\n", index, _loop_count, p + (signed short)PC[0] + 1);
break;
case LOOP_DOWN:
JIT_print(" if (l%d < e%d) goto __L%d;\n", index, _loop_count, p + (signed short)PC[0] + 1);
break;
case LOOP_UNKNOWN:
JIT_print(" if (((i%d > 0) && (l%d > e%d)) || ((i%d < 0) && (l%d < e%d))) goto __L%d;\n", _loop_count, index, _loop_count, _loop_count, index, _loop_count, p + (signed short)PC[0] + 1);
}
_loop_count++;
p +=2;
goto _MAIN;
_ENUM_FIRST:
index = GET_XX() - func->n_local;
type = get_type(-1);
if (!TYPE_is_object(type) && type != T_UNKNOWN)
{
JIT_print(" THROW_PC(E_NOBJECT, %d);\n", _pc);
pop_stack(1);
add_ctrl(index, T_OBJECT, NULL);
add_ctrl(index + 1, T_OBJECT, NULL);
}
else
{
pop_ctrl(index, type);
add_ctrl(index + 1, T_OBJECT, NULL);
JIT_print(" ENUM_FIRST(0x%04X, c%d, c%d);\n", code, _ctrl_index[index], _ctrl_index[index + 1]);
}
goto _MAIN;
_ENUM_NEXT:
index = (PC[-2] & 0xFF) - func->n_local;
JIT_print(" ENUM_NEXT(0x%04X, c%d, c%d, __L%d);\n", code, _ctrl_index[index], _ctrl_index[index + 1], p + (signed short)PC[0] + 1);
if ((code & 1) == 0)
push(T_UNKNOWN, "POP_u()");
p++;
goto _MAIN;
_PUSH_CONST:
index = GET_UXX();
push_constant(class, index);
goto _MAIN;
_PUSH_CONST_EX:
push_constant(class, PC[0]);
p++;
goto _MAIN;
_PUSH_ARRAY:
push_array(code);
goto _MAIN;
_POP_ARRAY:
pop_array(code);
goto _MAIN;
_ADD_QUICK:
index = GET_XXX();
push(T_INTEGER, "%d", abs(index));
if (index < 0)
{
PC[-1] = code = 0x3100;
goto _SUBR_SUB;
}
else
PC[-1] = code = 0x3000;
_SUBR_ADD:
push_subr_add(code, "+", "|", TRUE);
goto _MAIN;
_SUBR_SUB:
push_subr_add(code, "-", "^", TRUE);
goto _MAIN;
_SUBR_MUL:
push_subr_add(code, "*", "&", FALSE);
goto _MAIN;
_SUBR_DIV:
push_subr_div(code);
goto _MAIN;
_SUBR_NEG:
push_subr_arithmetic(MATH_NEG, code);
goto _MAIN;
_SUBR_QUO:
push_subr_quo(code, "/");
goto _MAIN;
_SUBR_REM:
push_subr_quo(code, "%");
goto _MAIN;
_SUBR_AND:
push_subr_and(code, "&");
goto _MAIN;
_SUBR_OR:
push_subr_and(code, "|");
goto _MAIN;
_SUBR_XOR:
push_subr_and(code, "^");
goto _MAIN;
_SUBR_NOT:
push_subr_not(code);
goto _MAIN;
_SUBR_CAT:
if (push_subr_cat(code))
p++;
goto _MAIN;
_SUBR_ABS:
push_subr_arithmetic(MATH_ABS, code);
goto _MAIN;
_SUBR_SGN:
push_subr_arithmetic(MATH_SGN, code);
goto _MAIN;
_SUBR_INT:
push_subr_float_arithmetic(MATH_INT, code);
goto _MAIN;
_SUBR_FIX:
push_subr_float_arithmetic(MATH_FIX, code);
goto _MAIN;
_SUBR_COMPE:
_SUBR_COMPN:
_SUBR_COMPGT:
_SUBR_COMPLE:
_SUBR_COMPLT:
_SUBR_COMPGE:
push_subr_comp(code);
goto _MAIN;
_SUBR_ISNAN:
push_subr_isnan(code);
goto _MAIN;
_SUBR_CONV:
push_subr_conv(code);
goto _MAIN;
_SUBR_LEFT:
push_subr_left_right(code, "SUBR_LEFT");
goto _MAIN;
_SUBR_MID:
push_subr_mid(code);
goto _MAIN;
_SUBR_RIGHT:
push_subr_left_right(code, "SUBR_RIGHT");
goto _MAIN;
_SUBR_LEN:
push_subr_len(code);
goto _MAIN;
_SUBR_MATH:
push_subr_math(code);
goto _MAIN;
_SUBR_PI:
push_subr_pi(code);
goto _MAIN;
_SUBR_BIT:
push_subr_bit(code);
goto _MAIN;
_SUBR_VARPTR:
push_subr_varptr(code);
goto _MAIN;
_SUBR_PTR:
push_subr_ptr(code);
goto _MAIN;
_BREAK:
goto _MAIN;
_QUIT:
JIT_print(" ");
if ((code & 3) == 3)
{
check_stack(1);
JIT_print("%s,", push_expr(-1, T_BYTE));
pop_stack(1);
}
JIT_print(" QUIT(0x%04X);\n", code);
goto _MAIN;
_TRY:
declare(&_decl_tp, "VALUE *tp");
JIT_print(" tp = EP; EP = sp;\n");
JIT_print(" { VALUE *volatile sp = EP; TRY {\n");
p++;
goto _MAIN;
_END_TRY:
JIT_print(" *JIT.got_error = 0;\n");
JIT_print(" } CATCH {\n");
JIT_print(" if (SP > sp) sp = SP; else SP = sp;\n");
JIT_print(" LEAVE_SUPER();\n");
//JIT_print(" if (sp > EP) { fprintf(stderr, \"release try %%d\\n\", sp - EP); JIT.release_many(sp, sp - EP); SP = sp = EP; }\n");
JIT_print(" if (sp > EP) { JIT.release_many(sp, sp - EP); SP = sp = EP; }\n");
JIT_print(" *JIT.got_error = 1;\n");
JIT_print(" JIT.error_set_last(FALSE);\n");
JIT_print(" } END_TRY }\n");
JIT_print(" EP = tp;\n");
goto _MAIN;
_CATCH:
_has_catch = TRUE;
if (!_has_finally)
print_catch();
JIT_print(" if (!error) goto __RELEASE;\n");
goto _MAIN;
_ON_GOTO_GOSUB:
index = GET_XX();
JIT_print(" {\n");
JIT_print(" static void *jump[] = { ");
for (i = 0; i < index; i++)
{
if (i) JIT_print(", ");
JIT_print("&&__L%d", PC[i] + p + i);
}
JIT_print(" };\n");
pop(T_INTEGER, " int n");
p += index + 2;
JIT_print(" if (n >= 0 && n < %d)\n", index);
if ((PC[index + 1] & 0xFF00) == C_GOSUB)
{
JIT_print(" {\n");
JIT_print(" PUSH_GOSUB(__L%d);\n", p);
}
JIT_print(" goto *jump[n];\n");
if ((PC[index + 1] & 0xFF00) == C_GOSUB)
JIT_print(" }\n");
JIT_print(" }\n");
JIT_print("__L%d:;\n", p);
goto _MAIN;
_PUSH_EVENT:
index = GET_UX();
if (index == 0xFF)
{
push_event(TRUE, PC[0]);
p++;
}
else
push_event(FALSE, index);
goto _MAIN;
_PUSH_EXTERN:
index = GET_UX();
push_function(CALL_EXTERN, index);
goto _MAIN;
_BYREF:
_CALL_QUICK:
_CALL_SLOW:
_ILLEGAL:
JIT_panic("unsupported opcode %04X", code);
}