/* $Id$ */
/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/*  S T R E N G T H   R E D U C T I O N
 *
 *  S R _ C A N D . C
 */


#include <em_mnem.h>
#include <em_pseu.h>
#include "../share/types.h"
#include "../share/lset.h"
#include "../share/cset.h"
#include "../share/debug.h"
#include "../share/global.h"
#include "../share/map.h"
#include "../share/utils.h"
#include "sr.h"
#include "sr_aux.h"
#include "sr_cand.h"


/* A candidate induction variable of a loop (hereafter called candidate) is a
 * local variable (of the current procedure) that is assigned a value
 * precisely once within the loop. Furthermore, this assignment must
 * take place in a firm block of the loop.
 * We determine those locals that are assigned precisely once, within
 * a firm block;
 *
 * We represent a local variable via an instruction that references it,
 * e.g. LOL -6 represents the local variable at offset -6 with size=wordsize.
 * We keep track of two sets:
 *  cand     - the set of all candidate variables
 *  dismiss  - a set of variables that may not be made a candidate
 *		(because they are assigned more than once, or because
 *		 they are assigned outside a firm block).
 * Only local variables for which a register message is given are considered.
 */


STATIC lset cand,		/* set of candidates */
	    dism;		/* set of dismissed variables */


#define ALL_LINES(lnp,list)	lnp = list; lnp != (line_p) 0; lnp = lnp->l_next



STATIC void un_cand(lnp)
	line_p lnp;
{
	/* remove the variable stored into by lnp from the list of
	 * candidates (if it was there anyway).
	 */

	Lindex i, next;

	for (i = Lfirst(cand); i != (Lindex) 0; i = next) {
		next = Lnext(i,cand);
		if (same_local(lnp,Lelem(i))) {
			OUTTRACE("remove candidate",0);
			Lremove(Lelem(i), &cand);
		}
	}
}


STATIC bool is_cand(lnp)
	line_p lnp;
{
	/* see if the variable stored into by lnp is a candate */

	Lindex i;

	for (i = Lfirst(cand); i != (Lindex) 0; i = Lnext(i,cand)) {
		if (same_local(lnp,Lelem(i))) {
			return TRUE;
		}
	}
	return FALSE;
}


STATIC void make_cand(lnp)
	line_p lnp;
{
	/* make the variable stored into by lnp a candidate */


	OUTTRACE("add a new candidate",0);
	Ladd(lnp,&cand);
}



STATIC void do_dismiss(lnp)
	line_p lnp;
{
	Ladd(lnp,&dism);
}


STATIC void dismiss(lnp)
	line_p lnp;
{
	/* The variable referenced by lnp is turned definitely into
	 * a non-candidate.
	 */

	un_cand(lnp);	/* remove it from the candidate set,
			 * if it was there in the first place.
			 */
	do_dismiss(lnp); /* add it to the set of dismissed variables */
}


STATIC bool not_dismissed(lnp)
	line_p lnp;
{
	Lindex i;

	for (i = Lfirst(dism); i != (Lindex) 0; i = Lnext(i,dism)) {
		if (same_local(Lelem(i),lnp)) {
			return FALSE; /* variable was dismissed */
		}
	}
	return TRUE;
}


STATIC void try_cand(lnp,b)
	line_p lnp;
	bblock_p b;
{
	/* If the variable stored into by lnp was not already a candidate
	 * and was not dismissed, then it is made a candidate
	 * (unless the assignment takes places in a block that is not firm).
	 */

	if (!is_regvar(off_set(lnp))) return;
	if (is_cand(lnp) || !IS_FIRM(b)) {
		dismiss(lnp);
	} else {
		if (not_dismissed(lnp)) {
			make_cand(lnp);
		}
	}
}


void candidates(lp,cand_out,vars_out)
	loop_p lp;
	lset   *cand_out, *vars_out;
{
	/* Find the candidate induction variables.
	 */

	bblock_p b;
	line_p lnp;
	Lindex i;

	OUTTRACE("find candidates of loop %d",lp->lp_id);
	cand = Lempty_set();
	dism = Lempty_set();

	for (i = Lfirst(lp->LP_BLOCKS); i != (Lindex) 0;
					  i = Lnext(i,lp->LP_BLOCKS)) {
		b = (bblock_p) Lelem(i);
		for ( ALL_LINES(lnp, b->b_start)) {
			OUTTRACE("inspect instruction %d",INSTR(lnp));
			switch(INSTR(lnp)) {
				case op_stl:
				case op_inl:
				case op_del:
					OUTTRACE("it's a store local",0);
					try_cand(lnp,b);
					break;
				case op_zrl:
					OUTTRACE("it's a destroy local",0);
					if (is_regvar(off_set(lnp))) {
						dismiss(lnp);
					}
					break;
			}
		}
	}
	*cand_out = cand;
	*vars_out = dism;
}
