/*
 * UnSSA - translate the SSA back to normal form.
 *
 * For now it's done by replacing to set of copies:
 * 1) For each phi-node, replace all their phisrc by copies to a common
 *    temporary.
 * 2) Replace all the phi-nodes by copies of the temporaries to the phi-node target.
 *    This is node to preserve the semantic of the phi-node (they should all "execute"
 *    simultaneously on entry in the basic block in which they belong).
 *
 * This is similar to the "Sreedhar method I" except that the copies to the
 * temporaries are not placed at the end of the predecessor basic blocks, but
 * at the place where the phi-node operands are defined (the same place where the
 * phisrc were present).
 * Is this a problem? 
 *
 * While very simple this method create a lot more copies that really necessary.
 * Ideally, "Sreedhar method III" should be used:
 * "Translating Out of Static Single Assignment Form", V. C. Sreedhar, R. D.-C. Ju,
 * D. M. Gillies and V. Santhanam.  SAS'99, Vol. 1694 of Lecture Notes in Computer
 * Science, Springer-Verlag, pp. 194-210, 1999.
 *
 * Copyright (C) 2005 Luc Van Oostenryck
 */

#include "lib.h"
#include "linearize.h"
#include "allocate.h"
#include "flow.h"
#include <assert.h>


static void remove_phisrc_defines(SCTX_ struct instruction *phisrc)
{
	struct instruction *phi;
	struct basic_block *bb = phisrc->bb;

	FOR_EACH_PTR(phisrc->phi_users, phi) {
		remove_pseudo(sctx_ &bb->defines, phi->target);
	} END_FOR_EACH_PTR(phi);
}

static void replace_phi_node(SCTX_ struct instruction *phi)
{
	pseudo_t tmp;

	tmp = alloc_pseudo(sctx_ NULL);
	tmp->type = phi->target->type;
	tmp->ident = phi->target->ident;
	tmp->def = NULL;		// defined by all the phisrc
	
	// update the current liveness
	remove_pseudo(sctx_ &phi->bb->needs, phi->target);
	add_pseudo(sctx_ &phi->bb->needs, tmp);
	track_phi_uses(sctx_ phi);

	phi->opcode = OP_COPY;
	use_pseudo(sctx_ phi, tmp, &phi->src);

	// FIXME: free phi->phi_list;
}

static void rewrite_phi_bb(SCTX_ struct basic_block *bb)
{
	struct instruction *insn;

	// Replace all the phi-nodes by copies of a temporary
	// (which represent the set of all the %phi that feed them).
	// The target pseudo doesn't change.
	FOR_EACH_PTR(bb->insns, insn) {
		if (!insn->bb)
			continue;
		if (insn->opcode != OP_PHI)
			continue;
		replace_phi_node(sctx_ insn);
	} END_FOR_EACH_PTR(insn);
}

static void rewrite_phisrc_bb(SCTX_ struct basic_block *bb)
{
	struct instruction *insn;

	// Replace all the phisrc by one or several copies to
	// the temporaries associated to each phi-node it defines.
	FOR_EACH_PTR_REVERSE(bb->insns, insn) {
		struct instruction *phi;
		int i;

		if (!insn->bb)
			continue;
		if (insn->opcode != OP_PHISOURCE)
			continue;

		i = 0;
		FOR_EACH_PTR(insn->phi_users, phi) {
			pseudo_t tmp = phi->src;
			pseudo_t src = insn->phi_src;

			if (i == 0) {	// first phi: we overwrite the phisrc
				insn->opcode = OP_COPY;
				insn->target = tmp;
				insn->src = src;
			} else {
				struct instruction *copy = __alloc_instruction(sctx_ 0);

				copy->bb = bb;
				copy->opcode = OP_COPY;
				copy->size = insn->size;
				copy->pos = insn->pos;
				copy->target = tmp;
				copy->src = src;

				INSERT_CURRENT(copy, insn);
			}
			// update the liveness info
			remove_phisrc_defines(sctx_ insn);
			// FIXME: should really something like add_pseudo_exclusive()
			add_pseudo(sctx_ &bb->defines, tmp);

			i++;
		} END_FOR_EACH_PTR(phi);

	} END_FOR_EACH_PTR_REVERSE(insn);
}

int unssa(SCTX_ struct entrypoint *ep)
{
	struct basic_block *bb;

	FOR_EACH_PTR(ep->bbs, bb) {
		rewrite_phi_bb(sctx_ bb);
	} END_FOR_EACH_PTR(bb);

	FOR_EACH_PTR(ep->bbs, bb) {
		rewrite_phisrc_bb(sctx_ bb);
	} END_FOR_EACH_PTR(bb);

	return 0;
}
