/* src/vm/jit/allocator/liveness.c - liveness analysis for lsra

   Copyright (C) 2005, 2006, 2007, 2008
   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO

   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., 51 Franklin Street, Fifth Floor, Boston, MA
   02110-1301, USA.

*/


#include "config.h"

#include <limits.h>
#include <stdlib.h>

#include "vm/types.h"

#include "mm/memory.hpp"

#include "toolbox/logging.hpp"
#include "toolbox/worklist.h"

#include "vm/jit/builtin.hpp"
#include "vm/exceptions.hpp"
#include "vm/global.h"
#include "vm/method.hpp"
#include "vm/resolve.hpp"
#include "vm/jit/codegen-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/allocator/lsra.h"
#include "vm/jit/allocator/liveness.h"


/* function prototypes */
void liveness_scan_registers_canditates(jitdata *jd, int b_index, int iindex, 
										stackelement_t* src, lv_sets *sets);
void liveness_set_stack(lsradata *ls, int block, int g_iindex, stackelement_t* s,
						lv_sets *sets, int op);
void liveness_set_local(lsradata *ls, int block, int g_iindex, s4 v_index,
						int type, lv_sets *sets, int op);

void liveness_add_ss(struct lifetime *lt, stackelement_t* s) {
	struct stackslot *ss;
	/* Stackslot noch nicht eingetragen? */

	if (s->varnum != lt->v_index) {
		ss = DNEW(struct stackslot);
		ss->s = s;
		ss->s->varnum = lt->v_index;
		ss->next = lt->local_ss;
		lt->local_ss = ss;
		if (s != NULL) lt->savedvar |= s->flags & SAVEDVAR;
		if (s != NULL) lt->type = s->type;
	}
}

void liveness_join_ss( struct lsradata *ls, struct stackelement *in,
				   struct stackelement *out) {
	struct lifetime *lt, *lto;
	struct stackslot *ss, *ss_last;


	if (in->varnum != out->varnum) {
		lt = &(ls->lifetime[-in->varnum - 1]);

#ifdef LV_DEBUG_CHECK
		if (lt->type == -1) { 
#ifdef LV_DEBUG_VERBOSE
			log_text("liveness_join_ss: lifetime for instack not found\n");
#endif
			assert(0);
		}
#endif

		if (out->varnum >= 0) { /* no lifetime for this slot till now */
			liveness_add_ss(lt, out);
		} else {
			lto = &(ls->lifetime[-out->varnum - 1]);

#ifdef LV_DEBUG_CHECK
			if (lto->type == -1) {
#ifdef LV_DEBUG_VERBOSE
				log_text("liveness_join_ss: lifetime for outstack not found\n");
#endif
				assert(0);
			}
#endif
#ifdef LV_DEBUG_CHECK
			if (lto->type != lt->type) {
#ifdef LV_DEBUG_VERBOSE
				log_text("liveness_join_ss: in/out stack type mismatch\n");
#endif
				assert(0);
			}
#endif
	
			/* take Lifetime lto out of ls->lifetimes */
			lto->type = -1;

			/* merge lto into lt of in */

			/* change varnums of all lto->local_ss->s to lt->v_index */
			ss_last = ss = lto->local_ss;
			while (ss != NULL) {
				ss_last = ss;
				ss->s->varnum = lt->v_index;
				ss = ss->next;
			}
			/* link both lto->local_ss list to lt->local_ss */
			if (ss_last != NULL) {
				ss_last->next = lt->local_ss;
				lt->local_ss = lto->local_ss;
			}

			lt->savedvar |= lto->savedvar;
			lt->flags |= lto->flags;
			lt->usagecount += lto->usagecount;

			/*join of [bb|i]_first_def und [bb|i]_last_use */
			if (lto->bb_first_def < lt->bb_first_def) {
				lt->bb_first_def = lto->bb_first_def;
				lt->i_first_def = lto->i_first_def;
			} else if ((lto->bb_first_def == lt->bb_first_def) &&
					   ( lto->i_first_def < lt->i_first_def)) {
				lt->i_first_def = lto->i_first_def;
			}	
			if (lto->bb_last_use > lt->bb_last_use) {
				lt->bb_last_use = lto->bb_last_use;
				lt->i_last_use = lto->i_last_use;
			} else if ((lto->bb_last_use == lt->bb_last_use) &&
					   ( lto->i_last_use > lt->i_last_use)) {
				lt->i_last_use = lto->i_last_use;
			}	
		}
	}
}

/* join instack of Basic Block b_index with outstack of predecessors */
void liveness_join_lifetimes(jitdata *jd, int b_index) {
	struct stackelement *in, *i, *out;
    struct _list *pred;

	lsradata *ls;
	methodinfo *m;

	ls = jd->ls;
	m  = jd->m;

	/* do not join instack of Exception Handler */ 
	if (m->basicblocks[b_index].type == BBTYPE_EXH)
		return;
	in=m->basicblocks[b_index].instack;
	/* do not join first instack element of a subroutine header */
	if (m->basicblocks[b_index].type == BBTYPE_SBR)
		in=in->prev; 
	
	if (in != NULL) {
		for (pred = ls->pred[b_index]; pred != NULL; pred = pred->next) {
			out = m->basicblocks[pred->value].outstack;
			for (i=in; (i != NULL); i = i->prev, out=out->prev) {
				liveness_join_ss(ls, i, out);
			}
		}
	}
}

void liveness_setup(jitdata *jd) {
	int i, icount, b_index;
	stackelement_t* src;
	methodinfo *m;
	lsradata *ls;

	ls = jd->ls;
	m  = jd->m;

	ls->icount_block = DMNEW(int, m->basicblockcount);
	ls->icount_block[0] = icount = 0;
	for(i = 0; i < m->basicblockcount; i++) {
		if (i != 0) {
			/* create a global instruction index in icount_block */
			if (ls->sorted[i-1] != -1)
				icount += m->basicblocks[ ls->sorted[i-1] ].icount;
				ls->icount_block[i] = icount;
		}

		if ((b_index = ls->sorted[i]) != -1) {
			/* 'valid' Basic Block */

			/* adapt in- and outstacks for LSRA */
			src = m->basicblocks[b_index].instack;
			if (m->basicblocks[b_index].type != BBTYPE_STD) {
#ifdef LV_DEBUG_CHECK
				if (src == NULL) {
#ifdef LV_DEBUG_VERBOSE
				log_text("No Incoming Stackslot for Exception/Subroutine BB\n");
#endif
					assert(0);
				}
#endif
				if (src->varkind == STACKVAR)
					src->varkind = TEMPVAR;
				src = src->prev;
			}
			for (;src != NULL; src=src->prev) {
				/* no ARGVAR possible at BB Boundaries with LSRA! */
				/* -> change to TEMPVAR                           */
				if (src->varkind == ARGVAR ) {
					src->varkind = TEMPVAR;
					/* On Architectures with own return registers a return
					   stackslot is marked as varkind=ARGVAR with varnum=-1
					   but for lsra a varkind==TEMPVAR, varnum=-1 would mean,
					   that already a lifetime was allocated! */
					if (src->varnum < 0) src->varnum = 0;
				}
				else if (src->varkind == LOCALVAR ) {
					/* only allowed for top most ss at sbr or exh entries! */
#ifdef LV_DEBUG_VERBOSE
					log_text("LOCALVAR at basicblock instack\n");
#endif
					abort();
				} else {
					if (src->varkind == STACKVAR )
						/* no Interfaces at BB Boundaries with LSRA! */
						/* -> change to TEMPVAR                      */
						src->varkind = TEMPVAR;
				}
			}

			src = m->basicblocks[b_index].outstack;
			for (;src != NULL; src=src->prev) {
				if (src->varkind == ARGVAR ) {
#ifdef LV_DEBUG_VERBOSE
					log_text("ARGVAR at basicblock outstack\n");
#endif
					abort();
				} else if (src->varkind == LOCALVAR ) {
#ifdef LV_DEBUG_VERBOSE
					log_text("LOCALVAR at basicblock outstack\n");
#endif
					abort();
				} else {
					/* no Interfaces at BB Boundaries with LSRA! */
					/* -> change to TEMPVAR                      */
					if (src->varkind == STACKVAR )
						src->varkind = TEMPVAR;
				}
			}
		}
	}
}

void liveness_init(jitdata *jd) {
	int i, b_index, len;
	int lifetimes;
	stackelement_t* src, dst;
	instruction *iptr;
	methodinfo *m;
	lsradata *ls;

	ls = jd->ls;
	m  = jd->m;

	lifetimes = 0;

	for(i = 0; i < m->basicblockcount; i++) {
		if ((b_index = ls->sorted[i]) != -1) {
			/* 'valid' Basic Block */

			/* Scan Number of Stack Lifetimes */
			lifetimes += m->basicblocks[b_index].indepth;

			dst = m->basicblocks[b_index].instack;
			len = m->basicblocks[b_index].icount;
			iptr = m->basicblocks[b_index].iinstr;
			for (;len>0; len--, iptr++) {
				src = dst;
				dst = iptr->dst;
				
				switch(iptr->opc) {
				case ICMD_SWAP:
				case ICMD_DUP2:
					lifetimes += 2;
					break;
				case ICMD_DUP_X1:
					lifetimes += 3;
					break;
				case ICMD_DUP2_X1:
					lifetimes += 5;
					break;
				case ICMD_DUP_X2:
					lifetimes += 4;
					break;
				case ICMD_DUP2_X2:
					lifetimes += 6;
					break;

				default:
					if (( dst != NULL) && (src != dst))
						lifetimes++;
				}
			}
		}
	}
	ls->maxlifetimes = lifetimes;
	ls->lifetimecount = lifetimes + jd->maxlocals * (TYPE_ADR+1);
}

void liveness_scan_basicblock(jitdata *jd, int b_index,
							  lv_sets *sets, int lifetimes) {
	int iindex;
	stackelement_t*    src;
	instruction *iptr;
	int i;
	methodinfo *m;
	lsradata *ls;

	ls = jd->ls;
	m  = jd->m;

	src = m->basicblocks[b_index].instack;

	iindex = m->basicblocks[b_index].icount - 1;
	/* set iptr to last instruction of BB */
	iptr = m->basicblocks[b_index].iinstr + iindex;

	bv_reset(sets->in, lifetimes);

	for (;iindex >= 0; iindex--, iptr--)  {
		/* get source Stack for the current instruction     */
		/* destination stack is available as iptr->dst                      */
		/* source stack is either the destination stack of the previos      */
		/* instruction, or the basicblock instack for the first instruction */
		if (iindex) /* != 0 is > 0 here, since iindex ist always >= 0 */
			src=(iptr-1)->dst;
		else
			src=m->basicblocks[b_index].instack;

		/* Reset kill and gen bitvectors for use in */
		/* liveness_scan_register_canditates */

		bv_reset(sets->kill, lifetimes);
		bv_reset(sets->gen, lifetimes);

		/* get gen and kill set of instruction */

		liveness_scan_registers_canditates(jd, b_index, iindex, src, sets);

		/* tmp = out(instr) - kill(instr) */

		bv_minus(sets->tmp, sets->out, sets->kill, lifetimes);

		/* in  = gen(instr) union tmp = gen union (out - kill) */

		bv_union(sets->in, sets->gen, sets->tmp, lifetimes);    

		/* Set SAVEDVAR flag for locals */

		if (op_needs_saved[iptr->opc])
			for(i = ls->maxlifetimes; i < ls->lifetimecount; i++)
				if (!ls->lifetime[i].savedvar)
					if ( bv_get_bit(sets->in,i) && bv_get_bit(sets->out,i) )
						ls->lifetime[i].savedvar = SAVEDVAR;

        /* out(instr-1) = in(instr) (only one successor)*/

		bv_copy(sets->out, sets->in, lifetimes);
	}
	/* create gen sets for incoming stackslots */

	/* global instruction index for bb b_index */

	iindex = ls->icount_block[ls->sorted_rev[b_index]];
	bv_reset(sets->kill, lifetimes);
	bv_reset(sets->gen, lifetimes);
	src = m->basicblocks[b_index].instack;
	if (m->basicblocks[b_index].type != BBTYPE_STD) {
		liveness_set_stack(ls, b_index, iindex, src, sets, LV_KILL);
		src = src->prev;
	}
	for (;src != NULL; src=src->prev) {
		/* only TEMP or LOCALVAR by now possible      */
		/* liveness_set_stack redirects for LOCALVARS */
		liveness_set_stack(ls, b_index, iindex, src,	sets, LV_GEN);
		_LV_ASSERT( ((src->varkind == LOCALVAR) || (src->varkind == TEMPVAR)) );
	}	
	/* in  = gen union (out - kill) */
	bv_minus(sets->tmp, sets->out, sets->kill, lifetimes);
	bv_union(sets->in, sets->gen, sets->tmp, lifetimes);    
}
		
void liveness(jitdata *jd) {
	bitvector *out;
	bitvector *in;
	bitvector params;
	char *buff;
	worklist *W;
	int b_index;
    struct _list *succ;
	struct _list *pred;
	bitvector visited;
	lv_sets   sets;
	int       i, p, t;
	methoddesc *md;
#ifdef LV_DEBUG_CHECK
	stackelement_t* s;
#endif
	methodinfo *m;
	registerdata *rd;
	lsradata *ls;
	/***************************************************************************
 TODO: 
 - Exact Lifeness Information for intra Basic Blocks Stackslots are trivial
 They should not be included in the gen, kill, in and out sets to improve
 performance.
 - Local Vars as represented in rd->locals "are quite sparse". An intermediate
 representation for really used index/type pairs should be implemented. 
	***************************************************************************/

	ls = jd->ls;
	m  = jd->m;
	rd = jd->rd;

	if (ls->lifetimecount == 0)
		return;
	ls->lifetime = DMNEW(struct lifetime, ls->lifetimecount);
	for (i=0; i < ls->lifetimecount; i++) ls->lifetime[i].type = -1;

	sets.gen = bv_new(ls->lifetimecount);
	sets.kill = bv_new(ls->lifetimecount);
	sets.tmp = bv_new(ls->lifetimecount);
	sets.out = bv_new(ls->lifetimecount);
	sets.in =  bv_new(ls->lifetimecount);

	params =  bv_new(ls->lifetimecount);

	visited = bv_new(m->basicblockcount);
	buff = DMNEW(char, ls->lifetimecount+1);

	out = DMNEW(bitvector, m->basicblockcount);
	in  = DMNEW(bitvector, m->basicblockcount);
	for(i = 0; i < m->basicblockcount; i++) {
		out[i] = bv_new(ls->lifetimecount);
		in[i]  = bv_new(ls->lifetimecount);
	}

	/* set in[0] to arguments */
 	/* <-> kill at 0, -1 */
	md = m->parseddesc;
	for (p = 0, i = 0; p < md->paramcount; p++) {
 		t = md->paramtypes[p].type;

		if (rd->locals[i][t].type >= 0)	
			/* Param to Local init happens before normal Code */
			liveness_set_local(ls, 0, -1, i, t, &sets, LV_KILL); 
 		i++;
 		if (IS_2_WORD_TYPE(t))    /* increment local counter a second time  */
 			i++;                  /* for 2 word types */
	}  /* end for */
	bv_copy(params, sets.kill, ls->lifetimecount);

	/* fill Worklist so that last node will be taken out first */
	W = wl_new(m->basicblockcount);
	for (i = 0; i < m->basicblockcount; i++)
		if (ls->sorted[i] != -1)
			wl_add(W, ls->sorted[i]);

	/* Worklist algorithm*/
	while (!wl_is_empty(W)) {
		b_index = wl_get(W);

		/* out[b_index] = for all s element of successors(b_index) union in[s]*/
		for (succ = ls->succ[b_index]; succ != NULL; succ = succ->next)
			bv_union(out[b_index], out[b_index], in[succ->value],
					 ls->lifetimecount);

		bv_copy(sets.out, out[b_index], ls->lifetimecount);

		/* compute in[b_index] */
		liveness_scan_basicblock(jd, b_index, &sets, ls->lifetimecount);

		if (!bv_get_bit(visited, b_index)) {
			liveness_join_lifetimes(jd, b_index);
			bv_set_bit(visited, b_index);
		}

		if (!bv_equal(sets.in, in[b_index], ls->lifetimecount)) {
			bv_copy(in[b_index], sets.in, ls->lifetimecount);
	 		for(pred = ls->pred[b_index]; pred != NULL; pred = pred->next)
				wl_add(W, pred->value);
		}
	}

#ifdef LV_DEBUG_CHECK
	s = m->basicblocks[b_index].instack;
	if ((s != NULL) && (m->basicblocks[b_index].flags != BBTYPE_STD))
		s = s->prev;
	for( ; s != NULL; s = s->prev) {
#ifdef LV_DEBUG_VERBOSE
		if (!bv_get_bit(in[b_index], -s->varnum - 1)) {
			log_text("liveness: Error In Stacklot not live!\n");
		}
#endif
		_LV_ASSERT( (bv_get_bit(in[b_index], -s->varnum - 1)) );
	}
#endif

	for (i = 0; i < m->basicblockcount; i++)
		if ((b_index=ls->sorted[i]) != -1) {
			for(t = 0; t < ls->lifetimecount; t++) {
				if (ls->lifetime[t].type != -1) {
					if (bv_get_bit(in[b_index], t)) {
						p = ls->icount_block[ls->sorted_rev[b_index]];
						if (p < ls->lifetime[t].i_first_def)
							ls->lifetime[t].i_first_def = p;
					}
					if (bv_get_bit(out[b_index], t)) {
						p = 
   ls->icount_block[ls->sorted_rev[b_index]]+m->basicblocks[b_index].icount - 1;
						if (p > ls->lifetime[t].i_last_use)
							ls->lifetime[t].i_last_use = p;
					}
				}
			}
		}
		   
}

struct lifetime *liveness_get_ss_lifetime(lsradata *ls, stackelement_t* s) {
	struct lifetime *n;
	
	if (s->varnum >= 0) { /* new stackslot lifetime */
#ifdef LV_DEBUG_VERBOSE		
		if (-ls->v_index - 1 >= ls->maxlifetimes) {
			printf("%i %i\n", -ls->v_index - 1, ls->maxlifetimes);
		}
#endif
		_LV_ASSERT(-ls->v_index - 1 < ls->maxlifetimes);

		n = &(ls->lifetime[-ls->v_index - 1]);
		n->type = s->type;
		n->v_index = ls->v_index--;
		n->usagecount = 0;
		
		n->i_last_use = -2;
		n->i_first_def = INT_MAX;
		n->local_ss = NULL;
		n->savedvar = 0;
		n->flags = 0;
	} else
		n = &(ls->lifetime[-s->varnum - 1]);

	liveness_add_ss( n, s);
	return n;
}

void liveness_set_stack(lsradata *ls, int block, int g_iindex, stackelement_t* s,
						lv_sets *sets,
						int op) {
	struct lifetime *n;

	if (s->varkind == LOCALVAR) {
		liveness_set_local(ls, block, g_iindex, s->varnum, s->type, sets, op);
	} else if (s->varkind == TEMPVAR) {
		n = liveness_get_ss_lifetime(ls, s);
		if (op == LV_KILL) {
			bv_set_bit(sets->kill, -s->varnum - 1);
			if (n->i_first_def > g_iindex) {
				n->i_first_def = g_iindex;
			}
		} else {
			/* LV_GEN */
			bv_set_bit(sets->gen, -s->varnum - 1);
			if (n->i_last_use < g_iindex) {
				n->i_last_use = g_iindex;
			}
		}
		n->usagecount+=ls->nesting[ls->sorted_rev[block]];
	}
}

void liveness_set_local(lsradata *ls, int block, int g_iindex, s4 v_index,
						int type, lv_sets *sets, int op) {
	struct lifetime *n;

	n = &(ls->lifetime[ ls->maxlifetimes + v_index * (TYPE_ADR+1) + type]);

	if (n->type == -1) { /* new local lifetime */
		n->local_ss=NULL;
		n->v_index=v_index;
		n->type=type;
		/* TODO: Look if local really needs to be a savedvar */
		n->savedvar = 0; /* SAVEDVAR; */
		n->flags = 0;
		n->usagecount = 0;

		n->i_last_use = -2;
		n->i_first_def = INT_MAX;
	}
	n->usagecount+=ls->nesting[ls->sorted_rev[block]];
	if (op == LV_KILL) {
		bv_set_bit(sets->kill, ls->maxlifetimes + v_index * (TYPE_ADR+1)+ type);
		if (n->i_first_def > g_iindex) {
			n->i_first_def = g_iindex;
		}
	} else {
		/* LV_GEN */
		bv_set_bit(sets->gen, ls->maxlifetimes + v_index * (TYPE_ADR+1) + type);
		if (n->i_last_use < g_iindex) {
			n->i_last_use = g_iindex;
		}
	}
}

void liveness_scan_registers_canditates(jitdata *jd, int b_index, int iindex,
										stackelement_t* src, lv_sets *sets)
{
/* 	methodinfo         *lm; */
	builtintable_entry *bte;
	methoddesc         *md;
	int i, g_iindex;
	instruction *iptr;
	stackelement_t*    dst;
	methodinfo *m;
	lsradata   *ls;

	m  = jd->m;
	ls = jd->ls;

	iptr = m->basicblocks[b_index].iinstr + iindex;
	dst = iptr->dst;
	g_iindex = ls->icount_block[ls->sorted_rev[b_index]] + iindex;
	switch (iptr->opc) {
		/* pop 0 push 0 */
	case ICMD_RET:
		/* local read (return adress) */
		liveness_set_local(ls, b_index, g_iindex, iptr->op1, TYPE_ADR, sets, LV_GEN);

		break;
	case ICMD_NOP:
/* 	case ICMD_ELSE_ICONST: */
	case ICMD_CHECKNULL:
	case ICMD_JSR:
	case ICMD_RETURN:
	case ICMD_GOTO:
	case ICMD_PUTSTATICCONST:
	case ICMD_INLINE_START:
	case ICMD_INLINE_END:
	case ICMD_INLINE_GOTO:
		break;
                             
	case ICMD_IINC:
		/* local = local+<const> */
		liveness_set_local(ls, b_index, g_iindex, iptr->op1, TYPE_INT, sets,  LV_GEN);
		liveness_set_local(ls, b_index, g_iindex, iptr->op1, TYPE_INT, sets,  LV_KILL);
		break;

		/* pop 0 push 1 const: const->stack */
	case ICMD_ICONST:
	case ICMD_LCONST:
	case ICMD_FCONST:
	case ICMD_DCONST:
	case ICMD_ACONST:
		/* new stack slot */
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		break;

		/* pop 0 push 1 load: local->stack */
	case ICMD_ILOAD:
	case ICMD_LLOAD:
	case ICMD_FLOAD:
	case ICMD_DLOAD:
	case ICMD_ALOAD:
		if (dst->varkind != LOCALVAR) {
			/* local->value on stack */
			liveness_set_local(ls, b_index, g_iindex, iptr->op1,
							   iptr->opc - ICMD_ILOAD, sets, LV_GEN);
			/* value->stack */
			liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL); 
		} else /* if (dst->varnum != iptr->op1) */ {
			/* local -> local */
			liveness_set_local(ls, b_index, g_iindex, iptr->op1,
							   iptr->opc - ICMD_ILOAD, sets, LV_GEN);
			liveness_set_local(ls, b_index, g_iindex, dst->varnum,
							   iptr->opc - ICMD_ILOAD, sets, LV_KILL);
		}
		break;

		/* pop 2 push 1 */
		/* Stack(arrayref,index)->stack */
	case ICMD_IALOAD:
	case ICMD_LALOAD:
	case ICMD_FALOAD:
	case ICMD_DALOAD:
	case ICMD_AALOAD:

	case ICMD_BALOAD:
	case ICMD_CALOAD:
	case ICMD_SALOAD:
		/* stack->index */
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
		/* stack->arrayref */
		liveness_set_stack(ls, b_index, g_iindex, src->prev, sets, LV_GEN);
		/* arrayref[index]->stack */
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		break;

		/* pop 3 push 0 */
		/* stack(arrayref,index,value)->arrayref[index]=value */
	case ICMD_IASTORE:
	case ICMD_LASTORE:
	case ICMD_FASTORE:
	case ICMD_DASTORE:
	case ICMD_AASTORE:

	case ICMD_BASTORE:
	case ICMD_CASTORE:
	case ICMD_SASTORE:
		/* stack -> value */
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN); 
		/* stack -> index*/
		liveness_set_stack(ls, b_index, g_iindex, src->prev, sets, LV_GEN);
		/* stack -> arrayref */
		liveness_set_stack(ls, b_index, g_iindex, src->prev->prev, sets, LV_GEN); 
		break;

		/* pop 1 push 0 store: stack -> local */
	case ICMD_ISTORE:
	case ICMD_LSTORE:
	case ICMD_FSTORE:
	case ICMD_DSTORE:
	case ICMD_ASTORE:
		if (src->varkind != LOCALVAR) {
			/* stack -> value */
			liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN); 
			liveness_set_local(ls, b_index, g_iindex, iptr->op1, iptr->opc - ICMD_ISTORE,
							   sets, LV_KILL);
		} else {
			liveness_set_local(ls, b_index, g_iindex, src->varnum, iptr->opc-ICMD_ISTORE,
							   sets, LV_GEN);
			liveness_set_local(ls, b_index, g_iindex, iptr->op1, iptr->opc - ICMD_ISTORE,
							   sets, LV_KILL);
		}
		break;

		/* pop 1 push 0 */
	case ICMD_POP: /* throw away a stackslot */
		/* TODO: check if used anyway (DUP...) and change codegen to */
		/*       ignore this stackslot */
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
		break;

		/* pop 1 push 0 */
	case ICMD_IRETURN:
	case ICMD_LRETURN:
	case ICMD_FRETURN:
	case ICMD_DRETURN:
	case ICMD_ARETURN: /* stack(value) -> [empty]    */

	case ICMD_ATHROW:  /* stack(objref) -> undefined */

	case ICMD_PUTSTATIC: /* stack(value) -> static_field */
	case ICMD_PUTFIELDCONST:

		/* pop 1 push 0 branch */
	case ICMD_IFNULL: /* stack(value) -> branch? */
	case ICMD_IFNONNULL:

	case ICMD_IFEQ:
	case ICMD_IFNE:
	case ICMD_IFLT:
	case ICMD_IFGE:
	case ICMD_IFGT:
	case ICMD_IFLE:

	case ICMD_IF_LEQ:
	case ICMD_IF_LNE:
	case ICMD_IF_LLT:
	case ICMD_IF_LGE:
	case ICMD_IF_LGT:
	case ICMD_IF_LLE:

		/* pop 1 push 0 table branch */
	case ICMD_TABLESWITCH:
	case ICMD_LOOKUPSWITCH:

	case ICMD_MONITORENTER:
	case ICMD_MONITOREXIT:
		/* stack -> value */
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN); 
		break;

		/* pop 2 push 0 */
	case ICMD_POP2: /* throw away 2 stackslots */
		/* TODO: check if used anyway (DUP...) and change codegen to */
		/*       ignore this stackslot */
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex, src->prev, sets, LV_GEN);
		break;

		/* pop 2 push 0 branch */

	case ICMD_IF_ICMPEQ: /* stack (v1,v2) -> branch(v1,v2) */
	case ICMD_IF_ICMPNE:
	case ICMD_IF_ICMPLT:
	case ICMD_IF_ICMPGE:
	case ICMD_IF_ICMPGT:
	case ICMD_IF_ICMPLE:

	case ICMD_IF_LCMPEQ:
	case ICMD_IF_LCMPNE:
	case ICMD_IF_LCMPLT:
	case ICMD_IF_LCMPGE:
	case ICMD_IF_LCMPGT:
	case ICMD_IF_LCMPLE:

	case ICMD_IF_ACMPEQ:
	case ICMD_IF_ACMPNE:

		/* pop 2 push 0 */
	case ICMD_PUTFIELD: /* stack(objref,value) -> objref = value */

	case ICMD_IASTORECONST:
	case ICMD_LASTORECONST:
	case ICMD_AASTORECONST:
	case ICMD_BASTORECONST:
	case ICMD_CASTORECONST:
	case ICMD_SASTORECONST:
		/* stack -> value*/
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);  
		liveness_set_stack(ls, b_index, g_iindex, src->prev, sets, LV_GEN);
		break;

		/* pop 0 push 1 dup */
	case ICMD_DUP: 
		/* src == dst->prev */
		/* src -> dst */

		/* src and dst->prev are identical */
		/*		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_KILL);
				liveness_set_stack(ls, b_index, g_iindex, dst->prev, sets, LV_GEN);*/

		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);

		break;

		/* pop 0 push 2 dup */
	case ICMD_DUP2: 
		/* src       ==  dst->prev->prev */
		/* src->prev == dst->prev->prev->prev */
		/* src       ->  dst */
		/* src->prev -> dst->prev-> */

		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev, sets, LV_KILL);
		break;

		/* pop 2 push 3 dup */
	case ICMD_DUP_X1:
		/* src       -> dst */
		/* src->prev -> dst->prev */
		/* src       -> dst->prev->prev */

		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev, sets,
						   LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex + 1, src, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev, sets, LV_GEN);
		break;

		/* pop 3 push 4 dup */
	case ICMD_DUP_X2:
		/* src             -> dst                   */
		/* src->prev       -> dst->prev             */
		/* src->prev->prev -> dst->prev->prev       */
		/* dst (=src)      -> dst->prev->prev->prev */

		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev, sets,
						   LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev->prev, sets,
						   LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex + 1, src, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev->prev, sets,
						   LV_GEN);
		break;

		/* pop 3 push 5 dup */
	case ICMD_DUP2_X1:
		/* src                    -> dst                   */
		/* src->prev              -> dst->prev             */
		/* src->prev->prev        -> dst->prev->prev       */
		/* dst (=src)             -> dst->prev->prev->prev */
		/* dst->prev (=src->prev) -> dst->prev->prev->prev->prev */
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev, sets,
						   LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev->prev, sets,
						   LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev->prev->prev,
						   sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex + 1, src, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev->prev, sets,
						   LV_GEN);
		break;

		/* pop 4 push 6 dup */
	case ICMD_DUP2_X2:
		/* src                    -> dst                   */
		/* src->prev              -> dst->prev             */
		/* src->prev->prev        -> dst->prev->prev       */
		/* src->prev->prev->prev  -> dst->prev->prev->prev       */
		/* dst (=src)             -> dst->prev->prev->prev->prev */
		/* dst->prev (=src->prev) -> dst->prev->prev->prev->prev->prev */
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev, sets,
						   LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev->prev, sets,
						   LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev->prev->prev->prev,
						   sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex,
						   dst->prev->prev->prev->prev->prev, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex + 1, src, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev->prev, sets,
						   LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev->prev->prev,
						   sets, LV_GEN);
		break;

		/* pop 2 push 2 swap */
	case ICMD_SWAP:
		/* src       -> dst->prev */
		/* src->prev -> dst       */
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, dst->prev, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex + 1, src, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex + 1, src->prev, sets, LV_GEN);
		break;

		/* pop 2 push 1 */
					
	case ICMD_LADD:
	case ICMD_LSUB:
	case ICMD_LMUL:

	case ICMD_LOR:
	case ICMD_LAND:
	case ICMD_LXOR:

	case ICMD_LSHL:
	case ICMD_LSHR:
	case ICMD_LUSHR:

	case ICMD_IADD:
	case ICMD_IMUL:

	case ICMD_ISHL:
	case ICMD_ISHR:
	case ICMD_IUSHR:
	case ICMD_IAND:
	case ICMD_IOR:
	case ICMD_IXOR:


	case ICMD_FADD:
	case ICMD_FSUB:
	case ICMD_FMUL:

	case ICMD_DADD:
	case ICMD_DSUB:
	case ICMD_DMUL:
	case ICMD_DDIV:
	case ICMD_DREM:

	case ICMD_ISUB:

	case ICMD_LDIV:
	case ICMD_LREM:

	case ICMD_IDIV:
	case ICMD_IREM:

	case ICMD_FDIV:
	case ICMD_FREM:

	case ICMD_LCMP:
	case ICMD_FCMPL:
	case ICMD_FCMPG:
	case ICMD_DCMPL:
	case ICMD_DCMPG:
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
		liveness_set_stack(ls, b_index, g_iindex, src->prev, sets, LV_GEN);
		break;

		/* pop 1 push 1 */
	case ICMD_LADDCONST:
	case ICMD_LSUBCONST:
	case ICMD_LMULCONST:
	case ICMD_LMULPOW2:
	case ICMD_LDIVPOW2:
	case ICMD_LREMPOW2:
	case ICMD_LANDCONST:
	case ICMD_LORCONST:
	case ICMD_LXORCONST:
	case ICMD_LSHLCONST:
	case ICMD_LSHRCONST:
	case ICMD_LUSHRCONST:

	case ICMD_IADDCONST:
	case ICMD_ISUBCONST:
	case ICMD_IMULCONST:
	case ICMD_IMULPOW2:
	case ICMD_IDIVPOW2:
	case ICMD_IREMPOW2:
	case ICMD_IANDCONST:
	case ICMD_IORCONST:
	case ICMD_IXORCONST:
	case ICMD_ISHLCONST:
	case ICMD_ISHRCONST:
	case ICMD_IUSHRCONST:

/* 	case ICMD_IFEQ_ICONST: */
/* 	case ICMD_IFNE_ICONST: */
/* 	case ICMD_IFLT_ICONST: */
/* 	case ICMD_IFGE_ICONST: */
/* 	case ICMD_IFGT_ICONST: */
/* 	case ICMD_IFLE_ICONST: */

	case ICMD_INEG:
	case ICMD_INT2BYTE:
	case ICMD_INT2CHAR:
	case ICMD_INT2SHORT:
	case ICMD_LNEG:
	case ICMD_FNEG:
	case ICMD_DNEG:

	case ICMD_I2L:
	case ICMD_I2F:
	case ICMD_I2D:
	case ICMD_L2I:
	case ICMD_L2F:
	case ICMD_L2D:
	case ICMD_F2I:
	case ICMD_F2L:
	case ICMD_F2D:
	case ICMD_D2I:
	case ICMD_D2L:
	case ICMD_D2F:

	case ICMD_CHECKCAST:

	case ICMD_ARRAYLENGTH:
	case ICMD_INSTANCEOF:

	case ICMD_NEWARRAY:
	case ICMD_ANEWARRAY:

	case ICMD_GETFIELD:
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
		break;

		/* pop 0 push 1 */
	case ICMD_GETSTATIC:

	case ICMD_NEW:
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		break;

		/* pop many push any */

	case ICMD_INVOKESTATIC:
	case ICMD_INVOKESPECIAL:
	case ICMD_INVOKEVIRTUAL:
	case ICMD_INVOKEINTERFACE:
			INSTRUCTION_GET_METHODDESC(iptr,md);
			i = md->paramcount;
			while (--i >= 0) {
				liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
				src = src->prev;
			}
			if (md->returntype.type != TYPE_VOID)
				liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
			break;

	case ICMD_BUILTIN:
		bte = iptr->val.a;
		md = bte->md;
		i = md->paramcount;
		while (--i >= 0) {
			liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
			src = src->prev;
		}
		if (md->returntype.type != TYPE_VOID)
			liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		break;

	case ICMD_MULTIANEWARRAY:
		i = iptr->op1;
		while (--i >= 0) {
			liveness_set_stack(ls, b_index, g_iindex, src, sets, LV_GEN);
			src = src->prev;
		}
		liveness_set_stack(ls, b_index, g_iindex, dst, sets, LV_KILL);
		break;

	default:
		exceptions_throw_internalerror("Unknown ICMD %d during register allocation",
									   iptr->opc);
		return;
	} /* switch */
}

/*
 * 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:
 * vim:noexpandtab:sw=4:ts=4:
 */
