/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   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 of the License, 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.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
/*
 * icode peep optimizer
 *
 * Does branch optimizationns of various sorts
 */
#include        <stdio.h>
#include 				"utype.h"
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include				"iexpr.h"
#include				"iopt.h"

extern int firstlabel;
extern int prm_optdead;
extern long nextlabel;

static QUAD **golist;	/* list of goto statements */

static void scan_gotos(QUAD *head)
/*
 * make a list of goto statements
 */
{
	while (head) {
		switch(head->dc.opcode) {
			case i_goto:
			case i_jc:
			case i_jnc:
			case i_jbe:
			case i_ja:
			case i_je:
			case i_jne:
			case i_jge:
			case i_jg:
			case i_jle:
			case i_jl:
			case i_dc:
			case i_coswitch:
				golist[head->dc.label-firstlabel] = head;
				break;
		}
		head = head->fwd;
	}
}
static void kill_unreach(QUAD *head)
/*
 * kill unreachable code
 */
{
	while (head && head->fwd && head->fwd->dc.opcode != i_label) {
      if (head->fwd->dc.opcode != i_block) {
         head->fwd->fwd->back = head;
         head->fwd = head->fwd->fwd;
      } else
         head = head->fwd ;
	}
}
void peepini(void)
{
}
static void kill_brtonext(QUAD *head)
/*
 * branches to the next statement get wiped
 */
{
		QUAD *temp;
		while (TRUE) {
start:
			switch(head->dc.opcode) {
				case i_goto:
				case i_jc:
				case i_jnc:
				case i_jbe:
				case i_ja:
				case i_je:
				case i_jne:
				case i_jge:
				case i_jg:
				case i_jle:
				case i_jl:
					temp = head->fwd;
					while (temp->dc.opcode == i_label || temp->dc.opcode == i_line || temp->dc.opcode == i_block || temp->dc.opcode == i_passthrough) {
						if (temp->dc.opcode == i_label && temp->dc.label == head->dc.label) {
							head->fwd->back = head->back;
							head->back->fwd = head->fwd;
							head = head->back;
							while (head && head->back && (head->dc.opcode == i_line || head->dc.opcode == i_block || head->dc.opcode == i_label || head->dc.opcode == i_passthrough))
								head = head->back;
							goto start;
						}
						temp = temp->fwd;
					}
				default:
					return;
			}
		}
}
void kill_labeledgoto(QUAD *head)
/*
 * if any goto goes to a label which is immediately followed by a goto,
 * replaces the original goto label with the new label annd possibly
 * get rid of the labeled goto if it is preceded by another goto
 */
{
	QUAD *newhead,*back,*gotoitem;
	newhead = head->fwd;
	/* look for following goto */
	while (newhead && (newhead->dc.opcode == i_line || newhead->dc.opcode == i_block || newhead->dc.opcode == i_passthrough))
		newhead = newhead->fwd;
	if (newhead->dc.opcode != i_goto)
		return;
	/* got one, look for the ref to it */
	gotoitem = golist[head->dc.label - firstlabel];
	if (!gotoitem)
		return;
	golist[head->dc.label - firstlabel] = 0;
	gotoitem->dc.label = newhead->dc.label;
	/* got one- kill unreachable code and a possible brtonext that was genned */
	if (gotoitem->dc.opcode == i_goto) {
		kill_unreach(gotoitem);
		kill_brtonext(gotoitem);
	}
	/* now see if was preceded by a goto */
	back = head->back;
	while (back && (back->dc.opcode == i_line || back->dc.opcode == i_block || back->dc.opcode == i_passthrough))
		back = back->back;
	/* yep- get rid of the label and the goto */
	if (back->dc.opcode == i_goto || back->dc.opcode == i_dc) {
		head->back->fwd = newhead->fwd;
		newhead->fwd->back = head->back;
	}
}
void kill_jumpover(QUAD *head)
/*
 * Conditionnal jumps over gotos get squashed here
 */
{
	int newtype;
	QUAD *newhead=head->fwd;
	/* skip to next statement */
	while (newhead && newhead->dc.opcode == i_block || newhead->dc.opcode == i_line || newhead->dc.opcode == i_passthrough)
		newhead=newhead->fwd;
	if (!newhead)
		return;
	if (newhead->dc.opcode == i_goto) {
		/* if it was a goto, swap the conditional type and 
		 * put the new label in the goto statement 
		 */
		head->dc.label = newhead->dc.label;
		head->fwd = newhead->fwd;
		newhead->fwd->back = head;
		switch(head->dc.opcode) {
			case i_jc:
				newtype = i_jnc;
				break;
			case i_jnc:
				newtype = i_jc;
				break;
			case i_jbe:
				newtype = i_ja;
				break;
			case i_ja:
				newtype = i_jbe;
				break;
			case i_je:
				newtype = i_jne;
				break;
			case i_jne:
				newtype = i_je;
				break;
			case i_jge:
				newtype = i_jl;
				break;
			case i_jg:
				newtype = i_jle;
				break;
			case i_jle:
				newtype = i_jg;
				break;
			case i_jl:
				newtype = i_jge;
				break;
		}
		head->dc.opcode = newtype;
	}
}
void peep_icode(QUAD *head)
/*
 * ICODE peep main routine
 */
{
	golist = oalloc(sizeof(QUAD *)*(nextlabel-firstlabel));
	scan_gotos(head);
	while (head) {
		switch (head->dc.opcode) {
			case i_goto:
				kill_unreach(head);
				kill_brtonext(head);		
				break;
			case i_jc:
			case i_jnc:
			case i_jbe:
			case i_ja:
			case i_je:
			case i_jne:
			case i_jge:
			case i_jg:
			case i_jle:
			case i_jl:
				kill_brtonext(head);		
				kill_jumpover(head);
				break;
			case i_label:
				kill_labeledgoto(head);
				break;
		}
		head = head->fwd;
	}
}