/************************************************************************/
/*                                                                      */
/* Copyright (c) 1988 -                                                 */
/*     The Rector and Visitors of the University of Virginia            */
/*                                                                      */
/* This code may not be distributed further without permission          */
/*      from the University of Virginia.                                */
/* This code is distributed WITHOUT ANY WARRANTY.  No claims are        */
/*      made as to whether it serves any particular purpose or even     */
/*      works at all.                                                   */
/*                                                                      */
/************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "peep.h"
#include "error.h"
#define MAXWINDOW 25

linelist s[MAXWINDOW];
linelist *t = s;
linelist *h = (linelist*)0;
linelist *lp = (linelist*)0;
char arg[MAXARGS][SIZE];
int windowsize;
extern int opeep;
extern int fnum[], currlevel;
extern int gpregused, fpregused;

/*
 * print out the instruction and check for the use of registers to set
 * the register mask
 */
void
putinst(p)
register linelist *p;
{
	register char *cp;
	register int c, n;

	/*
	 * output line of assembly code.  Look gp and fp register usage.
	 * If fp pseudo register encountered, use stack variable instead.
	 */
	for (cp = p->line; c = *cp++; ) {
		if (c == '%') {
			if (*cp == 'f' && *(cp+1) != 'p') {
				n = *(cp+1) - '0';
				if (n > 2) {  /* pseudo fp register */
					printf (".T%d+%d(%%fp)", fnum[currlevel], n << 3);
					cp += 2;
					if (n >= fpregused)
						fpregused = n + 1;
					continue;
				}
			}
			else if (*cp == 'r') {
				n = 9 - (*(cp+1) - '0');
				if (n < 7 && n > gpregused)
					gpregused = n;
			}
		}
		putchar(c);
	}
	putchar ('\n');
}
	
/*
 * The instructions are contained in a circular window.  When the window
 * overflows then the oldest instruction is output.
 * If the optimizer flag is set optimize the instruction, output the 
 * instruction, wrap around the window if necessary.
 */
void
as_emit1()
{
	void peep();

	if (opeep) {
		t->prev = lp;
		t->next = NULL;
		if (lp)
			lp->next = t;
		else
			h = t;
		peep();
		lp = t;
		t = t+1 >= s+MAXWINDOW ? s : t+1;
		windowsize = h <= lp ? lp-h : lp+MAXWINDOW-h;
		if (windowsize >= MAXWINDOW-1) {
			putinst(h);
			h = h->next;
			h->prev = NULL;
		}
	}
	else
		putinst(t);
}

/*
 * Output any instructions in the peephole window, and output the instruction.
 */

void
as_emit2()
{
	register linelist *r;

	if (h && opeep) {
		for (r = h; r; r = r->next)
			putinst(r);
	}
	putinst(t);
	if (h && opeep) {
		t = s;
		lp = h = NULL;
	}
}

/*
 * The instruction is as_emitted before the last instruction.
 * (Note only applies when peephole optimization is used.)
 * An example is spilling r0 or r1 into another register before a call.
 */
void
as_emit0()
{
	char line[SIZE];
	void peep();
	extern int stmtno;

	if (!opeep) {
		strcpy(err_text, "as_emit0 used without peephole optimization");
		err_msg(E_INTERNAL, E_CURLINE);
	}
	else if (lp) {
		strcpy (line, t->line);
		strcpy (t->line, lp->line);
		strcpy (lp->line, line);
		peep ();
	}
	as_emit1 ();
}
