#include "jit-internal.h"
typedef struct _jit_block_stack_entry
{
jit_block_t block;
int index;
} _jit_block_stack_entry_t;
static void
create_edge(jit_function_t func, jit_block_t src, jit_block_t dst, int flags, int create)
{
_jit_edge_t edge;
if(create)
{
edge = jit_memory_pool_alloc(&func->builder->edge_pool, struct _jit_edge);
if(!edge)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
edge->src = src;
edge->dst = dst;
edge->flags = flags;
src->succs[src->num_succs] = edge;
dst->preds[dst->num_preds] = edge;
}
++(src->num_succs);
++(dst->num_preds);
}
static void
build_edges(jit_function_t func, int create)
{
jit_block_t src, dst;
jit_insn_t insn;
int opcode, flags;
jit_label_t *labels;
int index, num_labels;
for(src = func->builder->entry_block; src != func->builder->exit_block; src = src->next)
{
insn = _jit_block_get_last(src);
opcode = insn ? insn->opcode : JIT_OP_NOP;
if(opcode >= JIT_OP_RETURN && opcode <= JIT_OP_RETURN_SMALL_STRUCT)
{
flags = _JIT_EDGE_RETURN;
dst = func->builder->exit_block;
}
else if(opcode == JIT_OP_BR)
{
flags = _JIT_EDGE_BRANCH;
dst = jit_block_from_label(func, (jit_label_t) insn->dest);
if(!dst)
{
jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL);
}
}
else if(opcode > JIT_OP_BR && opcode <= JIT_OP_BR_NFGE_INV)
{
flags = _JIT_EDGE_BRANCH;
dst = jit_block_from_label(func, (jit_label_t) insn->dest);
if(!dst)
{
jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL);
}
}
else if(opcode == JIT_OP_THROW || opcode == JIT_OP_RETHROW)
{
flags = _JIT_EDGE_EXCEPT;
dst = jit_block_from_label(func, func->builder->catcher_label);
if(!dst)
{
dst = func->builder->exit_block;
}
}
else if(opcode == JIT_OP_CALL_FINALLY || opcode == JIT_OP_CALL_FILTER)
{
flags = _JIT_EDGE_EXCEPT;
dst = jit_block_from_label(func, (jit_label_t) insn->dest);
if(!dst)
{
jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL);
}
}
else if(opcode >= JIT_OP_CALL && opcode <= JIT_OP_CALL_EXTERNAL_TAIL)
{
flags = _JIT_EDGE_EXCEPT;
dst = jit_block_from_label(func, func->builder->catcher_label);
if(!dst)
{
dst = func->builder->exit_block;
}
}
else if(opcode == JIT_OP_JUMP_TABLE)
{
labels = (jit_label_t *) insn->value1->address;
num_labels = (int) insn->value2->address;
for(index = 0; index < num_labels; index++)
{
dst = jit_block_from_label(func, labels[index]);
if(!dst)
{
jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL);
}
create_edge(func, src, dst, _JIT_EDGE_BRANCH, create);
}
dst = 0;
}
else
{
dst = 0;
}
if(dst)
{
create_edge(func, src, dst, flags, create);
}
if(!src->ends_in_dead)
{
create_edge(func, src, src->next, _JIT_EDGE_FALLTHRU, create);
}
}
}
static void
alloc_edges(jit_function_t func)
{
jit_block_t block;
for(block = func->builder->entry_block; block; block = block->next)
{
if(block->num_succs == 0)
{
block->succs = 0;
}
else
{
block->succs = jit_calloc(block->num_succs, sizeof(_jit_edge_t));
if(!block->succs)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
block->num_succs = 0;
}
if(block->num_preds == 0)
{
block->preds = 0;
}
else
{
block->preds = jit_calloc(block->num_preds, sizeof(_jit_edge_t));
if(!block->preds)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
block->num_preds = 0;
}
}
}
static void
detach_edge_src(_jit_edge_t edge)
{
jit_block_t block;
int index;
block = edge->src;
for(index = 0; index < block->num_succs; index++)
{
if(block->succs[index] == edge)
{
for(block->num_succs--; index < block->num_succs; index++)
{
block->succs[index] = block->succs[index + 1];
}
block->succs = jit_realloc(block->succs, block->num_succs * sizeof(_jit_edge_t));
return;
}
}
}
static void
detach_edge_dst(_jit_edge_t edge)
{
jit_block_t block;
int index;
block = edge->dst;
for(index = 0; index < block->num_preds; index++)
{
if(block->preds[index] == edge)
{
for(block->num_preds--; index < block->num_preds; index++)
{
block->preds[index] = block->preds[index + 1];
}
block->preds = jit_realloc(block->preds,
block->num_preds * sizeof(_jit_edge_t));
return;
}
}
}
static void
attach_edge_dst(_jit_edge_t edge, jit_block_t block)
{
_jit_edge_t *preds;
preds = jit_realloc(block->preds, (block->num_preds + 1) * sizeof(_jit_edge_t));
if(!preds)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
preds[block->num_preds++] = edge;
block->preds = preds;
edge->dst = block;
}
static void
delete_edge(jit_function_t func, _jit_edge_t edge)
{
detach_edge_src(edge);
detach_edge_dst(edge);
jit_memory_pool_dealloc(&func->builder->edge_pool, edge);
}
static void
delete_block(jit_block_t block)
{
jit_free(block->succs);
block->succs = 0;
jit_free(block->preds);
block->preds = 0;
jit_free(block->insns);
block->insns = 0;
block->next = block->func->builder->deleted_blocks;
block->func->builder->deleted_blocks = block;
}
static int
is_empty_block(jit_block_t block)
{
int index, opcode;
index = block->num_insns;
if(index == 0)
{
return 1;
}
opcode = block->insns[--index].opcode;
if(opcode != JIT_OP_NOP && opcode != JIT_OP_MARK_OFFSET && opcode != JIT_OP_BR)
{
return 0;
}
while(index > 0)
{
opcode = block->insns[--index].opcode;
if(opcode != JIT_OP_NOP && opcode != JIT_OP_MARK_OFFSET)
{
return 0;
}
}
return 1;
}
#ifdef _JIT_BLOCK_DEBUG
static void
label_loop_check(jit_function_t func, jit_block_t block, jit_label_t label)
{
jit_label_t block_label = block->label;
while(block_label != jit_label_undefined)
{
if(block_label == label)
{
abort();
}
block_label = func->builder->label_info[block_label].alias;
}
}
#endif
static void
merge_labels(jit_function_t func, jit_block_t src, jit_block_t dst)
{
_jit_label_info_t *info;
jit_label_t label, alias;
label = src->label;
src->label = jit_label_undefined;
#ifdef _JIT_BLOCK_DEBUG
label_loop_check(func, dst, label);
#endif
while(label != jit_label_undefined)
{
info = &func->builder->label_info[label];
alias = info->alias;
if((info->flags & JIT_LABEL_ADDRESS_OF) == 0)
{
info->block = dst;
info->alias = dst->label;
dst->label = label;
}
else
{
info->alias = src->label;
src->label = label;
}
label = alias;
}
}
static void
merge_empty(jit_function_t func, jit_block_t block, int *changed)
{
_jit_edge_t succ_edge, pred_edge, fallthru_edge;
jit_block_t succ_block;
int index;
succ_edge = block->succs[0];
succ_block = succ_edge->dst;
merge_labels(func, block, succ_block);
fallthru_edge = 0;
for(index = 0; index < block->num_preds; index++)
{
pred_edge = block->preds[index];
if(pred_edge->flags == _JIT_EDGE_FALLTHRU)
{
fallthru_edge = pred_edge;
}
else
{
*changed = 1;
attach_edge_dst(pred_edge, succ_block);
}
}
if(!block->address_of && fallthru_edge && succ_edge->flags == _JIT_EDGE_FALLTHRU)
{
*changed = 1;
attach_edge_dst(fallthru_edge, succ_block);
fallthru_edge = 0;
}
if(fallthru_edge)
{
if(block->num_preds > 1)
{
block->num_preds = 1;
block->preds = jit_realloc(block->preds, sizeof(_jit_edge_t));
block->preds[0] = fallthru_edge;
}
}
else if(block->address_of)
{
if(block->num_preds > 0)
{
block->num_preds = 0;
jit_free(block->preds);
block->preds = 0;
}
}
else
{
detach_edge_dst(succ_edge);
jit_memory_pool_dealloc(&func->builder->edge_pool, succ_edge);
_jit_block_detach(block, block);
delete_block(block);
}
}
static void
combine_block(jit_function_t func, jit_block_t block, int *changed)
{
jit_block_t succ_block;
int branch, num_insns, max_insns;
jit_insn_t insns;
succ_block = block->succs[0]->dst;
branch = (block->succs[0]->flags == _JIT_EDGE_BRANCH);
if(branch && !succ_block->max_insns)
{
succ_block->insns = jit_malloc(sizeof(struct _jit_insn));
if(!succ_block->insns)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
succ_block->max_insns = 1;
}
max_insns = block->max_insns;
num_insns = block->num_insns + succ_block->num_insns;
if(num_insns > max_insns)
{
max_insns = num_insns;
insns = (jit_insn_t) jit_realloc(block->insns,
max_insns * sizeof(struct _jit_insn));
if(!insns)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
}
else
{
insns = block->insns;
}
if(succ_block->num_insns)
{
jit_memcpy(insns + block->num_insns,
succ_block->insns,
succ_block->num_insns * sizeof(struct _jit_insn));
}
block->insns = succ_block->insns;
block->max_insns = succ_block->max_insns;
if(branch)
{
jit_memcpy(block->insns,
insns + block->num_insns - 1,
sizeof(struct _jit_insn));
insns[block->num_insns - 1].opcode = JIT_OP_NOP;
}
block->num_insns = branch;
succ_block->insns = insns;
succ_block->max_insns = max_insns;
succ_block->num_insns = num_insns;
merge_empty(func, block, changed);
}
static void
split_address_of(jit_function_t func, jit_block_t block, jit_label_t label)
{
int index;
jit_insn_t insn;
jit_label_t branch_label;
jit_label_t *jump_labels;
int jump_index, num_labels;
branch_label = jit_label_undefined;
for(index = 0; index < block->num_preds; index++)
{
if(block->preds[index]->flags != _JIT_EDGE_BRANCH)
{
continue;
}
if(branch_label == jit_label_undefined)
{
branch_label = (func->builder->next_label)++;
if(!_jit_block_record_label(block, branch_label))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
}
insn = _jit_block_get_last(block->preds[index]->src);
if(insn->opcode != JIT_OP_JUMP_TABLE)
{
insn->dest = (jit_value_t)branch_label;
}
else
{
jump_labels = (jit_label_t *) insn->value1->address;
num_labels = (int) insn->value2->address;
for(jump_index = 0; jump_index < num_labels; jump_index++)
{
if(jump_labels[jump_index] == label)
{
jump_labels[jump_index] = branch_label;
}
}
}
}
}
static void
set_address_of(jit_function_t func)
{
int index, count;
jit_block_t block;
count = func->builder->max_label_info;
for(index = 0; index < count; index++)
{
block = func->builder->label_info[index].block;
if(block && (func->builder->label_info[index].flags & JIT_LABEL_ADDRESS_OF) != 0)
{
block->address_of = 1;
split_address_of(func, block, index);
}
}
}
static void
eliminate_block(jit_block_t block)
{
_jit_edge_t edge;
int index;
_jit_block_detach(block, block);
for(index = 0; index < block->num_succs; index++)
{
edge = block->succs[index];
detach_edge_dst(edge);
jit_memory_pool_dealloc(&block->func->builder->edge_pool, edge);
}
for(index = 0; index < block->num_preds; index++)
{
edge = block->preds[index];
detach_edge_src(edge);
jit_memory_pool_dealloc(&block->func->builder->edge_pool, edge);
}
delete_block(block);
}
#if 0#endif
static void
eliminate_unreachable(jit_function_t func)
{
jit_block_t block, next_block;
block = func->builder->entry_block;
while(block != func->builder->exit_block)
{
next_block = block->next;
if(block->visited)
{
block->visited = 0;
}
else if(!block->address_of)
{
eliminate_block(block);
}
block = next_block;
}
}
static void
clear_visited(jit_function_t func)
{
jit_block_t block;
for(block = func->builder->entry_block; block; block = block->next)
{
block->visited = 0;
}
}
static int
count_blocks(jit_function_t func)
{
int count;
jit_block_t block;
count = 0;
for(block = func->builder->entry_block; block; block = block->next)
{
++count;
}
return count;
}
static void
free_order(jit_function_t func)
{
jit_free(func->builder->block_order);
func->builder->block_order = NULL;
func->builder->num_block_order = 0;
}
int
_jit_block_init(jit_function_t func)
{
func->builder->entry_block = _jit_block_create(func);
if(!func->builder->entry_block)
{
return 0;
}
func->builder->exit_block = _jit_block_create(func);
if(!func->builder->exit_block)
{
return 0;
}
func->builder->entry_block->next = func->builder->exit_block;
func->builder->exit_block->prev = func->builder->entry_block;
return 1;
}
void
_jit_block_free(jit_function_t func)
{
jit_block_t block, next;
free_order(func);
block = func->builder->entry_block;
while(block)
{
next = block->next;
_jit_block_destroy(block);
block = next;
}
block = func->builder->deleted_blocks;
while(block)
{
next = block->next;
_jit_block_destroy(block);
block = next;
}
func->builder->entry_block = 0;
func->builder->exit_block = 0;
}
void
_jit_block_build_cfg(jit_function_t func)
{
build_edges(func, 0);
alloc_edges(func);
build_edges(func, 1);
}
void
_jit_block_clean_cfg(jit_function_t func)
{
int index, changed;
jit_block_t block;
jit_insn_t insn;
if(!_jit_block_compute_postorder(func))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
set_address_of(func);
eliminate_unreachable(func);
loop:
changed = 0;
for(index = 1; index < (func->builder->num_block_order - 1); index++)
{
block = func->builder->block_order[index];
if(block->num_succs == 0)
{
continue;
}
if(block->succs[0]->flags == _JIT_EDGE_BRANCH)
{
insn = _jit_block_get_last(block);
if(insn->opcode == JIT_OP_JUMP_TABLE)
{
continue;
}
if(block->succs[0]->dst == block->next)
{
changed = 1;
insn->opcode = JIT_OP_NOP;
if(block->num_succs == 2)
{
#ifdef _JIT_BLOCK_DEBUG
printf("%d cbranch->fallthru %d\n", index, block->label);
#endif
delete_edge(func, block->succs[0]);
}
else
{
#ifdef _JIT_BLOCK_DEBUG
printf("%d ubranch->fallthru %d\n", index, block->label);
#endif
block->ends_in_dead = 0;
block->succs[0]->flags = _JIT_EDGE_FALLTHRU;
}
}
else if(block->num_succs == 2
&& block->next->num_succs == 1
&& block->next->succs[0]->flags == _JIT_EDGE_BRANCH
&& block->succs[0]->dst == block->next->succs[0]->dst
&& is_empty_block(block->next))
{
#ifdef _JIT_BLOCK_DEBUG
printf("%d cbranch->ubranch %d\n", index, block->label);
#endif
changed = 1;
insn->opcode = JIT_OP_BR;
block->ends_in_dead = 1;
delete_edge(func, block->succs[1]);
}
}
if(block->num_succs == 1
&& (block->succs[0]->flags == _JIT_EDGE_BRANCH
|| block->succs[0]->flags == _JIT_EDGE_FALLTHRU))
{
if(is_empty_block(block))
{
#ifdef _JIT_BLOCK_DEBUG
printf("%d merge_empty %d\n", index, block->label);
#endif
merge_empty(func, block, &changed);
}
else if(block->succs[0]->dst->num_preds == 1
&& block->succs[0]->dst->address_of == 0)
{
#ifdef _JIT_BLOCK_DEBUG
printf("%d combine_block %d\n", index, block->label);
#endif
combine_block(func, block, &changed);
}
}
}
if(changed)
{
if(!_jit_block_compute_postorder(func))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
clear_visited(func);
goto loop;
}
}
int
_jit_block_compute_postorder(jit_function_t func)
{
int num_blocks, index, num, top;
jit_block_t *blocks, block, succ;
_jit_block_stack_entry_t *stack;
if(func->builder->block_order)
{
free_order(func);
}
num_blocks = count_blocks(func);
blocks = (jit_block_t *) jit_malloc(num_blocks * sizeof(jit_block_t));
if(!blocks)
{
return 0;
}
stack = (_jit_block_stack_entry_t *) jit_malloc(num_blocks * sizeof(_jit_block_stack_entry_t));
if(!stack)
{
jit_free(blocks);
return 0;
}
func->builder->entry_block->visited = 1;
stack[0].block = func->builder->entry_block;
stack[0].index = 0;
top = 1;
num = 0;
do
{
block = stack[top - 1].block;
index = stack[top - 1].index;
if(index == block->num_succs)
{
blocks[num++] = block;
--top;
}
else
{
succ = block->succs[index]->dst;
if(succ->visited)
{
stack[top - 1].index = index + 1;
}
else
{
succ->visited = 1;
stack[top].block = succ;
stack[top].index = 0;
++top;
}
}
}
while(top);
jit_free(stack);
if(num < num_blocks)
{
blocks = jit_realloc(blocks, num * sizeof(jit_block_t));
}
func->builder->block_order = blocks;
func->builder->num_block_order = num;
return 1;
}
jit_block_t
_jit_block_create(jit_function_t func)
{
jit_block_t block;
block = jit_cnew(struct _jit_block);
if(!block)
{
return 0;
}
block->func = func;
block->label = jit_label_undefined;
return block;
}
void
_jit_block_destroy(jit_block_t block)
{
jit_meta_destroy(&block->meta);
jit_free(block->succs);
jit_free(block->preds);
jit_free(block->insns);
jit_free(block);
}
void
_jit_block_detach(jit_block_t first, jit_block_t last)
{
last->next->prev = first->prev;
first->prev->next = last->next;
}
void
_jit_block_attach_after(jit_block_t block, jit_block_t first, jit_block_t last)
{
first->prev = block;
last->next = block->next;
block->next->prev = last;
block->next = first;
}
void
_jit_block_attach_before(jit_block_t block, jit_block_t first, jit_block_t last)
{
first->prev = block->prev;
last->next = block;
block->prev->next = first;
block->prev = last;
}
static int
ensure_label_table(jit_function_t func, jit_label_t label)
{
jit_label_t num;
_jit_label_info_t *info;
if(label >= func->builder->max_label_info)
{
num = func->builder->max_label_info;
if(num < 64)
{
num = 64;
}
while(num <= label)
{
num *= 2;
}
info = (_jit_label_info_t *) jit_realloc(func->builder->label_info,
num * sizeof(_jit_label_info_t));
if(!info)
{
return 0;
}
jit_memzero(info + func->builder->max_label_info,
sizeof(_jit_label_info_t) * (num - func->builder->max_label_info));
func->builder->label_info = info;
func->builder->max_label_info = num;
}
return 1;
}
int
_jit_block_record_label(jit_block_t block, jit_label_t label)
{
jit_builder_t builder;
if(!ensure_label_table(block->func, label))
{
return 0;
}
builder = block->func->builder;
if(builder->label_info[label].block)
{
return 0;
}
builder->label_info[label].block = block;
builder->label_info[label].alias = block->label;
block->label = label;
return 1;
}
int
_jit_block_record_label_flags(jit_function_t func, jit_label_t label, int flags)
{
if(!ensure_label_table(func, label))
{
return 0;
}
func->builder->label_info[label].flags = flags;
return 1;
}
jit_insn_t
_jit_block_add_insn(jit_block_t block)
{
int max_insns;
jit_insn_t insns;
if(block->num_insns == block->max_insns)
{
max_insns = block->max_insns ? block->max_insns * 2 : 4;
insns = (jit_insn_t) jit_realloc(block->insns,
max_insns * sizeof(struct _jit_insn));
if(!insns)
{
return 0;
}
block->insns = insns;
block->max_insns = max_insns;
}
jit_memzero(&block->insns[block->num_insns], sizeof(struct _jit_insn));
return &block->insns[block->num_insns++];
}
jit_insn_t
_jit_block_get_last(jit_block_t block)
{
if(block->num_insns > 0)
{
return &block->insns[block->num_insns - 1];
}
else
{
return 0;
}
}
int
_jit_block_is_final(jit_block_t block)
{
for(block = block->next; block; block = block->next)
{
if(block->num_insns)
{
return 0;
}
}
return 1;
}
jit_function_t
jit_block_get_function(jit_block_t block)
{
if(block)
{
return block->func;
}
else
{
return 0;
}
}
jit_context_t
jit_block_get_context(jit_block_t block)
{
if(block)
{
return block->func->context;
}
else
{
return 0;
}
}
jit_label_t
jit_block_get_label(jit_block_t block)
{
if(block)
{
return block->label;
}
return jit_label_undefined;
}
jit_label_t
jit_block_get_next_label(jit_block_t block, jit_label_t label)
{
jit_builder_t builder;
if(block)
{
if(label == jit_label_undefined)
{
return block->label;
}
builder = block->func->builder;
if(builder
&& label < builder->max_label_info
&& block == builder->label_info[label].block)
{
return builder->label_info[label].alias;
}
}
return jit_label_undefined;
}
jit_block_t
jit_block_next(jit_function_t func, jit_block_t previous)
{
if(previous)
{
return previous->next;
}
else if(func && func->builder)
{
return func->builder->entry_block;
}
else
{
return 0;
}
}
jit_block_t
jit_block_previous(jit_function_t func, jit_block_t previous)
{
if(previous)
{
return previous->prev;
}
else if(func && func->builder)
{
return func->builder->exit_block;
}
else
{
return 0;
}
}
jit_block_t
jit_block_from_label(jit_function_t func, jit_label_t label)
{
if(func && func->builder && label < func->builder->max_label_info)
{
return func->builder->label_info[label].block;
}
else
{
return 0;
}
}
int
jit_block_set_meta(jit_block_t block, int type, void *data, jit_meta_free_func free_data)
{
return jit_meta_set(&(block->meta), type, data, free_data, block->func);
}
void *
jit_block_get_meta(jit_block_t block, int type)
{
return jit_meta_get(block->meta, type);
}
void
jit_block_free_meta(jit_block_t block, int type)
{
jit_meta_free(&(block->meta), type);
}
int
jit_block_is_reachable(jit_block_t block)
{
jit_block_t entry;
entry = block->func->builder->entry_block;
while(block != entry && block->label == jit_label_undefined)
{
block = block->prev;
if(block->ends_in_dead)
{
return 0;
}
}
return 1;
}
int
jit_block_ends_in_dead(jit_block_t block)
{
return block->ends_in_dead;
}
int
jit_block_current_is_dead(jit_function_t func)
{
jit_block_t block = jit_block_previous(func, 0);
return !block || jit_block_ends_in_dead(block) || !jit_block_is_reachable(block);
}