#include "jit-internal.h"
#include "jit-rules.h"
#include "jit-reg-alloc.h"
#include "jit-setjmp.h"
#ifdef _JIT_COMPILE_DEBUG
# include <jit/jit-dump.h>
# include <stdio.h>
#endif
typedef struct
{
jit_function_t func;
int memory_locked;
int memory_started;
int restart;
int page_factor;
struct jit_gencode gen;
} _jit_compile_t;
#define _JIT_RESULT_TO_OBJECT(x) ((void *) ((jit_nint) (x) - JIT_RESULT_OK))
#define _JIT_RESULT_FROM_OBJECT(x) ((jit_nint) ((void *) (x)) + JIT_RESULT_OK)
static void *
internal_exception_handler(int exception_type)
{
return _JIT_RESULT_TO_OBJECT(exception_type);
}
static void
optimize(jit_function_t func)
{
if(func->is_optimized || func->optimization_level == JIT_OPTLEVEL_NONE)
{
return;
}
_jit_block_build_cfg(func);
_jit_block_clean_cfg(func);
func->is_optimized = 1;
}
int
jit_optimize(jit_function_t func)
{
jit_jmp_buf jbuf;
jit_exception_func handler;
if(!func)
{
return JIT_RESULT_NULL_FUNCTION;
}
if(!func->builder)
{
if(func->is_compiled)
{
return JIT_RESULT_OK;
}
else
{
return JIT_RESULT_NULL_FUNCTION;
}
}
handler = jit_exception_set_handler(internal_exception_handler);
_jit_unwind_push_setjmp(&jbuf);
if(setjmp(jbuf.buf))
{
_jit_unwind_pop_setjmp();
jit_exception_set_handler(handler);
return _JIT_RESULT_FROM_OBJECT(jit_exception_get_last_and_clear());
}
optimize(func);
_jit_unwind_pop_setjmp();
jit_exception_set_handler(handler);
return JIT_RESULT_OK;
}
void
mark_offset(jit_gencode_t gen, jit_function_t func, unsigned long offset)
{
unsigned long native_offset = gen->ptr - gen->mem_start;
if(!_jit_varint_encode_uint(&gen->offset_encoder, (jit_uint) offset))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
if(!_jit_varint_encode_uint(&gen->offset_encoder, (jit_uint) native_offset))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
}
static void
compile_block(jit_gencode_t gen, jit_function_t func, jit_block_t block)
{
jit_insn_iter_t iter;
jit_insn_t insn;
#ifdef _JIT_COMPILE_DEBUG
printf("Block #%d: %d\n\n", func->builder->block_count++, block->label);
#endif
jit_insn_iter_init(&iter, block);
while((insn = jit_insn_iter_next(&iter)) != 0)
{
#ifdef _JIT_COMPILE_DEBUG
unsigned char *p1, *p2;
p1 = gen->ptr;
printf("Insn #%d: ", func->builder->insn_count++);
jit_dump_insn(stdout, func, insn);
printf("\nStart of binary code: 0x%08x\n", p1);
#endif
switch(insn->opcode)
{
case JIT_OP_NOP:
break;
case JIT_OP_CHECK_NULL:
if(!_jit_insn_check_is_redundant(&iter))
{
_jit_gen_insn(gen, func, block, insn);
}
break;
#ifndef JIT_BACKEND_INTERP
case JIT_OP_CALL:
case JIT_OP_CALL_TAIL:
case JIT_OP_CALL_INDIRECT:
case JIT_OP_CALL_INDIRECT_TAIL:
case JIT_OP_CALL_VTABLE_PTR:
case JIT_OP_CALL_VTABLE_PTR_TAIL:
case JIT_OP_CALL_EXTERNAL:
case JIT_OP_CALL_EXTERNAL_TAIL:
_jit_regs_spill_all(gen);
_jit_gen_insn(gen, func, block, insn);
_jit_regs_clear_all_outgoing(gen);
break;
#endif
#ifndef JIT_BACKEND_INTERP
case JIT_OP_IMPORT:
_jit_gen_fix_value(insn->value2);
insn->opcode = JIT_OP_ADD_RELATIVE;
insn->value2 = jit_value_create_nint_constant(func, jit_type_nint,
insn->value2->frame_offset);
_jit_gen_insn(gen, func, block, insn);
break;
#endif
#ifndef JIT_BACKEND_INTERP
case JIT_OP_INCOMING_REG:
_jit_regs_set_incoming(gen,
(int)jit_value_get_nint_constant(insn->value2),
insn->value1);
_jit_gen_insn(gen, func, block, insn);
break;
#endif
case JIT_OP_INCOMING_FRAME_POSN:
insn->value1->frame_offset = jit_value_get_nint_constant(insn->value2);
insn->value1->in_register = 0;
insn->value1->has_frame_offset = 1;
if(insn->value1->has_global_register)
{
insn->value1->in_global_register = 1;
_jit_gen_load_global(gen, insn->value1->global_reg, insn->value1);
}
else
{
insn->value1->in_frame = 1;
}
break;
#ifndef JIT_BACKEND_INTERP
case JIT_OP_OUTGOING_REG:
_jit_regs_set_outgoing(gen,
(int)jit_value_get_nint_constant(insn->value2),
insn->value1);
break;
#endif
#ifndef JIT_BACKEND_INTERP
case JIT_OP_RETURN_REG:
_jit_regs_set_incoming(gen,
(int)jit_value_get_nint_constant(insn->value2),
insn->value1);
_jit_gen_insn(gen, func, block, insn);
break;
#endif
case JIT_OP_MARK_OFFSET:
mark_offset(gen, func, (unsigned long)(long)jit_value_get_nint_constant(insn->value1));
break;
default:
_jit_gen_insn(gen, func, block, insn);
break;
}
#ifdef _JIT_COMPILE_DEBUG
p2 = gen->ptr;
printf("Length of binary code: %d\n\n", p2 - p1);
fflush(stdout);
#endif
}
}
static void
reset_value(jit_value_t value)
{
value->reg = -1;
value->in_register = 0;
value->in_global_register = value->has_global_register;
value->in_frame = 0;
}
static void
cleanup_on_restart(jit_gencode_t gen, jit_function_t func)
{
jit_block_t block;
jit_insn_iter_t iter;
jit_insn_t insn;
block = 0;
while((block = jit_block_next(func, block)) != 0)
{
block->address = 0;
block->fixup_list = 0;
block->fixup_absolute_list = 0;
jit_insn_iter_init(&iter, block);
while((insn = jit_insn_iter_next(&iter)) != 0)
{
if(insn->dest && (insn->flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
{
reset_value(insn->dest);
}
if(insn->value1 && (insn->flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
{
reset_value(insn->value1);
}
if(insn->value2 && (insn->flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
{
reset_value(insn->value2);
}
}
}
if(func->builder->setjmp_value)
{
reset_value(func->builder->setjmp_value);
}
if(func->builder->parent_frame)
{
reset_value(func->builder->parent_frame);
}
if(func->builder->has_tail_call)
{
gen->touched = jit_regused_init;
}
else
{
gen->touched = gen->permanent;
}
gen->epilog_fixup = 0;
}
static void
memory_acquire(_jit_compile_t *state)
{
state->gen.context = state->func->context;
_jit_memory_lock(state->gen.context);
state->memory_locked = 1;
if(!_jit_memory_ensure(state->gen.context))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
}
static void
memory_release(_jit_compile_t *state)
{
if(state->memory_locked)
{
_jit_memory_unlock(state->gen.context);
state->memory_locked = 0;
}
}
static void
memory_align(_jit_compile_t *state, int align, int diff, int nop)
{
jit_nuint p, n;
if(align < 1)
{
align = 1;
}
p = (jit_nuint) state->gen.ptr;
n = (p + (jit_nuint) align - 1) & ~((jit_nuint) align - 1);
if(p == n || (p - n) >= (jit_nuint) diff)
{
return;
}
align = (int) (n - p);
_jit_gen_check_space(&state->gen, align);
#ifdef jit_should_pad
_jit_pad_buffer(state->gen.ptr, align);
#else
jit_memset(state->gen.ptr, nop, align);
state->gen.ptr += align;
#endif
}
static void
memory_start(_jit_compile_t *state)
{
state->memory_started = 1;
state->gen.mem_start = _jit_memory_get_break(state->gen.context);
state->gen.mem_limit = _jit_memory_get_limit(state->gen.context);
state->gen.ptr = state->gen.mem_start;
memory_align(state, JIT_FUNCTION_ALIGNMENT, JIT_FUNCTION_ALIGNMENT, 0);
_jit_varint_init_encoder(&state->gen.offset_encoder);
}
static void
memory_alloc(_jit_compile_t *state)
{
int result;
result = _jit_memory_start_function(state->gen.context, state->func);
if(result == JIT_MEMORY_RESTART)
{
_jit_memory_extend_limit(state->gen.context, state->page_factor++);
result = _jit_memory_start_function(state->gen.context, state->func);
}
if(result != JIT_MEMORY_OK)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
memory_start(state);
}
static void
memory_flush(_jit_compile_t *state)
{
int result;
if(state->memory_started)
{
state->memory_started = 0;
_jit_memory_set_break(state->gen.context, state->gen.code_end);
result = _jit_memory_end_function(state->gen.context, JIT_MEMORY_OK);
if(result != JIT_MEMORY_OK)
{
if(result == JIT_MEMORY_RESTART)
{
jit_exception_builtin(JIT_RESULT_MEMORY_FULL);
}
else
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
}
#ifndef JIT_BACKEND_INTERP
_jit_flush_exec(state->gen.code_start,
state->gen.code_end - state->gen.code_start);
#endif
if(!_jit_varint_encode_end(&state->gen.offset_encoder))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
state->func->bytecode_offset = _jit_varint_get_data(&state->gen.offset_encoder);
}
}
static void
memory_abort(_jit_compile_t *state)
{
if(state->memory_started)
{
state->memory_started = 0;
_jit_memory_end_function(state->gen.context, JIT_MEMORY_RESTART);
_jit_varint_free_data(_jit_varint_get_data(&state->gen.offset_encoder));
}
}
static void
memory_realloc(_jit_compile_t *state)
{
int result;
memory_abort(state);
_jit_memory_extend_limit(state->gen.context, state->page_factor++);
result = _jit_memory_start_function(state->gen.context, state->func);
if(result != JIT_MEMORY_OK)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
memory_start(state);
}
static void
codegen_prepare(_jit_compile_t *state)
{
if(!state->func->builder->may_throw)
{
state->func->no_throw = 1;
}
if(!state->func->builder->ordinary_return)
{
state->func->no_return = 1;
}
_jit_function_compute_liveness(state->func);
#ifndef JIT_BACKEND_INTERP
_jit_regs_alloc_global(&state->gen, state->func);
#endif
}
static void
codegen(_jit_compile_t *state)
{
jit_function_t func = state->func;
struct jit_gencode *gen = &state->gen;
jit_block_t block;
gen->code_start = gen->ptr;
#ifdef JIT_PROLOG_SIZE
_jit_gen_check_space(gen, JIT_PROLOG_SIZE);
gen->ptr += JIT_PROLOG_SIZE;
#endif
block = 0;
while((block = jit_block_next(func, block)) != 0)
{
_jit_gen_start_block(gen, block);
#ifndef JIT_BACKEND_INTERP
_jit_regs_init_for_block(gen);
#endif
compile_block(gen, func, block);
#ifndef JIT_BACKEND_INTERP
_jit_regs_spill_all(gen);
#endif
_jit_gen_end_block(gen, block);
}
_jit_gen_epilog(gen, func);
gen->code_end = gen->ptr;
#ifdef JIT_PROLOG_SIZE
gen->code_start = _jit_gen_prolog(gen, func, gen->code_start);
#endif
#if !defined(JIT_BACKEND_INTERP) && (!defined(jit_redirector_size) || !defined(jit_indirector_size))
if(func->is_recompilable && !func->indirector)
{
func->indirector = _jit_gen_redirector(&gen, func);
}
#endif
}
static int
compile(_jit_compile_t *state, jit_function_t func)
{
jit_exception_func handler;
jit_jmp_buf jbuf;
int result;
jit_memzero(state, sizeof(_jit_compile_t));
state->func = func;
handler = jit_exception_set_handler(internal_exception_handler);
_jit_unwind_push_setjmp(&jbuf);
restart:
if(setjmp(jbuf.buf))
{
result = _JIT_RESULT_FROM_OBJECT(jit_exception_get_last_and_clear());
if(result == JIT_RESULT_MEMORY_FULL)
{
state->restart = 1;
goto restart;
}
memory_abort(state);
goto exit;
}
if(state->restart == 0)
{
optimize(state->func);
codegen_prepare(state);
memory_acquire(state);
memory_alloc(state);
}
else
{
cleanup_on_restart(&state->gen, state->func);
memory_realloc(state);
}
#ifdef _JIT_COMPILE_DEBUG
if(state->restart == 0)
{
printf("\n*** Start code generation ***\n\n");
}
else
{
printf("\n*** Restart code generation ***\n\n");
}
state->func->builder->block_count = 0;
state->func->builder->insn_count = 0;
#endif
#ifdef jit_extra_gen_init
jit_extra_gen_init(&state->gen);
#endif
codegen(state);
#ifdef jit_extra_gen_cleanup
jit_extra_gen_cleanup(&state->gen);
#endif
memory_flush(state);
result = JIT_RESULT_OK;
exit:
memory_release(state);
_jit_unwind_pop_setjmp();
jit_exception_set_handler(handler);
return result;
}
int
jit_compile(jit_function_t func)
{
_jit_compile_t state;
int result;
if(!func)
{
return JIT_RESULT_NULL_FUNCTION;
}
if(!func->builder)
{
if(func->is_compiled)
{
return JIT_RESULT_OK;
}
else
{
return JIT_RESULT_NULL_FUNCTION;
}
}
result = compile(&state, func);
if(result == JIT_RESULT_OK)
{
func->entry_point = state.gen.code_start;
func->is_compiled = 1;
_jit_function_free_builder(func);
}
return result;
}
int
jit_compile_entry(jit_function_t func, void **entry_point)
{
_jit_compile_t state;
int result;
if(entry_point)
{
*entry_point = 0;
}
else
{
return JIT_RESULT_NULL_REFERENCE;
}
if(!func)
{
return JIT_RESULT_NULL_FUNCTION;
}
if(!func->builder)
{
if(func->is_compiled)
{
*entry_point = func->entry_point;
return JIT_RESULT_OK;
}
else
{
return JIT_RESULT_NULL_FUNCTION;
}
}
result = compile(&state, func);
if(result == JIT_RESULT_OK)
{
*entry_point = state.gen.code_start;
}
return result;
}
void
jit_function_setup_entry(jit_function_t func, void *entry_point)
{
if(!func)
{
return;
}
if(entry_point)
{
func->entry_point = entry_point;
func->is_compiled = 1;
}
_jit_function_free_builder(func);
}
int
jit_function_compile(jit_function_t func)
{
return (JIT_RESULT_OK == jit_compile(func));
}
int
jit_function_compile_entry(jit_function_t func, void **entry_point)
{
return (JIT_RESULT_OK == jit_compile_entry(func, entry_point));
}
void *
_jit_function_compile_on_demand(jit_function_t func)
{
_jit_compile_t state;
int result;
jit_context_build_start(func->context);
if(func->is_compiled)
{
jit_context_build_end(func->context);
return func->entry_point;
}
if(!func->on_demand)
{
result = JIT_RESULT_COMPILE_ERROR;
}
else
{
result = (func->on_demand)(func);
if(result == JIT_RESULT_OK && !func->is_compiled)
{
result = compile(&state, func);
if(result == JIT_RESULT_OK)
{
func->entry_point = state.gen.code_start;
func->is_compiled = 1;
}
}
_jit_function_free_builder(func);
}
jit_context_build_end(func->context);
if(result != JIT_RESULT_OK)
{
jit_exception_builtin(result);
return 0;
}
return func->entry_point;
}
#define JIT_CACHE_NO_OFFSET (~((unsigned long)0))
unsigned long
_jit_function_get_bytecode(jit_function_t func,
void *func_info, void *pc, int exact)
{
unsigned long offset = JIT_CACHE_NO_OFFSET;
void *start;
unsigned long native_offset;
jit_varint_decoder_t decoder;
jit_uint off, noff;
start = _jit_memory_get_function_start(func->context, func_info);
native_offset = pc - start;
_jit_varint_init_decoder(&decoder, func->bytecode_offset);
for(;;)
{
off = _jit_varint_decode_uint(&decoder);
noff = _jit_varint_decode_uint(&decoder);
if(_jit_varint_decode_end(&decoder))
{
if(exact)
{
offset = JIT_CACHE_NO_OFFSET;
}
break;
}
if(noff >= native_offset)
{
if(noff == native_offset)
{
offset = off;
}
else if (exact)
{
offset = JIT_CACHE_NO_OFFSET;
}
break;
}
offset = off;
}
return offset;
}