/*--------------------------------------------------------------*/
/*

 TinC - Tiny, in C
 tinct13.c
 Jan 2005, Peter F. Gray

 Extrapolated from Tiny 1.1 coded by Jack W. Crenshaw, in Pascal,
 as part of Jack's "Let's Build A Compiler" tutorial.

*/
/*--------------------------------------------------------------*/

#include "tinc13.h"

#include <stdio.h>

extern FILE *ip[MAXINCL];		// input (source) file
extern int  slevel;			// source level (for #includes)
extern long linenumber[MAXINCL];	// current source line number
extern char sname[MAXINCL][STRTMPSIZE];	// current source name

extern FILE *op;		// output file
extern FILE *op1;		// output file 1
extern FILE *op2;		// output file 2
extern FILE *tr;		// trace file

extern int tracing;		// tracing flag
extern int opfile1;		// output file flag 1
extern int opfile2;		// output file flag 2
extern int genreguse;		// generate reg use pcodes
extern int genreguse2;		// as above, but for the final code

extern int warn;		// warning flag
extern int wcount;		// warning count

extern int interactive;		// interactive flag
extern int trindent;		// trace indent level
extern long totallines;		// total number of source lines

extern int endofallsource;	// no more source to process
extern int ifdeflevel;		// #ifdef level

extern int section;		// section type
extern int optlevel;		// optimization level

/*--------------------------------------------------------------*/
/* Symbol Tables etc	*/

extern Symbol GS_Name[MAXGLOBALS];	// global symbol table (name)
extern int    GS_Type[MAXGLOBALS];	// data type
extern int    GS_Size[MAXGLOBALS];	// size (in 'data type' units)

extern int    GS_ParamCount[MAXGLOBALS];
extern int    GS_ParamType[MAXGLOBALS][MAXPARAMS];

extern Symbol LS_Name[MAXLOCALS];	// local symbol table (name)
extern int    LS_Type[MAXLOCALS];	// data type
extern int    LS_Size[MAXLOCALS];	// size (in 'data type' units)

extern long NumGlobals;			// number of globals used
extern long NumLocals;			// number of locals used
extern int  TotLocSize;			// space required for locals

extern Symbol DS_Name[MAXDEFS];		// definition table (name)
extern Symbol DS_Value[MAXDEFS];	// definition table (value)
extern long NumDefs;			// number of Defs used

extern Symbol PS_Name[MAXPARAMS];	// parameter list (name)
extern int    PS_Type[MAXPARAMS];	// data type
extern long NumParams;

extern Symbol CS_Name[MAXCONSTS];	// constant symbol table (name)
extern int    CS_Type[MAXCONSTS];	// data type
extern char   CS_Value[MAXCONSTS][MAXCONSTSIZE];
extern int    CCount;			// # of autogen constants
extern int    NumConsts;		// total # of constants

extern long   LCount;			// label Count


/*--------------------------------------------------------------*/
/* Staging Buffer */

extern struct sbuff_struct {
  int pcode;
  char s1[MAXLINESIZE];		// allow for #inline
  char s2[MAXSYMSIZE];
  char s3[MAXSYMSIZE];
  int  t1;
  int  t2;
} sbuff[MAXSTAGE];

extern long int sbufftop;		// top of the staging buffer
extern long int sbuffopt;		// pcode reduction by optimizer


/*--------------------------------------------------------------*/
/* Register Control */

extern struct vreg_struct {
  char  inuse;			// reg in use flag
  int allowed;			// data types this reg can be used for
  int datatype;   		// data type being held in reg
  char type;			// type of reg (NORMAL or STACK)
} vreg[MAXREGS];

extern char * DataTypeDesc (int);

extern void EmitLn();
extern void EmitLn_notab();
extern void Abort();
extern void EmitChar(char);
extern void Stage();
extern void Trace();

/*--------------------------------------------------------------*/
/* Generate code for a Staging Buffer item */

void Gen_Item (int pcode, char *s1, char *s2, char *s3,
                      int t1, int t2)
{
  char strtmp1[STRTMPSIZE], dt1[TYPEDESCSIZE], dt2[TYPEDESCSIZE];
  int tmp, tmp2 = 0;

  strcpy(dt1,DataTypeDesc(t1));
  strcpy(dt2,DataTypeDesc(t2));

  if (pcode < 0) return;
  switch (pcode) {
  case PC_LABEL:
    EmitLn("");
    sprintf(strtmp1,"%s:",s1);
    EmitLn_notab(strtmp1);
    break;
  case PC_BRANCH:
    sprintf(strtmp1,"Branch to %s",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_BRANCH_FALSE:
    sprintf(strtmp1,"Branch to %s if Reg%s (%s) false",s1,s2,dt1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_HEADER:
    EmitLn("Header");
    break;
  case PC_PROLOG:
    sprintf(strtmp1,"Prolog (%s)",s1);
    EmitLn(strtmp1);
    break;
  case PC_EPILOG:
    sprintf(strtmp1,"Epilog (%s)",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_ALLOCATE:
    sprintf(strtmp1,"Allocate %s %s",s1,s2);
    EmitLn(strtmp1);
    break;
  case PC_GROWSTACK:
    sprintf(strtmp1,"Grow Stack for '%s' (%s of type %s)",s1,s2,s3);
    EmitLn(strtmp1);
    break;
  case PC_SHRINKSTACK:
    sprintf(strtmp1,"Shrink Stack %s (%s)",s1,s2);
    EmitLn(strtmp1);
    break;

  case PC_MOVE_REG:
    sprintf(strtmp1,"Move Reg%s (%s) to Reg%s (%s) ",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_ADD_REG:
    sprintf(strtmp1,"Add Reg%s (%s) to Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_SUB_REG:
    sprintf(strtmp1,"Sub Reg%s (%s) from Reg%s (%s) (result in Reg%s)",s1,dt1,s2,dt2,s2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_VAR_REG:
    sprintf(strtmp1,"Move var '%s' (%s) to Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_LVAR_REG:
    sprintf(strtmp1,"Move local var '%s' (%s) (offset %s) to Reg%s (%s)",s1,dt1,s2,s3,dt2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_REG_VAR:
    sprintf(strtmp1,"Move Reg%s (%s) to var '%s' (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_REG_LVAR:
    sprintf(strtmp1,"Move Reg%s (%s) to local var '%s' (%s) (offset %s)",s1,dt1,s2,dt2,s3);
    EmitLn(strtmp1);
    break;
  case PC_MUL_REG:
    sprintf(strtmp1,"Multiply Reg%s (%s) by Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_DIV_REG:
    sprintf(strtmp1,"Divide Reg%s (%s) by Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_CLEAR_REG:
    sprintf(strtmp1,"Clear Reg%s (%s)",s1,dt1);
    EmitLn(strtmp1);
    break;
  case PC_COMPARE_REG:
    sprintf(strtmp1,"Compare Reg%s (%s) to Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_AND_REG:
    sprintf(strtmp1,"AND Reg%s (%s) with Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_OR_REG:
    sprintf(strtmp1,"OR Reg%s (%s) with Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_XOR_REG:
    sprintf(strtmp1,"XOR Reg%s (%s) with Reg%s (%s)",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_NOT_REG:
    sprintf(strtmp1,"NOT Reg%s (%s)",s1,dt1);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_A_REG:
    sprintf(strtmp1,"Move address of var '%s' to Reg%s (%s)",s1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_AL_REG:
    sprintf(strtmp1,"Move address of local var '%s' (%s) (offset %s) to Reg%s (%s)",
      s1,dt1,s3,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_ADJUST_REG:
    sprintf(strtmp1,"Adjust Reg%s (%s) by data size of '%s' (%s)",s1,dt1,s2,s3);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_I_REG:
    sprintf(strtmp1,"Move [Reg%s] to Reg%s (%s)",s1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_REG_I:
    sprintf(strtmp1,"Move Reg%s (%s) to [Reg%s] (%s)",s1,dt1,s2,s3);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_CONST_REG:
    sprintf(strtmp1,"Move constant '%s' to Reg%s (%s)",s1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_SET_REG_EQ:
    sprintf(strtmp1,"Set Reg%s if EQ",s1);
    EmitLn(strtmp1);
    break;
  case PC_SET_REG_NE:
    sprintf(strtmp1,"Set Reg%s if NE",s1);
    EmitLn(strtmp1);
    break;
  case PC_SET_REG_LT:
    sprintf(strtmp1,"Set Reg%s if LT",s1);
    EmitLn(strtmp1);
    break;
  case PC_SET_REG_GT:
    sprintf(strtmp1,"Set Reg%s if GT",s1);
    EmitLn(strtmp1);
    break;
  case PC_SET_REG_GE:
    sprintf(strtmp1,"Set Reg%s if GE",s1);
    EmitLn(strtmp1);
    break;
  case PC_SET_REG_LE:
    sprintf(strtmp1,"Set Reg%s if LE",s1);
    EmitLn(strtmp1);
    break;
  case PC_INLINE:
    sprintf(strtmp1,"#inline ");
    strcat(strtmp1,s1);
    EmitLn_notab(strtmp1);
    break;
  case PC_GETREG:
    if (genreguse) {
      sprintf(strtmp1,"+Reg%s",s1);
      EmitLn_notab(strtmp1);
    }
    break;
  case PC_FREEREG:
    if (genreguse) {
      sprintf(strtmp1,"-Reg%s",s1);
      EmitLn_notab(strtmp1);
    }
    break;
  case PC_SHIFTRIGHT:
    sprintf(strtmp1,"Shift Reg%s (%s) right by Reg%s (%s) bits",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_SHIFTLEFT:
    sprintf(strtmp1,"Shift Reg%s (%s) left by Reg%s (%s) bits",s1,dt1,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_SETFP:
    EmitLn("Set the Frame Pointer");
    break;
  case PC_PUSHFP:
    EmitLn("Push the Frame Pointer");
    break;
  case PC_POPFP:
    EmitLn("Pop the Frame Pointer");
    break;
  case PC_DOCALL:
    sprintf(strtmp1,"Call %s",s1);
    EmitLn(strtmp1);
    break;
  case PC_CALLRET:
    EmitLn("Return from routine");
    break;
  case PC_INTRET:
    EmitLn("Return from interrupt");
    break;
  case PC_MOVE_PVAR_REG:
    sprintf(strtmp1,"Move param '%s' (%s) (roffset %s) to Reg%s (%s)",s1,dt1,s2,s3,dt2);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_REG_PVAR:
    sprintf(strtmp1,"Move Reg%s (%s) to param '%s' (%s) (roffset %s)",s1,dt1,s2,dt2,s3);
    EmitLn(strtmp1);
    break;
  case PC_MOVE_AP_REG:
    sprintf(strtmp1,"Move address of param '%s' (%s) (roffset %s) to Reg%s (%s)",
      s1,dt1,s3,s2,dt2);
    EmitLn(strtmp1);
    break;
  case PC_PUSH_REG:
    sprintf(strtmp1,"Push Reg%s (%s) (%s)",s1,dt1,dt2);
    EmitLn(strtmp1);
    break;
  case PC_PROGSEC:
    EmitLn("");
    EmitLn("Program Section");
    break;
  case PC_DATASEC:
    EmitLn("");
    EmitLn("Data Section");
    break;
  case PC_CONST:
    sprintf(strtmp1,"Const %s (%s)",s1,dt1);
    EmitLn_notab(strtmp1);
    tmp = IsConst(s1);
    while (CS_Value[tmp-1][tmp2]) {
      EmitChar(CS_Value[tmp-1][tmp2]);
      tmp2++;
    }
    EmitLn("");
    break;

// level 1 optimization pcodes

  case PC_BRANCH_EQ:
    sprintf(strtmp1,"Branch to %s if EQ",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_BRANCH_NE:
    sprintf(strtmp1,"Branch to %s if NE",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_BRANCH_LT:
    sprintf(strtmp1,"Branch to %s if LT",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_BRANCH_GT:
    sprintf(strtmp1,"Branch to %s if GT",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_BRANCH_GE:
    sprintf(strtmp1,"Branch to %s if GE",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;
  case PC_BRANCH_LE:
    sprintf(strtmp1,"Branch to %s if LE",s1);
    EmitLn(strtmp1);
    EmitLn("");
    break;

// unknown pcode

  default:
    sprintf(strtmp1,"Unknown pcode (%d)",pcode);
    Abort(strtmp1);
    break;
  }
}


/*--------------------------------------------------------------*/
/* Register Control Initialization */

void InitRegs()
{
  int tmp;

  for (tmp=0; tmp<MAXREGS; tmp++) {
    vreg[tmp].inuse = FALSE;
    vreg[tmp].datatype = ' ';
    vreg[tmp].type = REG_NORMAL;
  }
  for (tmp=REG_STACKSTART; tmp<MAXREGS; tmp++) {
    vreg[tmp].type = REG_STACK;
    vreg[tmp].allowed = DT_ALL;		// all types
  }
  vreg[0].allowed = DT_XCHAR;		// all char types
  vreg[1].allowed = DT_XCHAR;		// all char types
  vreg[2].allowed = DT_XINT | DT_XPTR;	// all ints and ptrs
  vreg[3].allowed = DT_XINT | DT_XPTR;	// all ints and ptrs
}


/*--------------------------------------------------------------*/
/* Write Header Info */

void Header()
{
  Stage (PC_HEADER,"","","",0,0);
}


/*--------------------------------------------------------------*/
/* Write Trailer Info */

void Trailer()
{
  int tmp;

  trace (' ',"Trailer","","","");
  for (tmp=0; tmp<NumConsts; tmp++)
    Stage (PC_CONST,CS_Name[tmp],"","",CS_Type[tmp],0);
}


/*--------------------------------------------------------------*/
/* Get the next 'real' pcode */

long int NextPcode (long int i)
{
  long int j;
  for (j=i+1; j<sbufftop; j++)
    if (sbuff[j].pcode >= PC_FIRST_REAL) return j;
  return sbufftop;			// i.e. no more
}


/*--------------------------------------------------------------*/
/* Staging buffer optimizer (code according to target) */

void Optimize ()
{
  long int i,j;

  if (optlevel >= 1) {	// optimization level 1 (all targets)

    for (i=0; i<sbufftop; i++) {
      if ((j=NextPcode(i))==sbufftop) { i=sbufftop; break; }

// set reg <condition>; branch false; -> branch <reverse condition>

      if (sbuff[i].pcode >= PC_SET_REG_EQ &&
          sbuff[i].pcode <= PC_SET_REG_LE &&
          sbuff[j].pcode == PC_BRANCH_FALSE) {
	sbuff[j].pcode = -sbuff[j].pcode;
        strcpy(sbuff[i].s1,sbuff[j].s1);
        switch (sbuff[i].pcode) {
        case PC_SET_REG_EQ:
	  sbuff[i].pcode = PC_BRANCH_NE; break;
        case PC_SET_REG_NE:
	  sbuff[i].pcode = PC_BRANCH_EQ; break;
        case PC_SET_REG_LT:
	  sbuff[i].pcode = PC_BRANCH_GE; break;
        case PC_SET_REG_GT:
	  sbuff[i].pcode = PC_BRANCH_LE; break;
        case PC_SET_REG_GE:
	  sbuff[i].pcode = PC_BRANCH_LT; break;
        case PC_SET_REG_LE:
	  sbuff[i].pcode = PC_BRANCH_GT; break;
        }
        sbuffopt++;
        if ((i=NextPcode(i-1))==sbufftop) break;
        if ((j=NextPcode(i))==sbufftop) { i=sbufftop; break; }
      } // end of rule

// non-zero 'while' loop - simply remove the pcodes

      if (sbuff[i].pcode == PC_MOVE_CONST_REG &&
          sbuff[j].pcode == PC_BRANCH_FALSE && 
          !strcmp(sbuff[i].s2,sbuff[i].s2) &&
          strcmp(sbuff[i].s1,"0") ) {
        sbuffopt += 2;
	sbuff[i].pcode = -sbuff[i].pcode;
	sbuff[j].pcode = -sbuff[j].pcode;
        if ((i=NextPcode(i-1))==sbufftop) break;
        if ((j=NextPcode(i))==sbufftop) { i=sbufftop; break; }
      } // end of rule

    } // end of 'for'

  } // end of optimization level 1

}


/*--------------------------------------------------------------*/