/* $Id$ */
/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */

/* V A L U E N U M B E R I N G   M E T H O D */

#include <em_mnem.h>
#include "../share/types.h"
#include "../share/global.h"
#include "../share/debug.h"
#include "../share/aux.h"
#include "cs.h"
#include "cs_alloc.h"
#include "cs_aux.h"
#include "cs_entity.h"
#include "cs_avail.h"
#include "cs_stack.h"
#include "cs_kill.h"
#include "cs_partit.h"
#include "cs_getent.h"

STATIC push_entity(enp, lfirst)
	entity_p enp;
	line_p lfirst;
{
	/* Build token and Push it. */

	struct token tk;

	tk.tk_vn = enp->en_vn;
	tk.tk_size = enp->en_size;
	tk.tk_lfirst = lfirst;
	Push(&tk);
}

STATIC put_expensive_load(bp, lnp, lfirst, enp)
	bblock_p bp;
	line_p lnp, lfirst;
	entity_p enp;
{
	struct avail av;
	occur_p	ocp;

	av.av_instr = INSTR(lnp);
	av.av_size = enp->en_size;
	av.av_operand = enp->en_vn;

	ocp = newoccur(lfirst, lnp, bp);

	av_enter(&av, ocp, EXPENSIVE_LOAD);
}

STATIC put_aar(bp, lnp, lfirst, enp)
	bblock_p bp;
	line_p lnp, lfirst;
	entity_p enp;
{
	/* Enp points to an ENARRELEM. We do as if its address was computed. */

	struct avail av;
	occur_p	ocp;

	assert(enp->en_kind == ENARRELEM);
	av.av_instr = op_aar;
	av.av_size = ps;
	av.av_ofirst = enp->en_arbase;
	av.av_osecond = enp->en_index;
	av.av_othird = enp->en_adesc;

	ocp = newoccur(lfirst, lnp, bp);

	av_enter(&av, ocp, TERNAIR_OP);
}

STATIC push_avail(avp, lfirst)
	avail_p avp;
	line_p lfirst;
{
	struct token tk;

	tk.tk_vn = avp->av_result;
	tk.tk_size = avp->av_size;
	tk.tk_lfirst = lfirst;
	Push(&tk);
}

STATIC push_unair_op(bp, lnp, tkp1)
	bblock_p bp;
	line_p lnp;
	token_p tkp1;
{
	struct avail av;
	occur_p	ocp;

	av.av_instr = INSTR(lnp);
	av.av_size = avsize(lnp);
	av.av_operand = tkp1->tk_vn;

	ocp = newoccur(tkp1->tk_lfirst, lnp, bp);

	push_avail(av_enter(&av, ocp, UNAIR_OP), tkp1->tk_lfirst);
}

STATIC push_binair_op(bp, lnp, tkp1, tkp2)
	bblock_p bp;
	line_p lnp;
	token_p tkp1, tkp2;
{
	struct avail av;
	occur_p	ocp;

	av.av_instr = INSTR(lnp);
	av.av_size = avsize(lnp);
	av.av_oleft = tkp1->tk_vn;
	av.av_oright = tkp2->tk_vn;

	ocp = newoccur(tkp1->tk_lfirst, lnp, bp);

	push_avail(av_enter(&av, ocp, BINAIR_OP), tkp1->tk_lfirst);
}

STATIC push_ternair_op(bp, lnp, tkp1, tkp2, tkp3)
	bblock_p bp;
	line_p lnp;
	token_p tkp1, tkp2, tkp3;
{
	struct avail av;
	occur_p	ocp;

	av.av_instr = INSTR(lnp);
	av.av_size = avsize(lnp);
	av.av_ofirst = tkp1->tk_vn;
	av.av_osecond = tkp2->tk_vn;
	av.av_othird = tkp3->tk_vn;

	ocp = newoccur(tkp1->tk_lfirst, lnp, bp);

	push_avail(av_enter(&av, ocp, TERNAIR_OP), tkp1->tk_lfirst);
}

STATIC fiddle_stack(lnp)
	line_p lnp;
{
	/* The instruction in lnp does something to the valuenumber-stack. */

	struct token dummy;
	offset size;

	/* Partly initialize dummy. */
	dummy.tk_lfirst = lnp;

	switch (INSTR(lnp)) {
		default:
			assert(FALSE);
			break;
		case op_lor:
			dummy.tk_vn = newvalnum(); dummy.tk_size = ps;
			Push(&dummy);
			break;
		case op_asp:
			if ((size = off_set(lnp)) > 0) {
				Pop(&dummy, size);
			} else {
				dummy.tk_vn = newvalnum();
				dummy.tk_size = size;
				Push(&dummy);
			}
			break;
		case op_dup:
			Dup(lnp);
			break;
		case op_ass:
		case op_dus:
		case op_exg:
		case op_los:
			/* Don't waste effort. */
			clr_stack();
			break;
		case op_sig:
			Pop(&dummy, (offset) ps);
			break;
		case op_lfr:
			dummy.tk_vn = newvalnum();
			dummy.tk_size = off_set(lnp);
			Push(&dummy);
			break;
		case op_beq:
		case op_bge:
		case op_bgt:
		case op_bne:
		case op_ble:
		case op_blt:
			Pop(&dummy, (offset) ws);
			Pop(&dummy, (offset) ws);
			break;
		case op_bra:
		case op_csa:/* ??? */
		case op_csb:/* ??? */
		case op_gto:/* ??? */
		case op_ret:/* ??? */
		case op_rtt:/* ??? */
			break;
		case op_zeq:
		case op_zge:
		case op_zgt:
		case op_zne:
		case op_zle:
		case op_zlt:
		case op_trp:
			Pop(&dummy, (offset) ws);
			break;
		case op_rck:
			Pop(&dummy, (offset) ps);
			break;
	}
}

STATIC proc_p find_proc(vn)
	valnum vn;
{
	/* Find the procedure-identifier with valuenumber vn. */

	entity_p enp;

	enp = find_entity(vn);

	if (enp != (entity_p) 0 && enp->en_kind == ENPROC)
		return enp->en_pro;

	return (proc_p) 0;
}

STATIC side_effects(lnp)
	line_p lnp;
{
	/* Lnp contains a cai or cal instruction. We try to find the callee
	 * and see what side-effects it has.
	 */
	struct token tk;
	proc_p pp;

	if (INSTR(lnp) == op_cai) {
		Pop(&tk, (offset) ps);
		pp = find_proc(tk.tk_vn);
	} else {
		assert(INSTR(lnp) == op_cal);
		pp = PROC(lnp);
	}
	if (pp != (proc_p) 0) {
		kill_call(pp);
	} else {
		kill_much();
	}
}

hopeless(instr)
	int instr;
{
	/* The effect of `instr' is too difficult to
	 * compute. We assume worst case behaviour.
	 */
	switch (instr) {
		default:
			assert(FALSE);
			break;
		case op_mon:
		case op_str:
		case op_nop:	/* for volatiles */
			/* We can't even trust "static" entities. */
			kill_all();
			clr_stack();
			break;
		case op_blm:
		case op_bls:
		case op_sts:
			kill_much();
			clr_stack();
			break;
	}
}

vnm(bp)
	bblock_p bp;
{
	register line_p lnp;
	register entity_p rep;
	line_p lfirst;
	struct token tk, tk1, tk2, tk3;

	for (lnp = bp->b_start; lnp != (line_p) 0; lnp = lnp->l_next) {

		rep = getentity(lnp, &lfirst);
		switch (instrgroup(lnp)) {
			case SIMPLE_LOAD:	
				push_entity(rep, lfirst);
				break;
			case LOAD_ARRAY:
				put_aar(bp, lnp, lfirst, rep);
				/* Fall through ... */
			case EXPENSIVE_LOAD:
				push_entity(rep, lfirst);
				put_expensive_load(bp, lnp, lfirst, rep);
				break;
			case STORE_DIRECT:
				kill_direct(rep);
				Pop(&tk, rep->en_size);
				rep->en_vn = tk.tk_vn;
				break;
			case STORE_ARRAY:
				put_aar(bp, lnp, lfirst, rep);
				/* Fall through ... */
			case STORE_INDIR:
				kill_indir(rep);
				Pop(&tk, rep->en_size);
				rep->en_vn = tk.tk_vn;
				break;
			case UNAIR_OP:
				Pop(&tk1, op11size(lnp));
				push_unair_op(bp, lnp, &tk1);
				break;
			case BINAIR_OP:
				Pop(&tk2, op22size(lnp));
				Pop(&tk1, op12size(lnp));
				push_binair_op(bp, lnp, &tk1, &tk2);
				break;
			case TERNAIR_OP:
				Pop(&tk3, op33size(lnp));
				Pop(&tk2, op23size(lnp));
				Pop(&tk1, op13size(lnp));
				push_ternair_op(bp, lnp, &tk1, &tk2, &tk3);
				break;
			case KILL_ENTITY:
				kill_direct(rep);
				break;
			case SIDE_EFFECTS:
				side_effects(lnp);
				break;
			case FIDDLE_STACK:
				fiddle_stack(lnp);
				break;
			case IGNORE:
				break;
			case HOPELESS:
				hopeless(INSTR(lnp));
				break;
			case BBLOCK_END:
				fiddle_stack(lnp);
				break;
			default:
				assert(FALSE);
				break;
		}
	}
}