From: Aleksey Demakov Date: Fri, 5 Jun 2009 23:31:50 +0000 (+0000) Subject: move compile functions from jit-function.c to a new file jit-compile.c; X-Git-Tag: before.move.to.git X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=refs%2Fremotes%2Fupstream%2Forigin;p=francis%2Flibjit.git move compile functions from jit-function.c to a new file jit-compile.c; handle internal exceptions during compilation; optimize CFG by default but add possibility to turn optimization off; add jit_optimize function. --- diff --git a/ChangeLog b/ChangeLog index 9a9f294..55b765d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,28 @@ 2009-06-05 Aleksey Demakov + * jit/jit-compile.c (jit_function_compile) + (jit_function_compile_entry, jit_function_setup_entry) + (_jit_function_compile_on_demand): new file, move all compile + functions here from jit-function.c. + + * jit/jit-compile.c (jit_compile, jit_compile_entry): add new + functions that do exactly the same as jit_function_compile and + jit_function_compile_entry but return JIT_RESULT_ error code. + + * jit/jit-compile.c (jit_optimize): add function to optimize IR. + * include/jit/jit-function.h: add optimization level constants - JIT_OPTLEVEL_NONE and JIT_OPTLEVEL_NORMAL, + JIT_OPTLEVEL_NONE and JIT_OPTLEVEL_NORMAL. + * jit/jit-function.c (jit_function_create) set JIT_OPTLEVEL_NORMAL + optimization level by default. + * jit/jit-function.c (jit_function_get_max_optimization_level): + return JIT_OPTLEVEL_NORMAL. + * jit/jit-internal.h (struct _jit_function): add is_optimized field + to _jit_function struct. + + * jit/jit-compile.c (compile): catch internal exceptions. + * jit/jit-block.c: use internal exceptions instead of return codes + for CFG error handling. 2009-05-10 Aleksey Demakov diff --git a/jit/Makefile.am b/jit/Makefile.am index 681310f..4dd0274 100644 --- a/jit/Makefile.am +++ b/jit/Makefile.am @@ -18,6 +18,7 @@ libjit_la_SOURCES = \ jit-block.c \ jit-cache.h \ jit-cache.c \ + jit-compile.c \ jit-context.c \ jit-cpuid-x86.h \ jit-cpuid-x86.c \ diff --git a/jit/jit-block.c b/jit/jit-block.c index 2bfe7b6..43e9a67 100644 --- a/jit/jit-block.c +++ b/jit/jit-block.c @@ -37,7 +37,7 @@ typedef struct _jit_block_stack_entry } _jit_block_stack_entry_t; -static int +static void create_edge(jit_function_t func, jit_block_t src, jit_block_t dst, int flags, int create) { _jit_edge_t edge; @@ -49,7 +49,7 @@ create_edge(jit_function_t func, jit_block_t src, jit_block_t dst, int flags, in edge = jit_memory_pool_alloc(&func->builder->edge_pool, struct _jit_edge); if(!edge) { - return 0; + jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY); } /* Initialize edge fields */ @@ -65,11 +65,9 @@ create_edge(jit_function_t func, jit_block_t src, jit_block_t dst, int flags, in /* Count it */ ++(src->num_succs); ++(dst->num_preds); - - return 1; } -static int +static void build_edges(jit_function_t func, int create) { jit_block_t src, dst; @@ -97,7 +95,7 @@ build_edges(jit_function_t func, int create) if(!dst) { /* Bail out on undefined label */ - return 0; + jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL); } } else if(opcode > JIT_OP_BR && opcode <= JIT_OP_BR_NFGE_INV) @@ -107,7 +105,7 @@ build_edges(jit_function_t func, int create) if(!dst) { /* Bail out on undefined label */ - return 0; + jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL); } } else if(opcode == JIT_OP_THROW || opcode == JIT_OP_RETHROW) @@ -126,7 +124,7 @@ build_edges(jit_function_t func, int create) if(!dst) { /* Bail out on undefined label */ - return 0; + jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL); } } else if(opcode >= JIT_OP_CALL && opcode <= JIT_OP_CALL_EXTERNAL_TAIL) @@ -148,12 +146,9 @@ build_edges(jit_function_t func, int create) if(!dst) { /* Bail out on undefined label */ - return 0; - } - if(!create_edge(func, src, dst, _JIT_EDGE_BRANCH, create)) - { - return 0; + jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL); } + create_edge(func, src, dst, _JIT_EDGE_BRANCH, create); } dst = 0; } @@ -165,25 +160,17 @@ build_edges(jit_function_t func, int create) /* create a branch or exception edge if appropriate */ if(dst) { - if(!create_edge(func, src, dst, flags, create)) - { - return 0; - } + create_edge(func, src, dst, flags, create); } /* create a fall-through edge if appropriate */ if(!src->ends_in_dead) { - if(!create_edge(func, src, src->next, _JIT_EDGE_FALLTHRU, create)) - { - return 0; - } + create_edge(func, src, src->next, _JIT_EDGE_FALLTHRU, create); } } - - return 1; } -static int +static void alloc_edges(jit_function_t func) { jit_block_t block; @@ -200,7 +187,7 @@ alloc_edges(jit_function_t func) block->succs = jit_calloc(block->num_succs, sizeof(_jit_edge_t)); if(!block->succs) { - return 0; + jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY); } /* Reset edge count for the next build pass */ block->num_succs = 0; @@ -216,14 +203,12 @@ alloc_edges(jit_function_t func) block->preds = jit_calloc(block->num_preds, sizeof(_jit_edge_t)); if(!block->preds) { - return 0; + jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY); } /* Reset edge count for the next build pass */ block->num_preds = 0; } } - - return 1; } static void @@ -351,7 +336,7 @@ merge_labels(jit_function_t func, jit_block_t block, jit_label_t label) } /* Merge empty block with its successor */ -static int +static void merge_empty(jit_function_t func, jit_block_t block, int *changed) { _jit_edge_t succ_edge, pred_edge, fallthru_edge; @@ -379,7 +364,7 @@ merge_empty(jit_function_t func, jit_block_t block, int *changed) *changed = 1; if(!attach_edge_dst(pred_edge, succ_block)) { - return 0; + jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY); } } } @@ -394,7 +379,7 @@ merge_empty(jit_function_t func, jit_block_t block, int *changed) *changed = 1; if(!attach_edge_dst(pred_edge, succ_block)) { - return 0; + jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY); } fallthru_edge = 0; } @@ -414,8 +399,6 @@ merge_empty(jit_function_t func, jit_block_t block, int *changed) _jit_block_detach(block, block); delete_block(block); } - - return 1; } /* Delete block along with references to it */ @@ -571,28 +554,20 @@ _jit_block_free(jit_function_t func) func->builder->exit_block = 0; } -int +void _jit_block_build_cfg(jit_function_t func) { /* Count the edges */ - if(!build_edges(func, 0)) - { - return 0; - } + build_edges(func, 0); + /* Allocate memory for edges */ - if(!alloc_edges(func)) - { - return 0; - } + alloc_edges(func); + /* Actually build the edges */ - if(!build_edges(func, 1)) - { - return 0; - } - return 1; + build_edges(func, 1); } -int +void _jit_block_clean_cfg(jit_function_t func) { int index, changed; @@ -612,7 +587,7 @@ _jit_block_clean_cfg(jit_function_t func) if(!_jit_block_compute_postorder(func)) { - return 0; + jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY); } eliminate_unreachable(func); @@ -671,10 +646,7 @@ _jit_block_clean_cfg(jit_function_t func) if(is_empty_block(block)) { /* Remove empty block */ - if(!merge_empty(func, block, &changed)) - { - return 0; - } + merge_empty(func, block, &changed); } } @@ -685,13 +657,11 @@ _jit_block_clean_cfg(jit_function_t func) { if(!_jit_block_compute_postorder(func)) { - return 0; + jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY); } clear_visited(func); goto loop; } - - return 1; } int diff --git a/jit/jit-compile.c b/jit/jit-compile.c new file mode 100644 index 0000000..eea5698 --- /dev/null +++ b/jit/jit-compile.c @@ -0,0 +1,809 @@ +/* + * jit-compile.c - Function compilation. + * + * Copyright (C) 2004, 2006-2008 Southern Storm Software, Pty Ltd. + * + * This file is part of the libjit library. + * + * The libjit library is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 2.1 of + * the License, or (at your option) any later version. + * + * The libjit library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the libjit library. If not, see + * . + */ + +#include "jit-internal.h" +#include "jit-memory.h" +#include "jit-rules.h" +#include "jit-reg-alloc.h" +#include "jit-setjmp.h" +#ifdef _JIT_COMPILE_DEBUG +# include +#endif + +#define _JIT_RESULT_TO_OBJECT(x) ((void *) ((int) (x) - JIT_RESULT_OK)) +#define _JIT_RESULT_FROM_OBJECT(x) ((int) ((void *) (x)) + JIT_RESULT_OK) + +/* + * This exception handler overrides a user-defined handler during compilation. + */ +static void * +internal_exception_handler(int exception_type) +{ + return _JIT_RESULT_TO_OBJECT(exception_type); +} + +/* + * Optimize a function. + */ +static void +optimize(jit_function_t func) +{ + if(func->is_optimized || func->optimization_level == JIT_OPTLEVEL_NONE) + { + /* The function is already optimized or does not need optimization */ + return; + } + + /* Build control flow graph */ + _jit_block_build_cfg(func); + + /* Eliminate useless control flow */ + _jit_block_clean_cfg(func); + + /* Optimization is */ + func->is_optimized = 1; +} + +/*@ + * @deftypefun int jit_optimize (jit_function_t @var{func}) + * Optimize a function by analyzing and transforming its intermediate + * representation. If the function was already compiled or optimized, + * then do nothing. + * + * Returns @code{JIT_RESUlT_OK} on success, otherwise it might return + * @code{JIT_RESULT_OUT_OF_MEMORY}, @code{JIT_RESULT_COMPILE_ERROR} or + * possibly some other more specific @code{JIT_RESULT_} code. + * + * Normally this function should not be used because @code{jit_compile} + * performs all the optimization anyway. However it might be useful for + * debugging to verify the effect of the @code{libjit} code optimization. + * This might be done, for instance, by calling @code{jit_dump_function} + * before and after @code{jit_optimize}. + * @end deftypefun +@*/ +int +jit_optimize(jit_function_t func) +{ + jit_jmp_buf jbuf; + jit_exception_func handler; + + /* Bail out on invalid parameter */ + if(!func) + { + return JIT_RESULT_NULL_FUNCTION; + } + + /* Bail out if there is nothing to do here */ + if(!func->builder) + { + if(func->is_compiled) + { + /* The function is already compiled and we can't optimize it */ + return JIT_RESULT_OK; + } + else + { + /* We don't have anything to optimize at all */ + return JIT_RESULT_NULL_FUNCTION; + } + } + + /* Override user's exception handler */ + handler = jit_exception_set_handler(internal_exception_handler); + + /* Establish a "setjmp" point here so that we can unwind the + stack to this point when an exception occurs and then prevent + the exception from propagating further up the stack */ + _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()); + } + + /* Perform the optimizations */ + optimize(func); + + /* Restore the "setjmp" contexts and exit */ + _jit_unwind_pop_setjmp(); + jit_exception_set_handler(handler); + return JIT_RESULT_OK; +} + +/* + * Compile a single basic block within a function. + */ +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", func->builder->block_count++, block->label); +#endif + + /* Iterate over all blocks in the function */ + jit_insn_iter_init(&iter, block); + while((insn = jit_insn_iter_next(&iter)) != 0) + { +#ifdef _JIT_COMPILE_DEBUG + unsigned char *p1, *p2; + p1 = gen->posn.ptr; + printf("Insn: %5d, Opcode: 0x%04x\n", func->builder->insn_count++, insn->opcode); + printf("Start of binary code: 0x%08x\n", p1); +#endif + + switch(insn->opcode) + { + case JIT_OP_NOP: + /* Ignore NOP's */ + break; + + case JIT_OP_CHECK_NULL: + /* Determine if we can optimize the null check away */ + if(!_jit_insn_check_is_redundant(&iter)) + { + _jit_gen_insn(gen, func, block, insn); + } + break; + + 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: + /* Spill all caller-saved registers before a call */ + _jit_regs_spill_all(gen); + _jit_gen_insn(gen, func, block, insn); + break; + +#ifndef JIT_BACKEND_INTERP + case JIT_OP_INCOMING_REG: + /* Assign a register to an incoming value */ + _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: + /* Set the frame position for an incoming value */ + 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: + /* Copy a value into an outgoing register */ + _jit_regs_set_outgoing(gen, + (int)jit_value_get_nint_constant(insn->value2), + insn->value1); + break; +#endif + + case JIT_OP_OUTGOING_FRAME_POSN: + /* Set the frame position for an outgoing value */ + insn->value1->frame_offset = jit_value_get_nint_constant(insn->value2); + insn->value1->in_register = 0; + insn->value1->in_global_register = 0; + insn->value1->in_frame = 0; + insn->value1->has_frame_offset = 1; + insn->value1->has_global_register = 0; + break; + +#ifndef JIT_BACKEND_INTERP + case JIT_OP_RETURN_REG: + /* Assign a register to a return value */ + _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 the current code position as corresponding + to a particular bytecode offset */ + _jit_cache_mark_bytecode(&gen->posn, + (unsigned long)(long) + jit_value_get_nint_constant(insn->value1)); + break; + + default: + /* Generate code for the instruction with the back end */ + _jit_gen_insn(gen, func, block, insn); + break; + } + +#ifdef _JIT_COMPILE_DEBUG + p2 = gen->posn.ptr; + printf("Length of binary code: %d\n\n", p2 - p1); + fflush(stdout); +#endif + } +} + +/* + * Reset value on restart. + */ +static void +reset_value(jit_value_t value) +{ + value->reg = -1; + value->in_register = 0; + value->in_global_register = 0; + value->in_frame = 0; +} + +/* + * Clean up the compilation state on restart. + */ +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) + { + /* Clear the block addresses and fixup lists */ + block->address = 0; + block->fixup_list = 0; + block->fixup_absolute_list = 0; + + /* Reset values referred to by block instructions */ + 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); + } + } + } + + /* Reset values referred to by builder */ + if(func->builder->setjmp_value) + { + reset_value(func->builder->setjmp_value); + } + if(func->builder->parent_frame) + { + reset_value(func->builder->parent_frame); + } + + /* Reset the "touched" registers mask. The first time compilation + might have followed wrong code paths and thus allocated wrong + registers. */ + if(func->builder->has_tail_call) + { + /* For functions with tail calls _jit_regs_alloc_global() + does not allocate any global registers. The "permanent" + mask has all global registers set to prevent their use. */ + gen->touched = jit_regused_init; + } + else + { + gen->touched = gen->permanent; + } + + /* Reset the epilog fixup list */ + gen->epilog_fixup = 0; +} + +static void +prepare(jit_function_t func) +{ + /* Intuit "nothrow" and "noreturn" flags for this function */ + if(!func->builder->may_throw) + { + func->no_throw = 1; + } + if(!func->builder->ordinary_return) + { + func->no_return = 1; + } + + /* Compute liveness and "next use" information for this function */ + _jit_function_compute_liveness(func); +} + +static int +codegen(jit_function_t func, void **entry_point) +{ + struct jit_gencode gen; + jit_cache_t cache; + unsigned char *start; + unsigned char *end; + jit_block_t block; + int page_factor; + int result; + + /* Initialize the code generation state */ + jit_memzero(&gen, sizeof(gen)); + page_factor = 0; + start = 0; + end = 0; + + /* Allocate global registers to variables within the function */ +#ifndef JIT_BACKEND_INTERP + _jit_regs_alloc_global(&gen, func); +#endif + +#ifdef _JIT_COMPILE_DEBUG + printf("\n*** Start compilation ***\n\n"); + func->builder->block_count = 0; + func->builder->insn_count = 0; +#endif + + /* Get the method cache */ + cache = _jit_context_get_cache(func->context); + if(!cache) + { + return JIT_RESULT_OUT_OF_MEMORY; + } + + /* Start function output to the cache */ + result = _jit_cache_start_method(cache, &(gen.posn), + page_factor++, + JIT_FUNCTION_ALIGNMENT, func); + if (result == JIT_CACHE_RESTART) + { + /* No space left on the current cache page. Allocate a new one. */ + result = _jit_cache_start_method(cache, &(gen.posn), + page_factor++, + JIT_FUNCTION_ALIGNMENT, func); + } + if (result != JIT_CACHE_OK) + { + /* Failed to allocate any cache space */ + return JIT_RESULT_OUT_OF_MEMORY; + } + + for(;;) + { + start = gen.posn.ptr; + +#ifdef jit_extra_gen_init + /* Initialize information that may need to be reset each loop */ + jit_extra_gen_init(&gen); +#endif + +#ifdef JIT_PROLOG_SIZE + /* Output space for the function prolog */ + if(!jit_cache_check_for_n(&(gen.posn), JIT_PROLOG_SIZE)) + { + /* No space left on the current cache page. Restart. */ + jit_cache_mark_full(&(gen.posn)); + goto restart; + } + gen.posn.ptr += JIT_PROLOG_SIZE; +#endif + + /* Generate code for the blocks in the function */ + block = 0; + while((block = jit_block_next(func, block)) != 0) + { + /* Notify the back end that the block is starting */ + _jit_gen_start_block(&gen, block); + +#ifndef JIT_BACKEND_INTERP + /* Clear the local register assignments */ + _jit_regs_init_for_block(&gen); +#endif + + /* Generate the block's code */ + compile_block(&gen, func, block); + +#ifndef JIT_BACKEND_INTERP + /* Spill all live register values back to their frame positions */ + _jit_regs_spill_all(&gen); +#endif + + /* Notify the back end that the block is finished */ + _jit_gen_end_block(&gen, block); + + /* Stop code generation if the cache page is full */ + if(_jit_cache_is_full(cache, &(gen.posn))) + { + /* No space left on the current cache page. Restart. */ + goto restart; + } + } + + /* Output the function epilog. All return paths will jump to here */ + _jit_gen_epilog(&gen, func); + end = gen.posn.ptr; + +#ifdef JIT_PROLOG_SIZE + /* Back-patch the function prolog and get the real entry point */ + start = _jit_gen_prolog(&gen, func, start); +#endif + +#if !defined(JIT_BACKEND_INTERP) && (!defined(jit_redirector_size) || !defined(jit_indirector_size)) + /* If the function is recompilable, then we need an extra entry + point to properly redirect previous references to the function */ + if(func->is_recompilable && !func->indirector) + { + /* TODO: use _jit_create_indirector() instead of + _jit_gen_redirector() as both do the same. */ + func->indirector = _jit_gen_redirector(&gen, func); + } +#endif + + restart: + /* End the function's output process */ + result = _jit_cache_end_method(&(gen.posn)); + if(result != JIT_CACHE_RESTART) + { + break; + } + + /* Clean up the compilation state before restart */ + cleanup_on_restart(&gen, func); + +#ifdef _JIT_COMPILE_DEBUG + printf("\n*** Restart compilation ***\n\n"); + func->builder->block_count = 0; + func->builder->insn_count = 0; +#endif + + /* Restart function output to the cache */ + result = _jit_cache_start_method(cache, &(gen.posn), + page_factor, + JIT_FUNCTION_ALIGNMENT, func); + if(result != JIT_CACHE_OK) + { +#ifdef jit_extra_gen_cleanup + /* Clean up the extra code generation state */ + jit_extra_gen_cleanup(gen); +#endif + return JIT_RESULT_OUT_OF_MEMORY; + } + page_factor *= 2; + } + +#ifdef jit_extra_gen_cleanup + /* Clean up the extra code generation state */ + jit_extra_gen_cleanup(gen); +#endif + + /* Bail out if we ran out of memory while translating the function */ + if(result != JIT_CACHE_OK) + { + return JIT_RESULT_OUT_OF_MEMORY; + } + +#ifndef JIT_BACKEND_INTERP + /* Perform a CPU cache flush, to make the code executable */ + jit_flush_exec(start, (unsigned int)(end - start)); +#endif + + /* Record the entry point */ + *entry_point = start; + + return JIT_RESULT_OK; +} + +/* + * Compile a function and return its entry point. + */ +static int +compile(jit_function_t func, void **entry_point) +{ + jit_jmp_buf jbuf; + jit_exception_func handler; + volatile int unlock = 0; + + /* Override user's exception handler */ + handler = jit_exception_set_handler(internal_exception_handler); + + /* Establish a "setjmp" point here so that we can unwind the + stack to this point when an exception occurs and then prevent + the exception from propagating further up the stack */ + _jit_unwind_push_setjmp(&jbuf); + if(setjmp(jbuf.buf)) + { + if(unlock) + { + jit_mutex_unlock(&func->context->cache_lock); + } + _jit_unwind_pop_setjmp(); + jit_exception_set_handler(handler); + return _JIT_RESULT_FROM_OBJECT(jit_exception_get_last_and_clear()); + } + + /* Perform machine-independent optimizations */ + optimize(func); + + /* Prepare the data needed for code generation */ + prepare(func); + + /* We need the cache lock while we are generating the code */ + jit_mutex_lock(&func->context->cache_lock); + unlock = 1; + + /* Perform code generation */ + codegen(func, entry_point); + + /* Unlock the cache */ + jit_mutex_unlock(&func->context->cache_lock); + + /* Restore the "setjmp" contexts and exit */ + _jit_unwind_pop_setjmp(); + jit_exception_set_handler(handler); + return JIT_RESULT_OK; +} + +/*@ + * @deftypefun int jit_compile (jit_function_t @var{func}) + * Compile a function to its executable form. If the function was + * already compiled, then do nothing. Returns zero on error. + * + * If an error occurs, you can use @code{jit_function_abandon} to + * completely destroy the function. Once the function has been compiled + * successfully, it can no longer be abandoned. + * + * Sometimes you may wish to recompile a function, to apply greater + * levels of optimization the second time around. You must call + * @code{jit_function_set_recompilable} before you compile the function + * the first time. On the second time around, build the function's + * instructions again, and call @code{jit_compile} a second time. + * @end deftypefun +@*/ +int +jit_compile(jit_function_t func) +{ + int result; + void *entry_point; + + /* Bail out on invalid parameter */ + if(!func) + { + return JIT_RESULT_NULL_FUNCTION; + } + + /* Bail out if there is nothing to do here */ + if(!func->builder) + { + if(func->is_compiled) + { + /* The function is already compiled, and we don't need to recompile */ + return JIT_RESULT_OK; + } + else + { + /* We don't have anything to compile at all */ + return JIT_RESULT_NULL_FUNCTION; + } + } + + /* Compile and record the entry point. */ + result = compile(func, &entry_point); + if(result == JIT_RESULT_OK) + { + func->entry_point = entry_point; + func->is_compiled = 1; + + /* Free the builder structure, which we no longer require */ + _jit_function_free_builder(func); + } + + return result; +} + +/*@ + * @deftypefun int jit_compile_entry (jit_function_t @var{func}, void **@var{entry_point}) + * Compile a function to its executable form but do not make it + * available for invocation yet. It may be made available later + * with @code{jit_function_setup_entry}. + * @end deftypefun +@*/ +int +jit_compile_entry(jit_function_t func, void **entry_point) +{ + /* Init entry_point */ + if(entry_point) + { + *entry_point = 0; + } + else + { + return JIT_RESULT_NULL_REFERENCE; + } + + /* Bail out on invalid parameter */ + if(!func) + { + return JIT_RESULT_NULL_FUNCTION; + } + + /* Bail out if there is nothing to do here */ + if(!func->builder) + { + if(func->is_compiled) + { + /* The function is already compiled, and we don't need to recompile */ + *entry_point = func->entry_point; + return JIT_RESULT_OK; + } + else + { + /* We don't have anything to compile at all */ + return JIT_RESULT_NULL_FUNCTION; + } + } + + + if(func->is_compiled && !func->builder) + { + /* The function is already compiled, and we don't need to recompile */ + *entry_point = func->entry_point; + return 1; + } + if(!func->builder) + { + /* We don't have anything to compile at all */ + return 0; + } + + /* Compile and return the entry point. */ + return compile(func, entry_point); +} + +/*@ + * @deftypefun int jit_function_setup_entry (jit_function_t @var{func}, void *@var{entry_point}) + * Make a function compiled with @code{jit_function_compile_entry} + * available for invocation and free the resources used for + * compilation. If @var{entry_point} is null then it only + * frees the resources. + * @end deftypefun +@*/ +void +jit_function_setup_entry(jit_function_t func, void *entry_point) +{ + /* Bail out if we have nothing to do */ + if(!func) + { + return; + } + /* Record the entry point */ + if(entry_point) + { + func->entry_point = entry_point; + func->is_compiled = 1; + } + _jit_function_free_builder(func); +} + +/*@ + * @deftypefun int jit_function_compile (jit_function_t @var{func}) + * Compile a function to its executable form. If the function was + * already compiled, then do nothing. Returns zero on error. + * + * If an error occurs, you can use @code{jit_function_abandon} to + * completely destroy the function. Once the function has been compiled + * successfully, it can no longer be abandoned. + * + * Sometimes you may wish to recompile a function, to apply greater + * levels of optimization the second time around. You must call + * @code{jit_function_set_recompilable} before you compile the function + * the first time. On the second time around, build the function's + * instructions again, and call @code{jit_function_compile} + * a second time. + * @end deftypefun +@*/ +int +jit_function_compile(jit_function_t func) +{ + return (JIT_RESULT_OK == jit_compile(func)); +} + +/*@ + * @deftypefun int jit_function_compile_entry (jit_function_t @var{func}, void **@var{entry_point}) + * Compile a function to its executable form but do not make it + * available for invocation yet. It may be made available later + * with @code{jit_function_setup_entry}. + * @end deftypefun +@*/ +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) +{ + int result; + void *entry; + + /* Lock down the context */ + jit_context_build_start(func->context); + + /* Fast return if we are already compiled */ + if(func->is_compiled) + { + jit_context_build_end(func->context); + return func->entry_point; + } + + if(!func->on_demand) + { + /* Bail out with an error if the user didn't supply an + on-demand compiler */ + result = JIT_RESULT_COMPILE_ERROR; + } + else + { + /* Call the user's on-demand compiler. */ + result = (func->on_demand)(func); + if(result == JIT_RESULT_OK && !func->is_compiled) + { + /* Compile the function if the user didn't do so */ + result = compile(func, &entry); + if(result == JIT_RESULT_OK) + { + func->entry_point = entry; + func->is_compiled = 1; + } + } + _jit_function_free_builder(func); + } + + /* Unlock the context and report the result */ + jit_context_build_end(func->context); + if(result != JIT_RESULT_OK) + { + jit_exception_builtin(result); + /* Normally this should be unreachable but just in case... */ + return 0; + } + + return func->entry_point; +} diff --git a/jit/jit-function.c b/jit/jit-function.c index b95195f..8c92218 100644 --- a/jit/jit-function.c +++ b/jit/jit-function.c @@ -21,14 +21,10 @@ */ #include "jit-internal.h" -#include "jit-memory.h" -#include "jit-rules.h" -#include "jit-reg-alloc.h" #include "jit-apply-func.h" +#include "jit-cache.h" +#include "jit-rules.h" #include "jit-setjmp.h" -#ifdef _JIT_COMPILE_DEBUG -#include -#endif /*@ * @deftypefun jit_function_t jit_function_create (jit_context_t @var{context}, jit_type_t @var{signature}) @@ -105,6 +101,7 @@ jit_function_t jit_function_create(jit_context_t context, jit_type_t signature) /* Initialize the function block */ func->context = context; func->signature = jit_type_copy(signature); + func->optimization_level = JIT_OPTLEVEL_NORMAL; #if !defined(JIT_BACKEND_INTERP) && defined(jit_redirector_size) /* If we aren't using interpretation, then point the function's @@ -234,6 +231,7 @@ void _jit_function_free_builder(jit_function_t func) jit_free(func->builder->label_info); jit_free(func->builder); func->builder = 0; + func->is_optimized = 0; } } @@ -494,433 +492,6 @@ jit_function_t jit_function_get_nested_parent(jit_function_t func) } } -/* - * Compile a single basic block within a function. - */ -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", func->builder->block_count++, block->label); -#endif - - /* Iterate over all blocks in the function */ - jit_insn_iter_init(&iter, block); - while((insn = jit_insn_iter_next(&iter)) != 0) - { -#ifdef _JIT_COMPILE_DEBUG - unsigned char *p1, *p2; - p1 = gen->posn.ptr; - printf("Insn: %5d, Opcode: 0x%04x\n", func->builder->insn_count++, insn->opcode); - printf("Start of binary code: 0x%08x\n", p1); -#endif - - switch(insn->opcode) - { - case JIT_OP_NOP: - /* Ignore NOP's */ - break; - - case JIT_OP_CHECK_NULL: - /* Determine if we can optimize the null check away */ - if(!_jit_insn_check_is_redundant(&iter)) - { - _jit_gen_insn(gen, func, block, insn); - } - break; - - 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: - /* Spill all caller-saved registers before a call */ - _jit_regs_spill_all(gen); - _jit_gen_insn(gen, func, block, insn); - break; - -#ifndef JIT_BACKEND_INTERP - case JIT_OP_INCOMING_REG: - /* Assign a register to an incoming value */ - _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: - /* Set the frame position for an incoming value */ - 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: - /* Copy a value into an outgoing register */ - _jit_regs_set_outgoing(gen, - (int)jit_value_get_nint_constant(insn->value2), - insn->value1); - break; -#endif - - case JIT_OP_OUTGOING_FRAME_POSN: - /* Set the frame position for an outgoing value */ - insn->value1->frame_offset = jit_value_get_nint_constant(insn->value2); - insn->value1->in_register = 0; - insn->value1->in_global_register = 0; - insn->value1->in_frame = 0; - insn->value1->has_frame_offset = 1; - insn->value1->has_global_register = 0; - break; - -#ifndef JIT_BACKEND_INTERP - case JIT_OP_RETURN_REG: - /* Assign a register to a return value */ - _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 the current code position as corresponding - to a particular bytecode offset */ - _jit_cache_mark_bytecode(&gen->posn, - (unsigned long)(long) - jit_value_get_nint_constant(insn->value1)); - break; - - default: - /* Generate code for the instruction with the back end */ - _jit_gen_insn(gen, func, block, insn); - break; - } - -#ifdef _JIT_COMPILE_DEBUG - p2 = gen->posn.ptr; - printf("Length of binary code: %d\n\n", p2 - p1); - fflush(stdout); -#endif - } -} - -/* - * Reset value on restart. - */ -static void -reset_value(jit_value_t value) -{ - value->reg = -1; - value->in_register = 0; - value->in_global_register = 0; - value->in_frame = 0; -} - -/* - * Clean up the compilation state on restart. - */ -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) - { - /* Clear the block addresses and fixup lists */ - block->address = 0; - block->fixup_list = 0; - block->fixup_absolute_list = 0; - - /* Reset values referred to by block instructions */ - 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); - } - } - } - - /* Reset values referred to by builder */ - if(func->builder->setjmp_value) - { - reset_value(func->builder->setjmp_value); - } - if(func->builder->parent_frame) - { - reset_value(func->builder->parent_frame); - } - - /* Reset the "touched" registers mask. The first time compilation - might have followed wrong code paths and thus allocated wrong - registers. */ - if(func->builder->has_tail_call) - { - /* For functions with tail calls _jit_regs_alloc_global() - does not allocate any global registers. The "permanent" - mask has all global registers set to prevent their use. */ - gen->touched = jit_regused_init; - } - else - { - gen->touched = gen->permanent; - } - - /* Reset the epilog fixup list */ - gen->epilog_fixup = 0; -} - -/* - * Compile a function and return its entry point. - */ -static int -compile(jit_function_t func, void **entry_point) -{ - struct jit_gencode gen; - jit_cache_t cache; - unsigned char *start; - unsigned char *end; - jit_block_t block; - int page_factor; - int result; - - /* Initialize the code generation state */ - jit_memzero(&gen, sizeof(gen)); - page_factor = 0; - start = 0; - end = 0; - - /* Intuit "nothrow" and "noreturn" flags for this function */ - if(!(func->builder->may_throw)) - { - func->no_throw = 1; - } - if(!(func->builder->ordinary_return)) - { - func->no_return = 1; - } - - /* Build control flow graph */ - if(!_jit_block_build_cfg(func)) - { - return 0; - } - - /* Eliminate useless control flow */ - if(!_jit_block_clean_cfg(func)) - { - return 0; - } - - /* Compute liveness and "next use" information for this function */ - _jit_function_compute_liveness(func); - - /* Allocate global registers to variables within the function */ -#ifndef JIT_BACKEND_INTERP - _jit_regs_alloc_global(&gen, func); -#endif - - /* We need the cache lock while we are compiling the function */ - jit_mutex_lock(&(func->context->cache_lock)); - -#ifdef _JIT_COMPILE_DEBUG - printf("\n*** Start compilation ***\n\n"); - func->builder->block_count = 0; - func->builder->insn_count = 0; -#endif - - /* Get the method cache */ - cache = _jit_context_get_cache(func->context); - if(!cache) - { - jit_mutex_unlock(&(func->context->cache_lock)); - return 0; - } - - /* Start function output to the cache */ - result = _jit_cache_start_method(cache, &(gen.posn), - page_factor++, - JIT_FUNCTION_ALIGNMENT, func); - if (result == JIT_CACHE_RESTART) - { - /* No space left on the current cache page. Allocate a new one. */ - result = _jit_cache_start_method(cache, &(gen.posn), - page_factor++, - JIT_FUNCTION_ALIGNMENT, func); - } - if (result != JIT_CACHE_OK) - { - /* Failed to allocate any cache space */ - jit_mutex_unlock(&(func->context->cache_lock)); - return 0; - } - - for(;;) - { - start = gen.posn.ptr; - -#ifdef jit_extra_gen_init - /* Initialize information that may need to be reset each loop */ - jit_extra_gen_init(&gen); -#endif - -#ifdef JIT_PROLOG_SIZE - /* Output space for the function prolog */ - if(!jit_cache_check_for_n(&(gen.posn), JIT_PROLOG_SIZE)) - { - /* No space left on the current cache page. Restart. */ - jit_cache_mark_full(&(gen.posn)); - goto restart; - } - gen.posn.ptr += JIT_PROLOG_SIZE; -#endif - - /* Generate code for the blocks in the function */ - block = 0; - while((block = jit_block_next(func, block)) != 0) - { - /* Notify the back end that the block is starting */ - _jit_gen_start_block(&gen, block); - -#ifndef JIT_BACKEND_INTERP - /* Clear the local register assignments */ - _jit_regs_init_for_block(&gen); -#endif - - /* Generate the block's code */ - compile_block(&gen, func, block); - -#ifndef JIT_BACKEND_INTERP - /* Spill all live register values back to their frame positions */ - _jit_regs_spill_all(&gen); -#endif - - /* Notify the back end that the block is finished */ - _jit_gen_end_block(&gen, block); - - /* Stop code generation if the cache page is full */ - if(_jit_cache_is_full(cache, &(gen.posn))) - { - /* No space left on the current cache page. Restart. */ - goto restart; - } - } - - /* Output the function epilog. All return paths will jump to here */ - _jit_gen_epilog(&gen, func); - end = gen.posn.ptr; - -#ifdef JIT_PROLOG_SIZE - /* Back-patch the function prolog and get the real entry point */ - start = _jit_gen_prolog(&gen, func, start); -#endif - -#if !defined(JIT_BACKEND_INTERP) && (!defined(jit_redirector_size) || !defined(jit_indirector_size)) - /* If the function is recompilable, then we need an extra entry - point to properly redirect previous references to the function */ - if(func->is_recompilable && !func->indirector) - { - /* TODO: use _jit_create_indirector() instead of - _jit_gen_redirector() as both do the same. */ - func->indirector = _jit_gen_redirector(&gen, func); - } -#endif - - restart: - /* End the function's output process */ - result = _jit_cache_end_method(&(gen.posn)); - if(result != JIT_CACHE_RESTART) - { - break; - } - - /* Clean up the compilation state before restart */ - cleanup_on_restart(&gen, func); - -#ifdef _JIT_COMPILE_DEBUG - printf("\n*** Restart compilation ***\n\n"); - func->builder->block_count = 0; - func->builder->insn_count = 0; -#endif - - /* Restart function output to the cache */ - result = _jit_cache_start_method(cache, &(gen.posn), - page_factor, - JIT_FUNCTION_ALIGNMENT, func); - if(result != JIT_CACHE_OK) - { -#ifdef jit_extra_gen_cleanup - /* Clean up the extra code generation state */ - jit_extra_gen_cleanup(gen); -#endif - jit_mutex_unlock(&(func->context->cache_lock)); - return 0; - } - page_factor *= 2; - } - -#ifdef jit_extra_gen_cleanup - /* Clean up the extra code generation state */ - jit_extra_gen_cleanup(gen); -#endif - - /* Bail out if we ran out of memory while translating the function */ - if(result != JIT_CACHE_OK) - { - jit_mutex_unlock(&(func->context->cache_lock)); - return 0; - } - -#ifndef JIT_BACKEND_INTERP - /* Perform a CPU cache flush, to make the code executable */ - jit_flush_exec(start, (unsigned int)(end - start)); -#endif - - /* The function has been compiled successfully */ - jit_mutex_unlock(&(func->context->cache_lock)); - - /* Free the builder structure, which we no longer require */ - _jit_function_free_builder(func); - - /* Record the entry point */ - if(entry_point) - { - *entry_point = start; - } - - return 1; -} - /* * Information that is stored for an exception region in the cache. */ @@ -932,122 +503,6 @@ struct jit_cache_eh jit_cache_eh_t previous; }; -/*@ - * @deftypefun int jit_function_compile (jit_function_t @var{func}) - * Compile a function to its executable form. If the function was - * already compiled, then do nothing. Returns zero on error. - * - * If an error occurs, you can use @code{jit_function_abandon} to - * completely destroy the function. Once the function has been compiled - * successfully, it can no longer be abandoned. - * - * Sometimes you may wish to recompile a function, to apply greater - * levels of optimization the second time around. You must call - * @code{jit_function_set_recompilable} before you compile the function - * the first time. On the second time around, build the function's - * instructions again, and call @code{jit_function_compile} - * a second time. - * @end deftypefun -@*/ -int -jit_function_compile(jit_function_t func) -{ - int result; - void *entry_point; - - /* Bail out if we have nothing to do */ - if(!func) - { - return 0; - } - if(func->is_compiled && !(func->builder)) - { - /* The function is already compiled, and we don't need to recompile */ - return 1; - } - if(!(func->builder)) - { - /* We don't have anything to compile at all */ - return 0; - } - - /* Compile and record the entry point. */ - result = compile(func, &entry_point); - if(result) - { - func->entry_point = entry_point; - func->is_compiled = 1; - } - - return result; -} - -/*@ - * @deftypefun int jit_function_compile_entry (jit_function_t @var{func}, void **@var{entry_point}) - * Compile a function to its executable form but do not make it - * available for invocation yet. It may be made available later - * with @code{jit_function_setup_entry}. - * @end deftypefun -@*/ -int -jit_function_compile_entry(jit_function_t func, void **entry_point) -{ - /* Init entry_point */ - if(entry_point) - { - *entry_point = 0; - } - else - { - return 0; - } - - /* Bail out if we have nothing to do */ - if(!func) - { - return 0; - } - if(func->is_compiled && !(func->builder)) - { - /* The function is already compiled, and we don't need to recompile */ - *entry_point = func->entry_point; - return 1; - } - if(!(func->builder)) - { - /* We don't have anything to compile at all */ - return 0; - } - - /* Compile and return the entry point. */ - return compile(func, entry_point); -} - -/*@ - * @deftypefun int jit_function_setup_entry (jit_function_t @var{func}, void *@var{entry_point}) - * Make a function compiled with @code{jit_function_compile_entry} - * available for invocation and free the resources used for - * compilation. If @var{entry_point} is null then it only - * frees the resources. - * @end deftypefun -@*/ -void -jit_function_setup_entry(jit_function_t func, void *entry_point) -{ - /* Bail out if we have nothing to do */ - if(!func) - { - return; - } - /* Record the entry point */ - if(entry_point) - { - func->entry_point = entry_point; - func->is_compiled = 1; - } - _jit_function_free_builder(func); -} - /*@ * @deftypefun int jit_function_is_compiled (jit_function_t @var{func}) * Determine if a function has already been compiled. @@ -1369,62 +824,6 @@ jit_function_get_on_demand_compiler(jit_function_t func) return 0; } -void *_jit_function_compile_on_demand(jit_function_t func) -{ - void *entry = 0; - int result = JIT_RESULT_OK; - - /* Lock down the context */ - jit_context_build_start(func->context); - - /* If we are already compiled, then bail out */ - if(func->is_compiled) - { - entry = func->entry_point; - jit_context_build_end(func->context); - return entry; - } - - /* Call the user's on-demand compiler. Bail out with an error - if the user didn't supply an on-demand compiler */ - if(func->on_demand) - { - result = (*(func->on_demand))(func); - if(result == JIT_RESULT_OK) - { - /* Compile the function if the user didn't do so */ - if(!(func->is_compiled)) - { - if(jit_function_compile(func)) - { - entry = func->entry_point; - } - else - { - result = JIT_RESULT_OUT_OF_MEMORY; - } - } - else - { - entry = func->entry_point; - } - } - _jit_function_free_builder(func); - } - else - { - result = JIT_RESULT_COMPILE_ERROR; - } - - /* Unlock the context and report the result */ - jit_context_build_end(func->context); - if(result != JIT_RESULT_OK) - { - jit_exception_builtin(result); - } - return entry; -} - /*@ * @deftypefun int jit_function_apply (jit_function_t @var{func}, void **@var{args}, void *@var{return_area}) * Call the function @var{func} with the supplied arguments. Each element @@ -1547,8 +946,8 @@ int jit_function_apply_vararg * reached the maximum optimization level. * @end deftypefun @*/ -void jit_function_set_optimization_level - (jit_function_t func, unsigned int level) +void +jit_function_set_optimization_level(jit_function_t func, unsigned int level) { unsigned int max_level = jit_function_get_max_optimization_level(); if(level > max_level) @@ -1557,7 +956,7 @@ void jit_function_set_optimization_level } if(func) { - func->optimization_level = (int)level; + func->optimization_level = level; } } @@ -1566,15 +965,16 @@ void jit_function_set_optimization_level * Get the current optimization level for @var{func}. * @end deftypefun @*/ -unsigned int jit_function_get_optimization_level(jit_function_t func) +unsigned int +jit_function_get_optimization_level(jit_function_t func) { if(func) { - return (unsigned int)(func->optimization_level); + return func->optimization_level; } else { - return 0; + return JIT_OPTLEVEL_NONE; } } @@ -1583,10 +983,10 @@ unsigned int jit_function_get_optimization_level(jit_function_t func) * Get the maximum optimization level that is supported by @code{libjit}. * @end deftypefun @*/ -unsigned int jit_function_get_max_optimization_level(void) +unsigned int +jit_function_get_max_optimization_level(void) { - /* TODO - implement more than basic optimization */ - return 0; + return JIT_OPTLEVEL_NORMAL; } /*@ diff --git a/jit/jit-internal.h b/jit/jit-internal.h index e785e0b..1eaf0c6 100644 --- a/jit/jit-internal.h +++ b/jit/jit-internal.h @@ -441,6 +441,7 @@ struct _jit_function /* Flag bits for this function */ unsigned is_recompilable : 1; + unsigned is_optimized : 1; unsigned no_throw : 1; unsigned no_return : 1; unsigned has_try : 1; @@ -601,12 +602,12 @@ void _jit_block_free(jit_function_t func); * Build control flow graph edges for all blocks associated with a * function. */ -int _jit_block_build_cfg(jit_function_t func); +void _jit_block_build_cfg(jit_function_t func); /* * Eliminate useless control flow between blocks in a function. */ -int _jit_block_clean_cfg(jit_function_t func); +void _jit_block_clean_cfg(jit_function_t func); /* * Compute block postorder for control flow graph depth first traversal.