/* src/vm/jit/optimizing/ssa.c - static single-assignment form

   Copyright (C) 2005 - 2007 R. Grafl, A. Krall, C. Kruegel, C. Oates,
   R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
   C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
   Institut f. Computersprachen - TU Wien

   This file is part of CACAO.

   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, 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.

   Contact: cacao@complang.tuwien.ac.at

   Authors: Christian Ullrich

   $Id: $

*/

#include <stdio.h>
#include <stdlib.h>

#include "mm/memory.h"

#include "toolbox/bitvector.h"
#include "toolbox/worklist.h"

#include "vm/builtin.h"

#include "vm/jit/jit.h" /* icmd_table */

#include "vm/jit/optimizing/dominators.h"
#include "vm/jit/optimizing/graph.h"
#include "vm/jit/optimizing/lifetimes.h"
#include "vm/jit/optimizing/lsra.h"
#include "vm/jit/optimizing/ssa.h"
#include "vm/jit/optimizing/ssa_phi.h"

#if defined(SSA_DEBUG_VERBOSE)
#include "vmcore/options.h"   /* compileverbose */
#endif

/* ssa_place_phi_functions *****************************************************

Algorithm is based on "modern compiler implementation in C" from andrew 
w. appel, edition 2004

Corrections:
page 441 Algorithm 19.6. Inserting phi-functions:

...
    for each Y in DF[n]
-       if Y not element of A phi [n]
+       if a not element of A phi [n]
            insert the statment a <- ...
...
...
-       A phi [n] <- A phi [n] join {Y}
+       A phi [n] <- A phi [n] join {a}
-      if Y not element of A orig [n]
+      if a not element of A orig [Y]
           W <- W join {Y}


ls->phi[n][a][p] is created and populated.

n in [0..ls->basicblockcount[
a in [0..ls->ssavarcount[
p in [1..Count of Predecessors of Basicblock n]

For each basicblock Y in the dominance frontier of a basicblock n (0 <= n < 
ls->basicblockcount) in which a variable (0 <= a < ls->ssavarcount) is defined 
an entry in ls->phi[Y][a] is created.
This entry is an array with the number of predecessors of Y elements + 1
elements.
This elements are all set to the variable a and represent the phi function which
will get ai = phi(ai1, ai2, ..., aip) after ssa_rename.


ls->phi[n][a] == NULL, if no phi function for Variable a for Basicblock n exists

or

ls->phi[n][a][0] == a, representing the result of the phi function and
ls->phi[n][a][p] == a, representing the arguments of the phi function.

*******************************************************************************/


void ssa_place_phi_functions(jitdata *jd, graphdata *gd, dominatordata *dd)
{
	int a,i,j,n,Y;
	bitvector *def_sites;
	bitvector *A_phi;    /* [0..ls->basicblockcount[ of */
	                     /* ls->ssavarcount Bit         */
	worklist *W;
	int num_pred;

	lsradata *ls;

	ls = jd->ls;

	W = wl_new(ls->basicblockcount);

	def_sites = DMNEW(bitvector, ls->ssavarcount);
	for(a = 0; a < ls->ssavarcount; a++)
		def_sites[a] = bv_new(ls->basicblockcount);

	ls->phi = DMNEW(int **, ls->basicblockcount);
	A_phi = DMNEW(bitvector, ls->basicblockcount);
	for(i = 0; i < ls->basicblockcount; i++) {
		ls->phi[i] = DMNEW(int *, ls->ssavarcount);
		for(j = 0; j < ls->ssavarcount; j++)
			ls->phi[i][j] = NULL;
		A_phi[i] = bv_new(ls->ssavarcount);
	}

	/* copy var_def to def_sites */
	/* var_def is valid for 0.. jd->basicblockcount (bb 0 for param init) */

	for(n = 0; n <= jd->basicblockcount; n++)
		for(a = 0; a < ls->ssavarcount; a++)
			if (bv_get_bit(ls->var_def[n], a))
				bv_set_bit(def_sites[a], n);
#ifdef SSA_DEBUG_VERBOSE
	if (compileverbose) {
		printf("var Definitions:\n");
		for(i = 0; i < ls->ssavarcount; i++) {
			printf("def_sites[%3i]=%p:",i,(void *)def_sites[i]);
			for(j = 0; j < ls->basicblockcount; j++) {
				if ((j % 5) == 0) printf(" ");
				if (bv_get_bit(def_sites[i], j))
					printf("1");
				else
					printf("0");
			}
			printf(" (");

			printf("\n");
		}
	}
#endif

	for(a = 0; a < ls->ssavarcount; a++) {

		/* W<-def_sites(a) */

		for(n = 0; n < ls->basicblockcount; n++)
			if (bv_get_bit(def_sites[a],n)) {
				wl_add(W, n);
			}
				
		while (!wl_is_empty(W)) { /* W not empty */
			n = wl_get(W);

			for(i = 0; i < dd->num_DF[n]; i++) {
				Y = dd->DF[n][i];

				/* Node Y is in Dominance Frontier of n -> */
				/* insert phi function for a at top of Y*/

				_SSA_CHECK_BOUNDS(Y, 0, ls->basicblockcount);
				if (bv_get_bit( A_phi[Y], a) == 0) { 

					/* a is not a Element of A_phi[Y] */
					/* a <- phi(a,a...,a) to be inserted at top of Block Y */
					/* phi has as many arguments, as Y has predecessors    */

					num_pred =  graph_get_num_predecessor(gd, Y);
					ls->phi[Y][a] = DMNEW(int, num_pred + 1);
					for (j = 0; j < num_pred + 1; j++)
						ls->phi[Y][a][j] = a;

					/* increment the number of definitions of a by one */
					/* for this phi function */

					ls->num_defs[a]++;
					
					bv_set_bit(A_phi[Y], a);
					if (bv_get_bit(ls->var_def[Y],a)==0) {

						/* Iterated Dominance Frontier Criterion:  */
						/* if Y had no definition for a, insert it */
						/* into the Worklist, since now it         */
						/* defines a through the phi function      */

						wl_add(W, Y);
					}
				}
			}
		}
	}
}

void ssa_generate_phi_moves(jitdata *jd, graphdata *gd) {
	int a, i, j, pred;
	graphiterator iter;
	lsradata *ls;

	ls = jd->ls;

	/* count moves to be inserted at the end of each block in moves[] */

	ls->num_phi_moves = DMNEW(int, ls->basicblockcount);
	for(i = 0; i < ls->basicblockcount; i++)
		ls->num_phi_moves[i] = 0;
	for(i = 0; i < ls->basicblockcount; i++)
		for(a = 0; a < ls->ssavarcount; a++)
			if (ls->phi[i][a] != NULL) {
#if 0
				if (ls->lifetime[ls->phi[i][a][0]].use == NULL) {
					/* Var defined (only <- SSA Form!) in this phi function */
					/* and not used anywhere -> delete phi function and set */
					/* var to unused */

					/* TODO: first delete use sites of arguments of phi */
					/* function */
					VAR(ls->lifetime[ls->phi[i][a][0]].v_index)->type = UNUSED;
					ls->lifetime[ls->phi[i][a][0]].def = NULL;
					ls->phi[i][a] = NULL;
				}
				else 
#endif
					{
					pred = graph_get_first_predecessor(gd, i, &iter);
					for(; pred != -1; pred = graph_get_next(&iter)) {
						ls->num_phi_moves[pred]++;
					}
				}
			}

	/* allocate ls->phi_moves */

	ls->phi_moves = DMNEW( int **, ls->basicblockcount);
	for(i = 0; i < ls->basicblockcount; i++) {
		ls->phi_moves[i] = DMNEW( int *, ls->num_phi_moves[i]);
		for(j = 0; j <ls->num_phi_moves[i]; j++)
			ls->phi_moves[i][j] = DMNEW(int, 2);
#ifdef SSA_DEBUG_VERBOSE
		if (compileverbose)
			printf("ssa_generate_phi_moves: ls_num_phi_moves[%3i] = %3i\n",
				   i, ls->num_phi_moves[i]);
#endif
	}

	/* populate ls->phi_moves */

	for(i = 0; i < ls->basicblockcount; i++)
		ls->num_phi_moves[i] = 0;
	for(i = 0; i < ls->basicblockcount; i++)
		for(a = 0; a < ls->ssavarcount; a++)
			if (ls->phi[i][a] != NULL) {
				pred = graph_get_first_predecessor(gd, i, &iter);
				for(j = 0; pred != -1; j++, pred = graph_get_next(&iter)) {
					/* target is phi[i][a][0] */
					/* source is phi[i][a][j+1] */
					if (ls->phi[i][a][j+1] != ls->varcount_with_indices) {
						/* valid move */
						if (ls->phi[i][a][0] != ls->phi[i][a][j+1]) {
							ls->phi_moves[pred][ls->num_phi_moves[pred]][0] =
								ls->phi[i][a][0];
							ls->phi_moves[pred][(ls->num_phi_moves[pred])++][1]=
								ls->phi[i][a][j+1];
						}
					}
				}
			}
}

#ifdef SSA_DEBUG_VERBOSE
void ssa_print_phi(lsradata *ls, graphdata *gd) {
	int i,j,k;

	printf("phi Functions (varcount_with_indices: %3i):\n", 
		   ls->varcount_with_indices);
	for(i = 0; i < ls->basicblockcount; i++) {
		for(j = 0; j < ls->ssavarcount; j++) {
			if (ls->phi[i][j] != NULL) {
				printf("BB %3i %3i = phi(", i, ls->phi[i][j][0]);
				for(k = 1; k <= graph_get_num_predecessor(gd, i); k++)
					printf("%3i ",ls->phi[i][j][k]);
				printf(")\n");
			}
		}
	}

}
#endif

/*
 * These are local overrides for various environment variables in Emacs.
 * Please do not remove this and leave it at the end of the file, where
 * Emacs will automagically detect them.
 * ---------------------------------------------------------------------
 * Local variables:
 * mode: c
 * indent-tabs-mode: t
 * c-basic-offset: 4
 * tab-width: 4
 * End:
 */
