Alien-LibJIT
view release on metacpan or search on metacpan
libjit/jit/jit-block.c view on Meta::CPAN
/*
* jit-block.c - Functions for manipulating blocks.
*
* Copyright (C) 2004 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
* <http://www.gnu.org/licenses/>.
*/
#include "jit-internal.h"
/*@
@cindex jit-block.h
@*/
/* helper data structure for CFG DFS traversal */
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;
/* Create edge if required */
if(create)
{
/* Allocate memory for it */
edge = jit_memory_pool_alloc(&func->builder->edge_pool, struct _jit_edge);
if(!edge)
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
/* Initialize edge fields */
edge->src = src;
edge->dst = dst;
edge->flags = flags;
/* Store edge pointers in source and destination nodes */
src->succs[src->num_succs] = edge;
dst->preds[dst->num_preds] = edge;
}
/* Count it */
++(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;
/* TODO: Handle catch, finally, filter blocks. */
for(src = func->builder->entry_block; src != func->builder->exit_block; src = src->next)
{
/* Check the last instruction of the block */
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)
{
/* Bail out on undefined label */
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)
{
/* Bail out on undefined label */
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)
{
/* Bail out on undefined label */
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)
{
/* Bail out on undefined label */
jit_exception_builtin(JIT_RESULT_UNDEFINED_LABEL);
}
create_edge(func, src, dst, _JIT_EDGE_BRANCH, create);
}
dst = 0;
}
else
{
dst = 0;
}
/* create a branch or exception edge if appropriate */
if(dst)
{
create_edge(func, src, dst, flags, create);
}
/* create a fall-through edge if appropriate */
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)
{
/* Allocate edges to successor nodes */
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);
}
/* Reset edge count for the next build pass */
block->num_succs = 0;
}
/* Allocate edges to predecessor nodes */
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);
}
/* Reset edge count for the next build pass */
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)
libjit/jit/jit-block.c view on Meta::CPAN
{
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
/* Merge labels of the src block with labels of the dst block but retain
the address_of labels. This requires the address_of labels to be used
exclusively as such, no branches are allowed to use address_of labels.
This is ensured by the split_address_of() function. */
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;
}
}
/* Merge empty block with its successor */
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;
/* Find block successor */
succ_edge = block->succs[0];
succ_block = succ_edge->dst;
/* Retarget labels bound to this block to the successor block. */
merge_labels(func, block, succ_block);
/* Retarget all incoming edges except a fallthrough edge */
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);
}
}
/* Unless the block is taken address of, the incoming fallthrough edge
can be retargeted and then the block deleted if the outgoing edge is
also fallthrough. */
if(!block->address_of && fallthru_edge && succ_edge->flags == _JIT_EDGE_FALLTHRU)
{
*changed = 1;
attach_edge_dst(fallthru_edge, succ_block);
fallthru_edge = 0;
}
/* Free the block if there is no incoming edge left and it is not taken
address of. Otherwise adjust the preds array accordingly. */
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);
}
}
/* Combine non-empty block with its successor */
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;
/* Find block successor */
succ_block = block->succs[0]->dst;
/* Does block end with a (redundant) branch instruction? */
branch = (block->succs[0]->flags == _JIT_EDGE_BRANCH);
/* If the branch is there then preallocate memory for it,
doing it here simplifies handling of the out-of-memory
condition */
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;
}
/* Allocate enough memory for combined instructions */
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;
}
/* Copy instruction from the successor block after the instructions
of the original block */
if(succ_block->num_insns)
{
jit_memcpy(insns + block->num_insns,
succ_block->insns,
succ_block->num_insns * sizeof(struct _jit_insn));
}
/* Move combined instructions to the successor, but if there was
a branch in the original block then keep the branch around,
merge_empty() will take care of it if it may be optimized away.
To reduce the number of allocations, swap the arrays around
rather than allocating a fresh array for the empty block. */
block->insns = succ_block->insns;
block->max_insns = succ_block->max_insns;
if(branch)
{
/* Copy the branch instruction */
jit_memcpy(block->insns,
insns + block->num_insns - 1,
sizeof(struct _jit_insn));
/* In the combined block turn branch into NOP */
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);
}
/* Allow branch optimization by splitting the label that is both a branch target
and an address-of opcode source into two separate labels with single role.
TODO: handle jump tables. */
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;
}
}
}
}
}
/* Mark blocks that might be taken address of */
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);
}
}
}
/* Delete block along with references to it */
static void
eliminate_block(jit_block_t block)
{
_jit_edge_t edge;
int index;
/* Detach block from the list */
_jit_block_detach(block, block);
/* Remove control flow graph edges */
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);
}
/* Finally delete the block */
delete_block(block);
}
#if 0
/* Visit all successive blocks recursively */
static void
visit_reachable(jit_block_t block)
{
int index;
if(!block->visited)
{
block->visited = 1;
for(index = 0; index < block->num_succs; index++)
{
visit_reachable(block->succs[index]->dst);
}
}
}
#endif
/* Eliminate unreachable blocks */
static void
eliminate_unreachable(jit_function_t func)
{
jit_block_t block, next_block;
libjit/jit/jit-block.c view on Meta::CPAN
}
func->builder->entry_block = 0;
func->builder->exit_block = 0;
}
void
_jit_block_build_cfg(jit_function_t func)
{
/* Count the edges */
build_edges(func, 0);
/* Allocate memory for edges */
alloc_edges(func);
/* Actually build the edges */
build_edges(func, 1);
}
void
_jit_block_clean_cfg(jit_function_t func)
{
int index, changed;
jit_block_t block;
jit_insn_t insn;
/*
* The code below is based on the Clean algorithm described in
* "Engineering a Compiler" by Keith D. Cooper and Linda Torczon,
* section 10.3.1 "Eliminating Useless and Unreachable Code"
* (originally presented in a paper by Rob Shillner and John Lu
* http://www.cs.princeton.edu/~ras/clean.ps).
*
* Because libjit IR differs from ILOC the algorithm here has
* some differences too.
*/
if(!_jit_block_compute_postorder(func))
{
jit_exception_builtin(JIT_RESULT_OUT_OF_MEMORY);
}
set_address_of(func);
eliminate_unreachable(func);
loop:
changed = 0;
/* Go through blocks in post order skipping the entry and exit blocks */
for(index = 1; index < (func->builder->num_block_order - 1); index++)
{
block = func->builder->block_order[index];
if(block->num_succs == 0)
{
continue;
}
/* Take care of redundant branches that is, if possible, either
replace a branch with NOP turning it to a fallthrough case
or reduce a conditional branch to unconditional */
if(block->succs[0]->flags == _JIT_EDGE_BRANCH)
{
insn = _jit_block_get_last(block);
if(insn->opcode == JIT_OP_JUMP_TABLE)
{
/* skip jump tables, handle only branches */
continue;
}
if(block->succs[0]->dst == block->next)
{
/* Replace useless branch with NOP */
changed = 1;
insn->opcode = JIT_OP_NOP;
if(block->num_succs == 2)
{
/* For conditional branch delete the branch
edge while leaving the fallthough edge
intact */
#ifdef _JIT_BLOCK_DEBUG
printf("%d cbranch->fallthru %d\n", index, block->label);
#endif
delete_edge(func, block->succs[0]);
}
else
{
/* For unconditional branch replace the branch
edge with a fallthrough edge */
#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))
{
/* For conditional branch followed by unconditional
that has the same target make the first branch
unconditional too, remove the fallthrough edge while
leaving the branch edge intact */
#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]);
}
}
/* Try to simplify basic blocks that end with fallthrough or
unconditional branch */
if(block->num_succs == 1
&& (block->succs[0]->flags == _JIT_EDGE_BRANCH
|| block->succs[0]->flags == _JIT_EDGE_FALLTHRU))
{
if(is_empty_block(block))
{
/* Remove empty 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)
{
/* Combine with the successor block if it has
only one predecessor */
#ifdef _JIT_BLOCK_DEBUG
printf("%d combine_block %d\n", index, block->label);
#endif
combine_block(func, block, &changed);
}
/* TODO: "hoist branch" part of the Clean algorithm.
It is somewhat complicated as libjit conditional
branches differ too much from ILOC conditional
branches */
}
}
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)
libjit/jit/jit-block.c view on Meta::CPAN
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;
/* Bail out on previously recorded label */
if(builder->label_info[label].block)
{
return 0;
}
/* Record label info to the table */
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;
/* Make space for the instruction in the block's instruction list */
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;
}
/* Zero-init the instruction */
jit_memzero(&block->insns[block->num_insns], sizeof(struct _jit_insn));
/* Return the instruction, which is now ready to fill in */
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;
}
/*@
* @deftypefun jit_function_t jit_block_get_function (jit_block_t @var{block})
* Get the function that a particular @var{block} belongs to.
( run in 1.539 second using v1.01-cache-2.11-cpan-796a6f069b2 )