/* $Header:ropt.c 12.0$ */
/* $ACIS:ropt.c 12.0$ */
/* $Source: /ibm/acis/usr/src/lib/c2_ca/RCS/ropt.c,v $ */

#ifndef lint
static char *rcsid = "$Header:ropt.c 12.0$";
#endif

/* ROPT.C  -- ibm032 Optimizer */

#include "stdio.h"
#include "opt.h"
#include "error.h"
#include "inst.h"
#include "instps.h"


/*
     Optimize the assembly language output
     of the ibm032 Unix C Compiler.

     D. N. Smith,  October 1983; April/Oct 1984
     (c) 1984 by IBM; All Rights Reserved

     IBM Internal Use Only
*/


/* define storage for flags */
int Debug      = FALSE;
int Running    = TRUE;
int ShowMods   = FALSE;
int MoreToRead = TRUE;
int Trace      = FALSE;

int DoBALR       = FALSE;
int DoLoad       = FALSE;
int DoJumps      = TRUE;
int DoRegVar     = FALSE;
int DoMR         = TRUE;
int DoMisc       = TRUE;

/* TRUE for debug output from routine */
int DebugBALR    = FALSE;
int DebugLoad    = FALSE;
int DebugJumps   = FALSE;
int DebugRegVar  = FALSE;
int DebugMR      = FALSE;
int DebugMisc    = FALSE;
int DebugReadPgm = FALSE;

struct snode *Root = NULL;
struct snode *Tail = NULL;
struct snode *FunctLoc  = NULL;  /* pointer to _funct node */
struct snode *EFunctLoc = NULL;  /* pointer to _efunct node */

struct stats c;  /* reserve space */
int WhoRunning;  /* holds who_xxx for current phase      */
int ReadLineno;
int SeqnoWrong;  /* some change has make sequence
                           numbers invalid                      */

#define MAXFNLEN 60
char OptName[MAXFNLEN];
char SourceName[MAXFNLEN];

int RegType[16] = {
    REGTYP0,     REGTYP1,     REGTYP2,     REGTYP3,
    REGTYP4,     REGTYP5,     REGTYP6,     REGTYP7,
    REGTYP8,     REGTYP9,     REGTYP10,    REGTYP11,
    REGTYP12,    REGTYP13,    REGTYP14,    REGTYP15 };


main(argc, argv)
register char *argv[];
{
   int did1fcn = FALSE;

   Running = TRUE;
   parms(argc,argv);

   /* if filenames on command line use them */
   if( SourceName[0] )
      freopen( OptName, "r", stdin );
   if( OptName[0] )
      freopen( SourceName, "r", stdout );

#if SYSTEM==CMS
   /* on VM (at least) make sure stderr goes to STDERR */
   freopen( "stderr", "w", stderr );
#endif

   while( Running && MoreToRead)  {
      Debug = DebugReadPgm;
      ReadProgram();   if(!Running)  break;
      Debug = 0;
      FunctLoc  = ScanFor(i_funct,  Root);
      EFunctLoc = ScanFor(i_efunct, Root);
      if( FunctLoc && EFunctLoc )  {  /* if _funct then optimize */
         MakeLabRef();    if(!Running)  break;
         Stats();
         optimize();      if(!Running)  break;
         did1fcn = TRUE;
         }
      WriteProgram();  if(!Running)  break;
      DropLabRef();
   }
   if( ShowMods )   Summarize();
/* (ef) 2/13/86 -- warning message annoyed people.      */
/*   if( ! did1fcn )   Error( E_NOFUNCTIONS, "", NULL );*/
   fclose(stdout);
   fclose(stdin);
   exit(0);
}

/***********************************************************/

/* collect statistics on program as a whole */
static Stats()
{
   register struct snode *sn;

   sn = Root;
   while ( (sn = sn->next) != NULL)  {
      if( LOAD(sn) && !REFIMMED(sn) ) c.loads    += 1;  else
      if( STORE(sn) )                 c.stores   += 1;  else
      if( OPNUMBER(sn) == i_mr )      c.mrs      += 1;  else
      if( OPNUMBER(sn) == i_balr)     c.balrs    += 1;  else
      if( BRANCH(sn) )                c.branches += 1;
      }
}

/***********************************************************/

struct phaseTable {
   int *allow;        /* pointee is TRUE when phase allowed */
   int *debug;        /* pointee is TRUE to debug phase     */
   VOID (*funct)();   /* pointer to routine to run phase    */
   int who;           /* flag naming phase                  */
   char *name;        /* name of the phase                  */
   };

/* the following table defines when to run what phase and
   whether debuging information is to be output.  It is
   interpreted by the routine optimize() which follows       */

extern VOID  JumpOpt(),   MiscOpt(),   LoadOpt(),   RegVarOpt(),
             MrOpt(),     LoadOpt(),   MrOpt(),     BalrOpt();

#define MAXPHASE 8
static struct phaseTable phase[MAXPHASE] = {
/*
   do when    turn on debug funct name phase flag  phase name
   -------    ------------- ---------- ----------  ----------*/
   &DoJumps,  &DebugJumps,  JumpOpt,   who_1jump,  "Jumps1",
/* &DoMisc,   &DebugMisc,   MiscOpt,   who_misc,   "Misc", */
   &DoLoad,   &DebugLoad,   LoadOpt,   who_1load,  "Load1",
   &DoRegVar, &DebugRegVar, RegVarOpt, who_regvar, "RegVar",
   &DoMR,     &DebugMR,     MrOpt,     who_1mr,    "MR1",
   &DoLoad,   &DebugLoad,   LoadOpt,   who_2load,  "Load2",
   &DoMR,     &DebugMR,     MrOpt,     who_2mr,    "MR2",
   &DoJumps,  &DebugJumps,  JumpOpt,   who_2jump,  "Jumps2",
   &DoBALR,   &DebugBALR,   BalrOpt,   who_balr,   "BALR"  };

/*
Notes on ordering of phases (in the order the phases are run):

1)  Do jump opt first since it removes labels which
    otherwise obscure other possible optimizations.

2)  Next do Misc opts like: move stack bumps to prolog & epilog
    (Currently not used since calls to $$MUL also bump the stack;
    if $$MUL called in a parameter list then the partly built
    stack is messed up.)

3)  Try to delete redundant loads (LoadOpt).

4)  Add register variables next; do after LoadOpt since
    LoadOpt can delete memory references.

5)  Remove MR, ADDI, & SUBI instrctions added by above passes.

6)  Try to delete redundant loads again.

7)  Remove MR added by second LoadOpt.

8)  Do jump opt again since sometimes deletions uncover new
    opportunities.

9)  BALR opt should be last since it can move otherwise
    optimizable instructions behind a BALRX.
*/


/* run the optimization phases */
static optimize()
{
   int n = -1;

   while( ++n < MAXPHASE )  {
      if( *phase[n].allow )  {
         Debug = *phase[n].debug;
         WhoRunning = phase[n].who;
         if(DEBUG1)  fprintf(stderr,
            "\n\nPhase %s starting ---\n", phase[n].name );
         (*phase[n].funct) ();
         if(DEBUG1)  CheckAllNodes();
         if( !Running )  return;
         }
      }
}


/***********************************************************/


/* Process parameters */

static parms(argc,argv)
register char *argv[];
{
   register char *cp1, *end;
   char *rindex();
   int dlvl;

   if( argc==1 && argv[0][0]=='?')
      { usage(); exit(1); }

   argv++;
   while (--argc > 0) {
      if (argv[0][0] != '-')  {
         if( SourceName[0]==0 ) strcpy( SourceName, argv[0] );  else
         if( OptName[0]==0 )    strcpy( OptName,    argv[0] );  else
            Error( E_OPTION, argv[0], NULL );
         }
      else
         switch (argv[0][1]) {

           case 'A':
           case 'a':   DoLoad    = FALSE;
                       DoJumps   = TRUE;
                       DoBALR    = FALSE;
                       DoRegVar  = FALSE;
                       DoMR      = TRUE;
                       DoMisc    = TRUE;
                       break;

           case 'B':
           case 'b':   DoBALR = !DoBALR;
                       break;

           case 'E':  /* -e and -E also mean -D  (for CMS) */
           case 'e':
           case 'D':   setDebug( DBGLEV2, argv[0][2] );  break;
           case 'd':   setDebug( DBGLEV1, argv[0][2] );  break;

           case 'J':
           case 'j':   DoJumps = !DoJumps;
                       break;

           case 'L':
           case 'l':   DoLoad = !DoLoad;
                       break;

           case 'M':
           case 'm':   DoMR = !DoMR;
                       break;

           case 'N':
           case 'n':   DoLoad    = FALSE;
                       DoJumps   = FALSE;
                       DoBALR    = FALSE;
                       DoRegVar  = FALSE;
                       DoMR      = FALSE;
                       DoMisc    = FALSE;
                       break;

           case 'R':
           case 'r':   DoRegVar = !DoRegVar;
                       break;

           case 'S':
           case 's':   ShowMods = !ShowMods;
                       break;

           case 'T':
           case 't':   Trace = !Trace;
                       break;

           case 'X':
           case 'x':   DoMisc    = !DoMisc;
                       break;

           default:    Error(E_OPTION,argv[0]);
        }

      argv++;
   }
}

setDebug( level, phase )
register int level, phase;
{
   switch( phase )  {
      case 'a': /* all phases to be debugged */
      case 0:   DebugBALR    = level;
                DebugReadPgm = level;
                DebugJumps   = level;
                DebugLoad    = level;
                DebugMR      = level;
                DebugRegVar  = level;
                DebugMisc    = level;
                break;
      case 'b': DebugBALR    = level;   break;
      case 'i': DebugReadPgm = level;   break;
      case 'j': DebugJumps   = level;   break;
      case 'l': DebugLoad    = level;   break;
      case 'm': DebugMR      = level;   break;
      case 'r': DebugRegVar  = level;   break;
      case 'x': DebugMisc    = level;   break;
      default:  break;
      }
}


/***********************************************************/

#define F(s)  fprintf(stderr,s)

usage()
{
   F( " \n");
   F( "Usage:  ropt [flags] \n");
   F( " \n");
   F( "stdin contains a ibm032 Assembly language program produced by RUCC.\n");
   F( "It is optimized.  The output is placed in stdout. \n");
   F( " \n");
   F( "Flags are:\n");
   F( "   -a  do:     All optimizations (the default)\n");
   F( "   -n          No optimizations\n");
   F( "   -b  toggle: BALR->BALRX optimization\n");
   F( "   -j          Branch (Jump) optimization\n");
   F( "   -l          Load optimization\n");
   F( "   -m          MR optimization\n");
   F( "   -r          Register variable optimization\n");
   F( "   -x          Misc. optimizations\n");
   F( "   -d  set on: Debug mode for all phases; \n");
   F( "   -dx         Debug mode, selective; x is:\n");
   F( "                 a  All phases               l  Load Optimization Phase \n");
   F( "                 b  BALR Optimization Phase  m  MR Optimization Phase   \n");
   F( "                 i  During Input of program  r  Register Variable Phase \n");
   F( "                 j  Jump Optimization Phase  x  Misc. Optimizations Phase\n");
   F( "   -s  toggle: output Shows changes to statements (off by default)\n");
/* F( "   -t          Trace mode; comments illustrate processing done\n");  */
   F( " \n");
}
