/* $Id$ */
/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/*  C O N T R O L   F L O W
 *
 *  C F _ S U C C . C
 */


#include <stdio.h>
#include <em_spec.h>
#include <em_pseu.h>
#include <em_flag.h>
#include <em_mnem.h>
#include "../share/types.h"
#include "../share/def.h"
#include "../share/debug.h"
#include "../share/global.h"
#include "../share/lset.h"
#include "../share/cset.h"
#include "cf.h"
#include "cf_succ.h"
#include "../share/map.h"

extern char em_flag[];


STATIC void succeeds(succ,pred)
	bblock_p succ, pred;
{
	assert(pred != (bblock_p) 0);
	if (succ != (bblock_p) 0) {
		Ladd(succ, &pred->b_succ);
		Ladd(pred, &succ->b_pred);
	}
}


#define IS_RETURN(i)	(i == op_ret || i == op_rtt)
#define IS_CASE_JUMP(i)	(i == op_csa || i == op_csb)
#define IS_UNCOND_JUMP(i) (i <= sp_lmnem && (em_flag[i-sp_fmnem] & EM_FLO) == FLO_T)
#define IS_COND_JUMP(i)	(i <= sp_lmnem && (em_flag[i-sp_fmnem] & EM_FLO) == FLO_C)
#define TARGET(lnp)	(lbmap[INSTRLAB(lnp)])
#define ATARGET(arg)	(lbmap[arg->a_a.a_instrlab])



STATIC arg_p skip_const(arg)
	arg_p arg;
{
	assert(arg != (arg_p) 0);
	switch(arg->a_type) {
		case ARGOFF:
		case ARGICN:
		case ARGUCN:
			break;
		default:
			error("bad case descriptor");
	}
	return arg->a_next;
}


STATIC arg_p use_label(arg,b)
	arg_p arg;
	bblock_p b;
{
	if (arg->a_type == ARGINSTRLAB) {
		/* arg is a non-null label */
		succeeds(ATARGET(arg),b);
	}
	return arg->a_next;
}



STATIC void case_flow(instr,desc,b)
	short    instr;
	line_p   desc;
	bblock_p b;
{
	/* Analyse the case descriptor (given as a ROM pseudo instruction).
	 * Every instruction label appearing in the descriptor
	 * heads a basic block that is a successor of the block
	 * in which the case instruction appears (b).
	 */

	register arg_p arg;

	assert(instr == op_csa || instr == op_csb);
	assert(TYPE(desc) == OPLIST);
	arg = ARG(desc);
	arg = use_label(arg,b);
	/* See if there is a default label. If so, then
	 * its block is a successor of b. Set arg to
	 * next argument.
	 */
	if (instr == op_csa) {
		arg = skip_const(arg); /* skip lower bound */
		arg = skip_const(arg); /* skip lower-upper bound */
		while (arg != (arg_p) 0) {
			/* All following arguments are case labels
			 * or zeroes.
			 */
			arg = use_label(arg,b);
		}
	} else {
		/* csb instruction */
		arg = skip_const(arg);  /* skip #entries */
		while (arg != (arg_p) 0) {
			/* All following arguments are alternatively
			 * an index and an instruction label (possibly 0).
			 */
			arg = skip_const(arg);  /* skip index */
			arg = use_label(arg,b);
		}
	}
}



STATIC line_p case_descr(lnp)
	line_p lnp;
{
	/* lnp is the instruction just before a csa or csb,
	 * so it is the instruction that pushes the address
	 * of a case descriptor on the stack. Find that
	 * descriptor, i.e. a rom pseudo instruction.
	 * Note that this instruction will always be part
	 * of the procedure in which the csa/csb occurs.
	 */

	register line_p l;
	dblock_p d;
	obj_p    obj;
	dblock_id id;

	if (lnp == (line_p) 0 || (INSTR(lnp)) != op_lae) {
		error("cannot find 'lae descr' before csa/csb");
	}
	/* We'll first find the ROM and its dblock_id */
	obj = OBJ(lnp);
	if (obj->o_off != (offset) 0) {
		error("bad 'lae descr' before csa/csb");
		/* We require a descriptor to be an entire rom,
		 * not part of a rom.
		 */
	}
	d = obj->o_dblock;
	assert(d != (dblock_p) 0);
	if (d->d_pseudo != DROM) {
		error("case descriptor must be in rom");
	}
	id = d->d_id;
	/* We'll use the dblock_id to find the defining occurrence
	 * of the rom in the EM text (i.e. a rom pseudo). As all
	 * pseudos appear at the beginning of a procedure, we only
	 * have to look in its first basic block.
	 */
	assert(curproc != (proc_p) 0);
	assert(curproc->p_start != (bblock_p) 0);
	l = curproc->p_start->b_start; /* first instruction of curproc */
	while (l != (line_p) 0) {
		if ((INSTR(l)) == ps_sym &&
		    SHORT(l) == id) {
			/* found! */
			assert((INSTR(l->l_next)) == ps_rom);
			return l->l_next;
		}
		l = l->l_next;
	}
	error("cannot find rom pseudo for case descriptor");
	/* NOTREACHED */
}



STATIC void last2_instrs(b,last_out,prev_out)
	bblock_p b;
	line_p   *last_out,*prev_out;
{
	/* Determine the last and one-but-last instruction
	 * of basic block b. An end-pseudo is not regarded
	 * as an instruction. If the block contains only 1
	 * instruction, prev_out is 0.
	 */

	register line_p l1,l2;

	l2 = b->b_start;  /* first instruction of b */
	assert(l2 != (line_p) 0); /* block can not be empty */
	if ((l1 = l2->l_next) == (line_p) 0 || INSTR(l1) == ps_end) {
		*last_out = l2; /* single instruction */
		*prev_out = (line_p) 0;
	} else {
		while(l1->l_next != (line_p) 0 && INSTR(l1->l_next) != ps_end) {
			l2 = l1;
			l1 = l1->l_next;
		}
		*last_out = l1;
		*prev_out = l2;
	}
}



void control_flow(head)
	bblock_p head;
{
	/* compute the successor and predecessor relation
	 * for every basic block.
	 */

	register bblock_p b;
	line_p lnp, prev;
	short instr;

	for (b = head; b != (bblock_p) 0; b = b->b_next) {
		/* for every basic block, in textual order, do */
		last2_instrs(b, &lnp, &prev);
		/* find last and one-but-last instruction */
		instr = INSTR(lnp);
		/* The last instruction of the basic block
		 * determines the set of successors of the block.
		 */
		if (IS_CASE_JUMP(instr)) {
			case_flow(instr,case_descr(prev),b);
			/* If lnp is a csa or csb, then the instruction
			 * just before it (i.e. prev) must be the
			 * instruction that pushes the address of the
			 * case descriptor. This descriptor is found
			 * and analysed in order to build the successor
			 * and predecessor sets of b.
			 */
		} else {
		   if (!IS_RETURN(instr)) {
			if (IS_UNCOND_JUMP(instr)) {
				if (instr != op_gto) {
					succeeds(TARGET(lnp),b);
				}
			} else {
				if (IS_COND_JUMP(instr)) {
					succeeds(TARGET(lnp),b);
					succeeds(b->b_next, b);
					/* Textually next block is
					 * a successor of b.
					 */
				} else {
					/* normal instruction */
					succeeds(b->b_next, b);
				}
			}
		   }
		}
	}
}
