/*
* Copyright (c) 1999 The University of Utah and
* the Computer Systems Laboratory at the University of Utah (CSL).
*
* This file is part of Flick, the Flexible IDL Compiler Kit.
*
* Flick is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Flick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Flick; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
*/
/* This file handles the construction of any abort code needed */
#include <stdlib.h>
#include <assert.h>
#include <mom/c/libcast.h>
#include <mom/c/pbe.hh>
/* These are the default formats for the labels that are used. */
const char *mu_abort_block::label_format = "_flick_abort%d";
const char *mu_abort_block::block_format = "_flick_abort_block%d";
/* These are the counts for label and blocks, and
are used in the above formats */
int mu_abort_block::label_count = 0;
int mu_abort_block::block_count = 0;
mu_abort_block::mu_abort_block()
{
char *label_str;
this->flags = 0;
this->block_stmts = cast_new_block(0, 0);
label_str = flick_asprintf(block_format, block_count++);
this->block_label = cast_new_label(label_str, this->block_stmts);
this->curr_label = 0;
this->children.val = 0;
this->children.len = 0;
this->block_expr = 0;
this->reaper_label = 0;
}
mu_abort_block::~mu_abort_block()
{
}
void mu_abort_block::set_kind(int kind)
{
this->flags &= ~MABF_KIND;
this->flags |= kind;
}
int mu_abort_block::get_kind()
{
return( this->flags & MABF_KIND );
}
void mu_abort_block::set_expr(cast_expr expr)
{
this->block_expr = expr;
}
cast_expr mu_abort_block::get_expr()
{
return( this->block_expr );
}
void mu_abort_block::set_reaper_label(cast_stmt the_reaper_label)
{
this->reaper_label = the_reaper_label;
switch( this->flags & MABF_KIND ) {
case MABK_CONTROL:
case MABK_CONTROL_IF:
case MABK_CONTROL_IF_ELSE:
case MABK_CONTROL_SWITCH: {
int lpc;
/* If its a control block then we need to walk through
all the children and set their labels */
for( lpc = 0; lpc < this->children.len; lpc++ ) {
this->children.val[lpc]->
set_reaper_label(the_reaper_label);
}
break;
}
case MABK_THREAD:
/* If its a thread block then we just set the goto's label
to this one and increment its user count. */
this->block_stmts->cast_stmt_u_u.block.stmts.stmts_val[0]->
cast_stmt_u_u.s_label.stmt->cast_stmt_u_u.goto_label =
this->reaper_label->cast_stmt_u_u.s_label.label;
this->reaper_label->cast_stmt_u_u.s_label.users++;
break;
default:
break;
}
}
cast_stmt mu_abort_block::get_reaper_label()
{
return( this->reaper_label );
}
void mu_abort_block::clear_reaper_label()
{
switch( this->flags & MABF_KIND ) {
case MABK_THREAD:
this->block_stmts->cast_stmt_u_u.block.stmts.stmts_val[0]->
cast_stmt_u_u.s_label.stmt->kind = CAST_STMT_EMPTY;
if( this->reaper_label )
this->reaper_label->cast_stmt_u_u.s_label.users--;
break;
default:
break;
}
}
cast_stmt mu_abort_block::get_block_label()
{
return( this->block_label );
}
char *mu_abort_block::use_block_label()
{
this->block_label->cast_stmt_u_u.s_label.users++;
return( this->block_label->cast_stmt_u_u.s_label.label );
}
void mu_abort_block::drop_block_label()
{
this->block_label->cast_stmt_u_u.s_label.users--;
}
void mu_abort_block::add_stmt(cast_stmt stmt)
{
if( stmt ) {
switch( this->flags & MABF_KIND ) {
case MABK_THREAD: {
char *label_str;
/* In a thread block every statement needs to have
a label so it can be reached, so if it doesn't
have one we add one. */
if( stmt->kind == CAST_STMT_LABEL ) {
this->curr_label = stmt;
}
else {
label_str = flick_asprintf(label_format,
label_count++);
this->curr_label = cast_new_label(label_str,
stmt);
}
cast_block_add_stmt(&this->block_stmts->
cast_stmt_u_u.block,
this->curr_label);
break;
}
case MABK_CONTROL:
case MABK_CONTROL_IF:
case MABK_CONTROL_IF_ELSE:
case MABK_CONTROL_SWITCH:
/* Just add the stmt without a label. */
cast_block_add_stmt(&this->block_stmts->
cast_stmt_u_u.block,
stmt);
break;
default:
break;
}
}
}
int mu_abort_block::stmt_count()
{
int retval = 0;
switch( this->flags & MABF_KIND ) {
case MABK_THREAD:
retval = this->block_stmts->cast_stmt_u_u.block.
stmts.stmts_len - 1;
break;
case MABK_CONTROL:
case MABK_CONTROL_IF:
case MABK_CONTROL_IF_ELSE:
case MABK_CONTROL_SWITCH:
retval = this->block_stmts->cast_stmt_u_u.block.
stmts.stmts_len;
break;
default:
break;
}
return( retval );
}
void mu_abort_block::add_child(struct mu_abort_block *mab, int connection)
{
int i;
i = this->children.len++;
this->children.val = (struct mu_abort_block **)
mustrealloc(this->children.val,
this->children.len *
sizeof( struct mu_abort_block * ));
this->children.val[i] = mab;
switch( this->flags & MABF_KIND ) {
case MABK_THREAD:
/* The child is separate from this thread of execution
so we need to make a jump to the block and have
it end by going to the current label. */
mab->set_reaper_label(this->get_current_label());
if( (connection & MABF_OUT_OF_LINE) ) {
if( mab->stmt_count() )
this->add_stmt(cast_new_goto(
mab->use_block_label()));
else
connection = 0;
}
break;
case MABK_CONTROL:
case MABK_CONTROL_IF:
case MABK_CONTROL_IF_ELSE:
case MABK_CONTROL_SWITCH:
/* The flow of control isn't straightforward so we
construct it at a later time. */
if( this->reaper_label )
mab->set_reaper_label(this->reaper_label);
break;
default:
panic( "mu_abort_block::add_child - "
"Can't handle kind %d",
this->flags & MABF_KIND );
break;
}
if( connection & MABF_INLINE ) {
this->add_stmt(mab->get_block_label());
mab->block_stmts->cast_stmt_u_u.block.flags |=
CAST_BLOCK_INLINE;
}
mab->flags |= connection;
}
struct mu_abort_block *mu_abort_block::find_child(char *child_label)
{
struct mu_abort_block *retval = 0;
int lpc;
for( lpc = 0; (lpc < this->children.len) && !retval; lpc++ ) {
if( !strcmp( child_label,
this->children.val[lpc]->get_block_label()->
cast_stmt_u_u.s_label.label ) ) {
retval = this->children.val[lpc];
}
}
return( retval );
}
cast_stmt mu_abort_block::get_current_label()
{
return( this->curr_label );
}
char *mu_abort_block::use_current_label()
{
this->curr_label->cast_stmt_u_u.s_label.users++;
return( this->curr_label->cast_stmt_u_u.s_label.label );
}
void mu_abort_block::grab_label(char *label)
{
int i;
i = cast_find_label( &this->block_stmts->cast_stmt_u_u.block,
label );
this->block_stmts->cast_stmt_u_u.block.stmts.stmts_val[i]->
cast_stmt_u_u.s_label.users++;
}
void mu_abort_block::drop_label(char *label)
{
int i;
i = cast_find_label( &this->block_stmts->cast_stmt_u_u.block,
label );
if( i >= 0 ) {
this->block_stmts->cast_stmt_u_u.block.stmts.stmts_val[i]->
cast_stmt_u_u.s_label.users--;
}
else {
/* The label could be in one of our children so we search
through them as well. */
for( i = 0; i < this->children.len; i++ ) {
this->children.val[i]->drop_label(label);
}
}
}
void mu_abort_block::make_dispatch()
{
switch( this->flags & MABF_KIND ) {
case MABK_CONTROL_IF:
if( this->children.val[0]->stmt_count() ) {
cast_stmt if_stmt, t_stmt;
cast_expr test_expr;
test_expr = this->children.val[0]->get_expr();
t_stmt = cast_new_goto(this->children.val[0]->
use_block_label());
if_stmt = cast_new_if(test_expr, t_stmt, 0);
this->add_stmt(if_stmt);
this->add_stmt(cast_new_goto(""));
}
break;
case MABK_CONTROL_IF_ELSE: {
cast_stmt if_stmt, t_stmt = 0, f_stmt = 0;
cast_expr test_expr;
int zero;
zero = this->children.val[0]->get_expr() == 0;
test_expr = this->children.val[zero]->get_expr();
assert(test_expr);
if( this->children.val[zero]->stmt_count() )
t_stmt = cast_new_goto(this->children.val[zero]->
use_block_label());
if( this->children.val[!zero]->stmt_count() )
f_stmt = cast_new_goto(this->children.val[!zero]->
use_block_label());
if( !t_stmt ) {
test_expr = cast_new_unary_expr(CAST_UNARY_LNOT,
test_expr);
t_stmt = f_stmt;
f_stmt = 0;
}
if( t_stmt || f_stmt ) {
if_stmt = cast_new_if(test_expr, t_stmt, f_stmt);
this->add_stmt(if_stmt);
} else {
this->children.val[0]->flags &= ~MABF_CONNECTION;
this->children.val[1]->flags &= ~MABF_CONNECTION;
free(this->block_stmts->cast_stmt_u_u.block.stmts.
stmts_val);
this->block_stmts->cast_stmt_u_u.block.stmts.
stmts_val = 0;
this->block_stmts->cast_stmt_u_u.block.stmts.
stmts_len = 0;
}
break;
}
case MABK_CONTROL_SWITCH: {
cast_stmt sw_stmt, sw_block, child_stmt;
cast_expr child_value;
cast_block *block;
char *child_label;
int found = 0;
int lpc;
sw_block = cast_new_block(0, 0);
sw_stmt = cast_new_switch(this->block_expr, sw_block);
block = &sw_block->cast_stmt_u_u.block;
for( lpc = 0; lpc < this->children.len; lpc++ ) {
child_value = this->children.val[lpc]->
get_expr();
if( this->children.val[lpc]->stmt_count() ) {
found = 1;
child_label = this->children.val[lpc]->
use_block_label();
child_stmt = cast_new_goto(child_label);
}
else {
this->children.val[lpc]->flags &=
~MABF_CONNECTION;
child_stmt = cast_new_goto("");
}
if( child_value )
cast_block_add_stmt(block,
cast_new_case(child_value,
child_stmt));
else
cast_block_add_stmt(
block,
cast_new_default(child_stmt));
}
if( found )
this->add_stmt(sw_stmt);
break;
}
default:
break;
}
}
void mu_abort_block::begin()
{
switch( this->flags & MABF_KIND ) {
case MABK_THREAD:
/* Set the reverse flag for the block and add a
goto statement that will later point to the reaper label. */
this->block_stmts->cast_stmt_u_u.block.flags |=
CAST_BLOCK_REVERSE;
this->add_stmt(cast_new_goto("XXX"));
break;
default:
break;
}
}
void mu_abort_block::end()
{
switch( this->flags & MABF_KIND ) {
case MABK_CONTROL:
case MABK_CONTROL_IF:
case MABK_CONTROL_IF_ELSE:
case MABK_CONTROL_SWITCH:
/* Construct the control flow needed for an abort
to properly back out of a conditional flow of control. */
this->make_dispatch();
break;
default:
break;
}
}
int mu_abort_block::rollback(int has_path)
{
cast_block *block;
int lpc;
for( lpc = 0; lpc < this->children.len; lpc++ ) {
if( !(this->children.val[lpc]->flags & MABF_CONNECTION) ) {
this->children.val[lpc]->rollback(0);
}
}
switch( this->flags & MABF_KIND ) {
case MABK_CONTROL: {
int child_path = 0;
int lpc;
/* If we don't start with a path, check if our block
is used or not. */
if( has_path == 0 )
has_path = this->block_label->
cast_stmt_u_u.s_label.users != 0;
/* Since there's nothing in the CAST to indicate
path relationships between the children we have
to go through them individually here. */
for( lpc = 0; lpc < this->children.len; lpc++ ) {
if( this->children.val[lpc]->flags &
MABF_INLINE ) {
this->children.val[lpc]->clear_reaper_label();
}
if( this->children.val[lpc]->flags &
MABF_CONNECTION ) {
child_path = this->children.val[lpc]->
rollback(has_path) || child_path;
}
}
has_path = child_path;
break;
}
default:
/* If we don't start with a path, check if our block
is used or not. */
if( has_path == 0 )
has_path = this->block_label->
cast_stmt_u_u.s_label.users != 0;
/* Rollback our root block */
block = &this->block_stmts->cast_stmt_u_u.block;
has_path = this->rollback_block(block, 0,
block->stmts.stmts_len,
has_path);
break;
}
return( has_path );
}
int mu_abort_block::rollback_stmt(cast_block */*block*/, int /*pos*/,
cast_stmt curr_stmt, int has_path)
{
switch( curr_stmt->kind ) {
case CAST_STMT_TEXT:
/* Ignore text statements */
curr_stmt = 0;
break;
case CAST_STMT_LABEL: {
struct mu_abort_block *mab;
/* If the label has users then we have a path, else
we need to rollback the statement. */
if( curr_stmt->cast_stmt_u_u.s_label.users )
has_path = 1;
/* See if the label is from an inlined child,
if so rollback the child and check if it
has a path, else just rollback the statement. */
if( (mab = this->find_child(curr_stmt->cast_stmt_u_u.
s_label.label)) ) {
mab->clear_reaper_label();
has_path = mab->rollback(has_path);
} else {
has_path = this->
rollback_stmt(0, 0,
curr_stmt->cast_stmt_u_u.
s_label.stmt,
has_path);
if( curr_stmt->cast_stmt_u_u.s_label.stmt->kind ==
CAST_STMT_RETURN )
curr_stmt = 0;
}
break;
}
case CAST_STMT_GOTO: {
struct mu_abort_block *mab;
if( curr_stmt->cast_stmt_u_u.goto_label[0] == 0 ) {
curr_stmt->cast_stmt_u_u.goto_label = this->
reaper_label->cast_stmt_u_u.s_label.label;
this->reaper_label->cast_stmt_u_u.s_label.users++;
}
if( has_path ) {
if( (mab = this->find_child(curr_stmt->cast_stmt_u_u.
goto_label)) ) {
if( (mab->flags & MABF_CONTROL) &&
mab->stmt_count() )
mab->rollback(1);
else if( mab->stmt_count() == 0 ) {
curr_stmt->kind = CAST_STMT_EMPTY;
mab->drop_block_label();
mab->rollback(0);
} else {
mab->rollback(has_path);
}
}
}
else {
if( (mab = this->find_child(curr_stmt->cast_stmt_u_u.
goto_label)) ) {
/* If the goto jumps to a child then rollback
the child. */
mab->drop_block_label();
mab->rollback(has_path);
}
else if( !strcmp(curr_stmt->cast_stmt_u_u.goto_label,
this->reaper_label->
cast_stmt_u_u.s_label.label) ) {
/* If its a jump to the reaper label then
we need to decrement its user count.
This is sort of a notable case since
the label doesn't exist in our block
or any of our children unlike the
previous cases. */
this->reaper_label->cast_stmt_u_u.
s_label.users--;
}
}
break;
}
case CAST_STMT_BLOCK: {
cast_block *nest_block;
/* Just rollback the block like any other */
nest_block = &curr_stmt->cast_stmt_u_u.block;
has_path = this->rollback_block(nest_block, 0,
nest_block->
stmts.stmts_len,
has_path);
break;
}
case CAST_STMT_SWITCH:
this->rollback_stmt(0, 0,
curr_stmt->
cast_stmt_u_u.
s_switch.stmt,
has_path);
break;
case CAST_STMT_CASE:
this->rollback_stmt(0, 0,
curr_stmt->
cast_stmt_u_u.s_case.
stmt, has_path);
break;
case CAST_STMT_DEFAULT:
this->rollback_stmt(0, 0,
curr_stmt->
cast_stmt_u_u.
default_stmt, has_path);
break;
case CAST_STMT_IF:
this->rollback_stmt(0, 0,
curr_stmt->cast_stmt_u_u.s_if.
true_stmt, has_path);
if( curr_stmt->cast_stmt_u_u.s_if.false_stmt )
this->rollback_stmt(0, 0,
curr_stmt->
cast_stmt_u_u.s_if.
false_stmt, has_path);
break;
case CAST_STMT_RETURN:
has_path = 0;
curr_stmt = 0;
break;
default:
break;
}
if( !has_path && curr_stmt )
curr_stmt->kind = CAST_STMT_EMPTY;
return( has_path );
}
int mu_abort_block::rollback_block(cast_block *block, int start,
int len, int has_path)
{
cast_stmt curr_stmt;
int lpc;
if( block->flags & CAST_BLOCK_REVERSE ) {
/* We need to walk through the block in reverse otherwise
the path information will be incorrect */
for( lpc = (start + len) - 1; lpc >= 0; lpc-- ) {
curr_stmt = block->stmts.stmts_val[lpc];
has_path = this->rollback_stmt(block, lpc,
curr_stmt, has_path);
}
}
else {
for( lpc = start; lpc < len; lpc++ ) {
curr_stmt = block->stmts.stmts_val[lpc];
has_path = this->rollback_stmt(block, lpc,
curr_stmt, has_path);
}
}
return( has_path );
}
/* End of file. */
syntax highlighted by Code2HTML, v. 0.9.1