/*
 * jit-live.c - Liveness analysis for function bodies.
 *
 * Copyright (C) 2004  Southern Storm Software, Pty Ltd.
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "jit-internal.h"

/*
 * Compute liveness information for a basic block.
 */
static void compute_liveness_for_block(jit_block_t block)
{
	jit_insn_iter_t iter;
	jit_insn_t insn;
	jit_value_t dest;
	jit_value_t value1;
	jit_value_t value2;
	int flags;

	/* Scan backwards to compute the liveness flags */
	jit_insn_iter_init_last(&iter, block);
	while((insn = jit_insn_iter_previous(&iter)) != 0)
	{
		/* Skip NOP instructions, which may have arguments left
		   over from when the instruction was replaced, but which
		   are not relevant to our liveness analysis */
		if(insn->opcode == JIT_OP_NOP)
		{
			continue;
		}

		/* Fetch the value parameters to this instruction */
		flags = insn->flags;
		if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
		{
			dest = insn->dest;
			if(dest && dest->is_constant)
			{
				dest = 0;
			}
		}
		else
		{
			dest = 0;
		}
		if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
		{
			value1 = insn->value1;
			if(value1 && value1->is_constant)
			{
				value1 = 0;
			}
		}
		else
		{
			value1 = 0;
		}
		if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
		{
			value2 = insn->value2;
			if(value2 && value2->is_constant)
			{
				value2 = 0;
			}
		}
		else
		{
			value2 = 0;
		}

		/* Record the liveness information in the instruction flags */
		flags &= ~JIT_INSN_LIVENESS_FLAGS;
		if(dest)
		{
			if(dest->live)
			{
				flags |= JIT_INSN_DEST_LIVE;
			}
			if(dest->next_use)
			{
				flags |= JIT_INSN_DEST_NEXT_USE;
			}
		}
		if(value1)
		{
			if(value1->live)
			{
				flags |= JIT_INSN_VALUE1_LIVE;
			}
			if(value1->next_use)
			{
				flags |= JIT_INSN_VALUE1_NEXT_USE;
			}
		}
		if(value2)
		{
			if(value2->live)
			{
				flags |= JIT_INSN_VALUE2_LIVE;
			}
			if(value2->next_use)
			{
				flags |= JIT_INSN_VALUE2_NEXT_USE;
			}
		}
		insn->flags = (short)flags;

		/* Set the destination to "not live, no next use" */
		if(dest)
		{
			if((flags & JIT_INSN_DEST_IS_VALUE) == 0)
			{
				if(!(dest->next_use) && !(dest->live))
				{
					/* There is no next use of this value and it is not
					   live on exit from the block.  So we can discard
					   the entire instruction as it will have no effect */
					insn->opcode = (short)JIT_OP_NOP;
					continue;
				}
				dest->live = 0;
				dest->next_use = 0;
			}
			else
			{
				/* The destination is actually a source value for this
				   instruction (e.g. JIT_OP_STORE_RELATIVE_*) */
				dest->live = 1;
				dest->next_use = 1;
			}
		}

		/* Set value1 and value2 to "live, next use" */
		if(value1)
		{
			value1->live = 1;
			value1->next_use = 1;
		}
		if(value2)
		{
			value2->live = 1;
			value2->next_use = 1;
		}
	}

	/* Re-scan the block to reset the liveness flags on all non-temporaries
	   because we need them in the original state for the next block */
	jit_insn_iter_init_last(&iter, block);
	while((insn = jit_insn_iter_previous(&iter)) != 0)
	{
		flags = insn->flags;
		if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
		{
			dest = insn->dest;
			if(dest && !(dest->is_constant) && !(dest->is_temporary))
			{
				dest->live = 1;
				dest->next_use = 0;
			}
		}
		if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
		{
			value1 = insn->value1;
			if(value1 && !(value1->is_constant) && !(value1->is_temporary))
			{
				value1->live = 1;
				value1->next_use = 0;
			}
		}
		if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
		{
			value2 = insn->value2;
			if(value2 && !(value2->is_constant) && !(value2->is_temporary))
			{
				value2->live = 1;
				value2->next_use = 0;
			}
		}
	}
}

void _jit_function_compute_liveness(jit_function_t func)
{
	jit_block_t block = func->builder->first_block;
	while(block != 0)
	{
		/* If the block is never entered, then replace it with empty */
		if(!(block->entered_via_top) && !(block->entered_via_branch))
		{
			block->last_insn = block->first_insn - 1;
		}

		/* Perform peephole optimization on branches to branches */
		_jit_block_peephole_branch(block);

		/* Compute the liveness flags for the block */
		compute_liveness_for_block(block);

		/* Move on to the next block in the function */
		block = block->next;
	}
}
