/*
 * Copyright (c) 1998, 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.
 */

#include <assert.h>
#include <limits.h>

#include <mom/compiler.h>
#include <mom/c/libcast.h>
#include <mom/c/pbe.hh>

#define   min(a,b)        (a < b ? a : b)

cast_expr mu_msg_span::macro_name = 0;

void mu_msg_span::set_be_name(const char *be_name)
{
	macro_name = cast_new_expr_name(flick_asprintf("flick_%s_check_span",
						       be_name));
}

mu_msg_span::mu_msg_span()
{
	this->flags = 0;
	this->kind = MSK_NONE;
	this->parent = 0;
	this->size = 0;
	this->size_expr = 0;
	this->check_stmt =
		cast_new_stmt_expr(cast_new_expr_call_2(macro_name,0,0));
	this->block = 0;
	this->stmt_pos = -1;
	this->a_block = 0;
	this->children.val = 0;
	this->children.len = 0;
	this->children.min_size = UINT_MAX;
}

mu_msg_span::~mu_msg_span()
{
}

void mu_msg_span::set_flags(int the_flags)
{
	this->flags = the_flags;
}

int mu_msg_span::get_flags()
{
	return( this->flags );
}

void mu_msg_span::set_kind(int the_kind)
{
	this->kind = the_kind;
}

int mu_msg_span::get_kind()
{
	return( this->kind );
}

void mu_msg_span::set_block(cast_stmt the_block)
{
	this->block = the_block;
}

cast_stmt mu_msg_span::get_block()
{
	return( this->block );
}

void mu_msg_span::set_abort(struct mu_abort_block *mab)
{
	this->a_block = mab;
}

struct mu_abort_block *mu_msg_span::get_abort()
{
	return( this->a_block );
}

void mu_msg_span::grow(int increment)
{
	this->size += increment;
}

void mu_msg_span::shrink(int decrement)
{
	this->size -= decrement;
}

void mu_msg_span::set_size(cast_expr the_size)
{
	this->size_expr = the_size;
}

void mu_msg_span::mod_size(cast_binary_op op, struct mu_msg_span *mms)
{
	if( mms->size_expr ) {
		if( this->size_expr )
			this->size_expr = cast_new_binary_expr(op,
							       this->size_expr,
							       mms->size_expr);
		else
			this->size_expr = mms->size_expr;
	} else if( mms->size && this->size_expr ) {
		this->size_expr = cast_new_binary_expr(
			op,
			this->size_expr,
			cast_new_expr_lit_int(mms->size, 0));
		mms->size = 0;
	}
	this->size += mms->size;
}

void mu_msg_span::absorb_span(cast_binary_op op, struct mu_msg_span *mms)
{
	int absorbed = 0;
	int lpc;
	
	switch( this->kind ) {
	case MSK_UNION: {
		struct mu_msg_span mms_cp;
		
		for( lpc = 0; lpc < this->children.len; lpc++ ) {
			mms_cp = *mms;
			this->children.val[lpc]->absorb_span(op, &mms_cp);
			absorbed = 1;
		}
		break;
	}
	case MSK_ARRAY:
		this->children.val[0]->absorb_span(op, mms);
		absorbed = 1;
		break;
	case MSK_SEQUENTIAL:
		if( this->children.len ) {
			this->children.val[this->children.len - 1]->
				absorb_span(op, mms);
			absorbed = 1;
		}
		break;
	default:
		break;
	}
	if( !absorbed ) {
		this->mod_size(op, mms);
	}
}

void mu_msg_span::drop()
{
	if( (this->check_stmt->kind != CAST_STMT_EMPTY) ) {
		this->check_stmt->kind = CAST_STMT_EMPTY;
		if( this->a_block && (this->stmt_pos != -1) ) {
			this->a_block->
				drop_label(this->check_stmt->cast_stmt_u_u.
					   expr->cast_expr_u_u.call.params.
					   cast_expr_array_val[1]->
					   cast_expr_u_u.name.
					   cast_scoped_name_val[0].name);
		}
	}
}

void mu_msg_span::add_child(struct mu_msg_span *mms)
{
	int i;
	
	switch( this->kind ) {
	case MSK_ARRAY: {
		/*
		 * If the sequential that makes up the element is completely
		 * constant then we can just move the checks out of the loop.
		 */
		mms->collapse();
		if( (mms->kind == MSK_NONE) &&
		    !(mms->flags & MSF_ALIGN) &&
		    ((mms->size_expr && cast_expr_const(mms->size_expr)) ||
		     !mms->size_expr) && mms->size) {
			this->mod_size(CAST_BINARY_MUL, mms);
			this->kind = MSK_NONE;
			mms->drop();
		} else {
			this->size_expr = 0;
			this->drop();
		}
		break;
	}
	default:
		break;
	}
	i = this->children.len++;
	this->children.val = (struct mu_msg_span **)
		mustrealloc(this->children.val,
			    this->children.len *
			    sizeof(struct mu_msg_span *));
	this->children.val[i] = mms;
	mms->parent = this;
}

void mu_msg_span::rem_child(struct mu_msg_span *mms)
{
	int found = 0, lpc;
	
	for( lpc = 0; (lpc < this->children.len) && !found; lpc++ ) {
		if( mms == this->children.val[lpc] )
			found = 1;
	}
	for( ; lpc < this->children.len; lpc++ ) {
		this->children.val[lpc - 1] = this->children.val[lpc];
	}
	this->children.len--;
}

void mu_msg_span::begin()
{
	if( this->kind == MSK_SEQUENTIAL )
		return;
	assert(this->block);
	/* Add the check */
	this->check_stmt->cast_stmt_u_u.expr->cast_expr_u_u.call.
		params.cast_expr_array_val[1] =
		cast_new_expr_name(a_block->use_current_label());
	cast_block_add_stmt(&this->block->cast_stmt_u_u.block,
			    this->check_stmt);
	this->stmt_pos = this->block->cast_stmt_u_u.block.stmts.
		stmts_len - 1;
}

void mu_msg_span::end()
{
	/* Resolve two sizes into an expression if needed */
	if( this->size_expr && this->size ) {
		this->size_expr = cast_new_binary_expr(
			CAST_BINARY_ADD,
			this->size_expr,
			cast_new_expr_lit_int(this->size, 0));
		this->size = 0;
	}
}

void mu_msg_span::collapse()
{
	switch( this->kind ) {
	case MSK_UNION: {
		int lpc;
		
		/*
		 * Figure out which of our children we can drop and then
		 * grow ourself to compensate.  We can only drop those with
		 * the same size as the min since the others still have to
		 * check the rest of the span.
		 */
		for( lpc = 0; lpc < this->children.len; lpc++ ) {
			this->children.val[lpc]->collapse();
			if( this->children.val[lpc]->size_expr )
				this->children.min_size = 0;
			else
				this->children.min_size =
					min(this->children.min_size,
					    this->children.val[lpc]->size);
		}
		for( lpc = 0; lpc < this->children.len; lpc++ ) {
			if( !this->children.val[lpc]->size_expr &&
			    (this->children.min_size ==
			     this->children.val[lpc]->size) ) {
				this->children.val[lpc]->drop();
			}
		}
		if( this->children.len == 1 ) {
			struct mu_msg_span *child;
			
			child = this->children.val[0];
			if( !(child->flags & MSF_ALIGN) ) {
				this->size = this->children.val[0]->size;
				this->size_expr =
					this->children.val[0]->size_expr;
				this->children.val[0]->drop();
			}
		} else if( this->children.len )
			this->grow(this->children.min_size);
		else
			this->drop();
		break;
	}
	case MSK_SEQUENTIAL: {
		struct mu_msg_span *child_pred, *child;
		int lpc;
		
		/*
		 * Walk backwards through the list of children and try to
		 * combine them.  We can only do this if the values are
		 * constant though, since otherwise they might be variables
		 * which haven't been initialized yet.
		 */
		for( lpc = this->children.len - 1; lpc > 0; lpc-- ) {
			child = this->children.val[lpc];
			child_pred = this->children.val[lpc - 1];
			child->collapse();
			if( !(child->flags & MSF_ALIGN) &&
			    (child_pred->check_stmt->kind !=
			     CAST_STMT_EMPTY) &&
			    ((child->size_expr &&
			      cast_expr_const(child->size_expr)) ||
			     !child->size_expr) ) {
				child_pred->
					absorb_span(CAST_BINARY_ADD,
						    child);
				child->drop();
			}
		}
		if( this->children.len == 1 ) {
			if( !(this->children.val[0]->flags & MSF_ALIGN) )
				*this = *this->children.val[0];
		} else if( this->children.len ) {
			struct mu_msg_span *child;
			
			child = this->children.val[0];
			if( !(child->flags & MSF_ALIGN) &&
			    ((child->size_expr &&
			      cast_expr_const(child->size_expr)) ||
			     !child->size_expr) ) {
				this->size = child->size;
				this->size_expr = child->size_expr;
				this->block = child->block;
				this->check_stmt = child->check_stmt;
				this->stmt_pos = child->stmt_pos;
				this->a_block = child->a_block;
			}
		} else {
			this->kind = MSK_NONE;
			this->size = 0;
			this->size_expr = 0;
		}
		break;
	}
	default:
		break;
	}
}

void mu_msg_span::commit()
{
	int lpc;
	
	for( lpc = 0; lpc < this->children.len; lpc++ ) {
		this->children.val[lpc]->commit();
	}
	if( this->size ) {
		cast_expr expr = cast_new_expr_lit_int(this->size, 0);
		
		if( this->size_expr )
			this->size_expr = cast_new_binary_expr(CAST_BINARY_ADD,
							       this->size_expr,
							       expr);
		else
			this->size_expr = expr;
	}
	if( this->size_expr )
		this->check_stmt->cast_stmt_u_u.expr->cast_expr_u_u.call.
			params.cast_expr_array_val[0] = this->size_expr;
	else
		this->drop();
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1