/* AEC, the AE Compiler.
   Copyright (C) 1989, 1990 by James R. Larus (larus@cs.wisc.edu)

   AE and AEC are 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 1, or (at your option) any
   later version.

   AE and AEC are 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 GNU CC; see the file COPYING.  If not, write to James R.
   Larus, Computer Sciences Department, University of Wisconsin--Madison,
   1210 West Dayton Street, Madison, WI 53706, USA or to the Free
   Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */


/* $Header: /var/home/larus/AE/AEC/RCS/aec.c,v 2.0 90/02/09 17:23:41 larus Exp Locker: larus $ */


/* Syntax is:
   aec -x a.out -o aec.out {-q, -d} f1.sma ... fn.sma

   -x a.out	(Mandatory)	Executable:  Name of the executable file a.out.
   -o aec.out	(Mandatory)	Output:  Name of the output file.
   -q		(Optional)	Quiet: do not write information.
   -d		(Optional)	Debug: produce safer, but slower code.
   -m		(Optional)	Multiple inst.: treat consecutive inst.
				together
 */


#include <stdio.h>
#include <strings.h>
#include <varargs.h>
#include "config.h"
#include "rtl.h"
#include "ae.h"
#include "ae-machine.h"
#include "aec.h"

#ifdef ECOFF_AOUT
#include <filehdr.h>
#include <syms.h>
#include <ldfcn.h>
#endif
#ifdef BSD_AOUT
#include <nlist.h>
#endif


#define streq(s1,s2) !strcmp((s1),(s2))


struct symbol
{
  struct symbol *next;		/* Next structure in the bucket.  */
  char *name;			/* Name. */
  int used_in_call;		/* Non-zero if symbol referenced in call */
  int schema_seen_p;		/* Non-zero if schema processed. */
  unsigned long address;	/* Address in core. */
  int addr_missing_warning;	/* Non-zero if complained about unknown addr */
};

/* From rtl.c: */
#define DEF_MACHMODE(SYM, NAME, CLASS, SIZE, UNIT, WIDER)  \
  (SIZE*UNITS_PER_WORD+3)/4,

int mode_size[] = {
#include "machmode.def"
};


void add_hash ();
void annotate_loop_edges ();
char *arg_out ();
void block_end ();
void block_end_cjump ();
void block_end_jump ();
void block_start ();
void block_start_target ();
void call_indirect_inst ();
void call_inst ();
void copy_file_to_out_file ();
void end_block_common ();
void fatal_error ();
void free_list ();
void function_end ();
void function_start ();
void indirect_call_code ();
void initialize_scanner ();
void issue_instruction ();
struct symbol *lookup_symbol ();
void make_dummy_function ();
void non_fatal_error ();
void output_preface ();
void output_reg_decl ();
void output_set_value ();
int nlist_addr_cmp ();
void printf_out ();
void report_statistics ();
void schema_for_absent_functions ();
char *string_cat ();
void switch_error ();
unsigned long symbol_address ();
unsigned long symbol_address_or_die ();
unsigned long symbol_address_or_warn ();
void uneventful_inst ();
void yyerror ();
int yyparse ();


void bzero ();
void exit ();
void free ();
void fprintf ();
void fclose ();
int fread ();
void fwrite ();
char *malloc ();


char *input_file_name;	/* Input file name */
static char *executable_file_name = NULL; /* Executable file name */

static FILE *out_file = NULL;	/*  */


#ifdef ECOFF_AOUT
LDFILE *ex_file;
#endif

static int indirect_call_seen = 0; /*  */


#define GLOBAL_HASH_TBL_SIZE 8191

static struct symbol *global_hash_tbl [GLOBAL_HASH_TBL_SIZE];


/* Counters to collect statistics: */

static int quiet = 0;		/* Zero => statistics printed */
static int debug = 0;		/* Non-zero => safer and slower code */
static int multiple_inst = 0;	/* Non-zero => multiple insts together */

static int function_cnt = 0;	/* Number of functions */
static int dummy_function_cnt = 0; /* Number of dummy functions */
static int block_cnt = 0;	/* Number of basic blocks */
static int target_block_cnt = 0; /* Number of blocks that are targets */
static int fall_block_cnt = 0;	/* Number of blocks that fall thru */
static int cjump_block_cnt = 0;	/* Number of blocks that end with cjump */
static int jump_block_cnt = 0;	/* Number of blocks that end with jump */
static int loop_cnt = 0;	/* Number of loops */
static int inst_cnt = 0;	/* Number of instructions */
static int store_cnt = 0;	/* Number of stores */
static int unknown_store_cnt = 0; /* Number of stores where data is read */
static int load_cnt = 0;	/* Number of loads */
static int unknown_load_cnt = 0; /* Number of loads where data is read */
static int known_def_cnt = 0;	/* Number of def w/ known operands */
static int unknown_def_cnt = 0;	/* Number of def w/ known operands */
static int call_cnt = 0;	/* Number of function calls */
static int indirect_call_cnt = 0; /* Number of indirect function calls */

static int loops_in_function = 0; /* Number of loops in current function */

extern int line_no;



void
main (argc, argv)
     int argc;
     char *argv[];
{
  register int i;
  char *out_file_name = NULL;
  FILE *in_file;

  if (argc == 1)
    switch_error ("");

  for (i = 1; i < argc; i++)
    {
      if (*argv [i] == '-')
	{
	  if (streq (argv [i], "-o"))
	    {
	      out_file_name = argv [i + 1];
	      i ++;
	    }
	  else if (streq (argv [i], "-x"))
	    {
	      executable_file_name = argv [i + 1];
	      i ++;
	    }
	  else if (streq (argv [i], "-q"))
	    quiet = 1;
	  else if (streq (argv [i], "-d"))
	    debug = 1;
	  else if (streq (argv [i], "-m"))
	    multiple_inst = 1;
	  else
	    switch_error ("aec: Unknown switch %s\n", argv [i]);
	}
      else
	{
	  if (out_file_name == NULL)
	    switch_error ("aec: No output file specified\n");
	  else if (executable_file_name == NULL)
	    switch_error ("aec: No executable specified\n");
	  else if (out_file == NULL)
	    {
	      out_file = fopen (out_file_name, "w");
#ifdef ECOFF_AOUT
	      ex_file = ldopen (executable_file_name, NULL);
#endif
	      output_preface ();
	    }

	  input_file_name = argv [i];
	  in_file = fopen (input_file_name, "r");
	  if (in_file == NULL)
	    fatal_error ("Input file %s cannot be opened\n", input_file_name);
	  initialize_scanner (in_file);
	  while (yyparse () == 1) ;
	}
    }

  schema_for_absent_functions ();
  indirect_call_code ();
  fprintf (out_file, "int max_loops = %d;\n", loop_cnt);
  copy_file_to_out_file (string_cat (LIBRARY_PATH, "aec-file-epilogue"));
  fclose (out_file);
#ifdef ECOFF_AOUT
  ldclose (ex_file);
#endif
  if (!quiet)
    report_statistics ();
  exit (0);
}


void
switch_error (message, x)
     char *message;
     int x;
{
  fprintf (stderr, message, x);
  fprintf (stderr, "aec -x a.out -o aec.out {-q, -d} f1.sma ... fn.sma\n");
  fprintf (stderr, "-x a.out	(Mandatory) Executable: name of executable a.out\n");
  fprintf (stderr, "-o aec.out	(Mandatory) Output: name of the output file\n");
  fprintf (stderr, "-q		(Optional)  Quiet: do not write information\n");
  fprintf (stderr, "-d		(Optional)  Debug: produce safer, but slower code\n");
  fprintf (stderr, "-m		(Optional)  Multiple inst: treat consecutive inst\n");
  exit (-1);
}


void
non_fatal_error (message, x)
     char *message;
     int x;
{
  fprintf (stderr, "aec: In file %s, line %d: ", input_file_name, line_no);
  fprintf (stderr, message, x);
}


void
fatal_error (message, x)
     char *message;
     int x;
{
  fprintf (stderr, "aec: In file %s, line %d: ", input_file_name, line_no);
  fprintf (stderr, message, x);
  exit (-1);
}


#define PER(x,y) ((x * 100) / y)

void
report_statistics ()
{
  fprintf (stderr, "\n\n%d functions w/ schemas  %d functions w/o schemas\n",
	   function_cnt, dummy_function_cnt);

  fprintf (stderr, "\n%d basic blocks:\n", block_cnt);
  fprintf (stderr, "  %6d (%d %%) targets\n", target_block_cnt,
	   PER (target_block_cnt, block_cnt));
  fprintf (stderr, "  %6d (%d %%) fall thru\n", fall_block_cnt,
	   PER (fall_block_cnt, block_cnt));
  fprintf (stderr, "  %6d (%d %%) end w/ cjump\n", cjump_block_cnt,
	   PER (cjump_block_cnt, block_cnt));
  fprintf (stderr, "  %6d (%d %%) end w/ jump\n", jump_block_cnt,
	   PER (jump_block_cnt, block_cnt));

  fprintf (stderr, "\n%d loops\n", loop_cnt);

  fprintf (stderr, "\n%d instructions:\n", inst_cnt);

  fprintf (stderr, "  %6d (%d %%) stores\n", store_cnt,
	   PER (store_cnt, inst_cnt));
  if (store_cnt > 0)
    fprintf (stderr, "    %6d (%d %%) unknown stores\n", unknown_store_cnt,
	     PER (unknown_store_cnt, store_cnt));

  fprintf (stderr, "  %6d (%d %%) loads\n", load_cnt,
	   PER (load_cnt, inst_cnt));
  if (load_cnt > 0)
    fprintf (stderr, "    %6d (%d %%) unknown loads\n", unknown_load_cnt,
	     PER (unknown_load_cnt, load_cnt));

  fprintf (stderr, "  %6d (%d %%) known defs\n", known_def_cnt,
	   PER (known_def_cnt, inst_cnt));
  fprintf (stderr, "  %6d (%d %%) unknown defs\n", unknown_def_cnt,
	   PER (unknown_def_cnt, inst_cnt));

  fprintf (stderr, "  %6d (%d %%) calls\n", call_cnt,
	   PER (call_cnt, inst_cnt));
  if (call_cnt > 0)
    fprintf (stderr, "    %6d (%d %%) indirect calls\n", indirect_call_cnt,
	     PER (indirect_call_cnt, call_cnt));
}




/* List data structure routines. */

list
cons (head, tail)
     int head;
     list tail;
{
  list x = (list) malloc (sizeof (list_cell));
  CAR (x) = head;
  CDR (x) = tail;
  return (x);
}


void
free_list (lst)
     list lst;
{
  while (lst != NULL)
    {
      list next = CDR (lst);
      free (lst);
      lst = next;
    }
}



static int call_used_regs [] = CALL_USED_REGISTERS;


void
output_preface ()
{
  int i;

  copy_file_to_out_file (string_cat (LIBRARY_PATH, "aec-file-prologue"));
  copy_file_to_out_file (string_cat (LIBRARY_PATH, "aec.h"));

  for (i = 0; i < FIRST_PSEUDO_REGISTER; i ++)
    if (!REG_LOCAL_TO_FUNCTION (i))
      if (HARD_REGNO_MODE_OK (i, SImode))
	output_reg_decl ("static", "int", i);
      else if (HARD_REGNO_MODE_OK (i, SFmode))
	output_reg_decl ("static",  "float", i);
      else if (HARD_REGNO_MODE_OK (i, DFmode))
	output_reg_decl ("static",  "double", i);

#ifdef PC_REGNUM
  printf_out ("#define PC R%d\n\n", PC_REGNUM);
#else
  printf_out ("static int PC;\n\n");
#endif

  printf_out ("static void __aec_global_reg_init ()\n{\n");
  INITIALIZE_REGISTERS ();
  printf_out ("}\n\n");
}


void
output_reg_decl (preface, type, regno)
     char *preface, *type;
     int regno;
{
  if (debug)
    printf_out ("%s %s *R%d = (%s *) -1;\n", preface, type, regno,
	     type);
  else
    printf_out ("%s %s R%d;\n", preface, type, regno);
}


void
schema_for_absent_functions ()
{
  register int i, error_head_output_p = 0;
  struct symbol *idp;

  for (i = 0; i < GLOBAL_HASH_TBL_SIZE; i++)
    for (idp = global_hash_tbl [i]; idp; idp = idp-> next)
      if (idp->used_in_call && !idp->schema_seen_p)
	{
	  if (!error_head_output_p)
	    {
	      fprintf (stdout, "aec: These functions are called but have no schemas.\n");
	      fprintf (stdout, "They must not produce events.\n");
	    }
	  error_head_output_p = 1;
	  fprintf (stdout, "  %s\n", idp->name);
	  make_dummy_function (idp->name);
	  idp->schema_seen_p = 1;
	  dummy_function_cnt ++;
	}
}


void
indirect_call_code ()
{
  printf_out ("static unsigned long func_tbl[] = {\n");
  if (indirect_call_seen)
    {
#ifdef ECOFF_AOUT
      int i;
      PDR ppd;
      unsigned long addr;
      char *name;
      SYMR sym;

      for (i = 0; i < SYMHEADER (ex_file).ipdMax - 1; i++)
	if (ldgetpd (ex_file, i, &ppd) != 0)
	  {
	    struct symbol *idp;

	    addr = ppd.adr;
	    ldtbread (ex_file, ppd.isym, &sym);
	    name = (char *) ldgetname (ex_file, &sym);
	    idp = lookup_symbol (name);
	    if (idp->schema_seen_p)
	      /* Program could invoke any function, but we can only follow
		 execution through those for which we have a schema. */
	      printf_out ("0x%x, (unsigned long) __ae_%s,\n", addr, name);
	  }
#endif
#ifdef BSD_AOUT
      register int i;
      register struct symbol *idp;
      register int count = 0;
      struct nlist *names;

      for (i = 0; i < GLOBAL_HASH_TBL_SIZE; i ++)
	for (idp = global_hash_tbl [i]; idp != NULL; idp = idp->next)
	  if (idp->schema_seen_p)
	    /* Program could invoke any function, but we can only follow
	       execution through those for which we have a schema. */
	    count += 1;

      names = (struct nlist *) malloc ((count + 1) * sizeof (struct nlist));
      names [count].n_name = NULL;
      count = 0;
      for (i = 0; i < GLOBAL_HASH_TBL_SIZE; i ++)
	for (idp = global_hash_tbl [i]; idp != NULL; idp = idp->next)
	  if (idp->schema_seen_p)
	    names [count ++].n_name = idp->name;

      nlist (executable_file_name, names);
      /* Sort names by address so can do binary search */
      qsort (names, count, sizeof (struct nlist), nlist_addr_cmp);
      for (i = 0; i < count; i++)
	if (names[i].n_type != 0)
	  printf_out ("0x%x, (unsigned long) __ae_%s,\n",
		      names [i].n_value, names [i].n_name);
#endif
    }
  printf_out ("0};\n\n");
}


#ifndef mips
nlist_addr_cmp (n1, n2)
     struct nlist *n1, *n2;
{return (n1->n_value - n2->n_value);}
#endif


void
copy_file_to_out_file (path)
     char *path;
{
  int n;
  FILE *pfd = fopen (path, "r", 0);
  char buffer[4096];

  if (pfd == NULL)
    fatal_error ("Cannot open file: %s\n", path);
  while ((n = fread (buffer, sizeof (char), 4096, pfd)) > 0)
    fwrite (buffer, sizeof (char), n, out_file);
  fclose (pfd);
}


char *
string_cat (s1, s2)
     char *s1, *s2;
{
  int l = strlen (s1) + strlen (s2);
  char *r = (char *) malloc (l + 1);

  strcpy (r, s1);
  return strcat (r, s2);
}


/* Return the address of global identifier from the core file or 0 if
   the identifier is not defined. */

unsigned long
symbol_address (name)
     char *name;
{
  struct symbol *idp = lookup_symbol (name);

  if (idp->address)		/* Cache address since lookup is expensive */
    return idp->address;

#ifdef ECOFF_AOUT
  {
    register int i;
    SYMR sym;

    for (i = 0; i <= SYMHEADER (ex_file).isymMax + SYMHEADER (ex_file).iextMax;
	 i++)
      if (ldtbread (ex_file, i, &sym))
	if (streq (name, ldgetname (ex_file, &sym)))
	  return (idp->address = sym.value);
  }
#endif
#ifdef BSD_AOUT
  {
    struct nlist names [2];

    names [0].n_name = name;
    names [1].n_name = NULL;
    if (nlist (executable_file_name, names) == -1)
      fatal_error ("Error reading symbol table for %s\n", name);
    return (idp->address = names [0].n_value);
  }
#endif
}


/* Find and return a symbol's address or die horribly. */

unsigned long
symbol_address_or_die (name)
     char *name;
{
  unsigned long addr = symbol_address (name);

  if (addr == 0)
    fatal_error ("Cannot find address of `%s' in %s\n", name,
		 executable_file_name);

  return addr;
}


/* Complain about a symbol not having an address and return 0. */

unsigned long
symbol_address_or_warn (name)
     char *name;
{
  unsigned long addr = symbol_address (name);

  if (addr == 0)
    {
      struct symbol *idp = lookup_symbol (name);
      if (!idp->addr_missing_warning && !quiet)
	fprintf (stderr, "aec: Cannot find address of '%s' in %s\n", name,
		 executable_file_name);
      idp->addr_missing_warning = 1;
    }

  return addr;
}



static char* open_function_name = NULL;


static int pc_offset = 0;


/* Non-zero if block numbers are encode in a byte, not a half-word. */

static int read_byte_p = 0;


void
function_start (name, num_blocks)
     char *name;
     int num_blocks;
{
  int i;
  unsigned long addr;

  if (open_function_name != NULL)
    non_fatal_error ("function %s is not terminated.\n", open_function_name);

  printf_out ("static int __ae_%s()\n{\n", name);

  printf_out ("  int in_pc;\n");
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i ++)
    if (REG_LOCAL_TO_FUNCTION(i))
      if (HARD_REGNO_MODE_OK (i, SImode))
	output_reg_decl ("  register",  "int", i);
      else if (HARD_REGNO_MODE_OK (i, SFmode))
	output_reg_decl ("  register",  "float", i);
      else if (HARD_REGNO_MODE_OK (i, DFmode))
	output_reg_decl ("  register",  "double", i);

  printf_out ("\n");

  addr = symbol_address_or_warn (name);
  if (addr != 0)
    printf_out ("  PC=0x%x;\n", addr);
  printf_out ("  in_pc = PC;\n");

  open_function_name = name;
  pc_offset = 0;
  read_byte_p = (num_blocks < 256);
  lookup_symbol (name)->schema_seen_p = 1;
  function_cnt ++;
  loops_in_function = 0;
}


void
make_dummy_function (name)
     char *name;
{
  /* exit and _exit have predefined functions because they do not obey
   the usual call/return convention. */
  if (!streq (name, "exit") && !streq (name, "_exit"))
    printf_out ("static int __ae_%s(){}\n\n", name);
}


void
function_end (name)
     char *name;
{
  register int i;

  if (debug)
    for (i = 0; i < FIRST_PSEUDO_REGISTER; i ++)
      if (REG_LOCAL_TO_FUNCTION (i))
	if (HARD_REGNO_MODE_OK (i, SImode))
	  printf_out ("  if (R%d != (int *) -1) free(R%d);\n", i, i);
	else if (HARD_REGNO_MODE_OK (i, SFmode))
	  printf_out ("  if (R%d != (float *) -1) free(R%d);\n", i, i);
	else if (HARD_REGNO_MODE_OK (i, DFmode))
	  printf_out ("  if (R%d != (double *) -1) free(R%d);\n", i, i);

  printf_out ("}\n\n");
  if (open_function_name == NULL || !streq (name, open_function_name))
    yyerror ("Function name mismatch");
  free (open_function_name);
  open_function_name = NULL;
  free (name);
  loop_cnt += loops_in_function;
}


static int open_bid = -1;


void
block_start (bid)
     int bid;
{
  if (open_bid != -1)
    non_fatal_error ("block %d is not terminated.\n", open_bid);
  printf_out ("/* Start of block %d */\n", bid);
  printf_out ("  L%d:\n", bid);
  printf_out ("  PC = in_pc+%d;\n", pc_offset);
  open_bid = bid;
  block_cnt ++;
}


void
block_start_target (bid)
     int bid;
{
  block_start (bid);
  target_block_cnt ++;
}


void
end_block_common (bid)
     int bid;
{
  if (bid != open_bid)
    yyerror ("Block id's mismatched");
  open_bid = -1;
  printf_out ("/* End of block %d */\n", bid);
}


void
block_end (bid, fall_thru_p, loop_anno)
     int bid, fall_thru_p;
     list loop_anno;
{
  if (fall_thru_p)		/* Falling into target of conditional. */
    printf_out ("  %s();\n", (read_byte_p ? "EAT_BYTE" : "EAT_HALF"));
  annotate_loop_edges (loop_anno, bid + 1, 1);
  end_block_common (bid);
  fall_block_cnt ++;
  free_list (loop_anno);
}


void
block_end_jump (bid, target_bid, jump_to_cjump_target_p, loop_anno)
     int bid, target_bid, jump_to_cjump_target_p;
     list loop_anno;
{
#ifdef JUMP_DELAY_SLOTS
  int i;

  for (i = 0; i < JUMP_DELAY_SLOTS; i ++)
    yyparse ();			/* Put delayed instruction first */
#endif

  issue_instruction ();
  if (jump_to_cjump_target_p)	/* Jump to target of conditional. */
    printf_out ("  %s();\n", (read_byte_p ? "EAT_BYTE" : "EAT_HALF"));
  annotate_loop_edges (loop_anno, target_bid, 1);
  printf_out ("  goto L%d;\n", target_bid);
  end_block_common (bid);
  jump_block_cnt ++;
  free_list (loop_anno);
}


void
block_end_cjump (bid, targets, loop_anno)
     int bid;
     list targets;
     list loop_anno;
{
  list t = targets;
  int hi = 0;
  char *seen_label;
#ifdef CJUMP_DELAY_SLOTS
  int i;

  for (i = 0; i < CJUMP_DELAY_SLOTS; i ++)
    yyparse ();			/* Put delayed instruction first */
#endif

  for (t = targets; t!= NULL; t = CDR (t))
    if (CAR (t) > hi) hi = CAR (t);

  seen_label = (char *) malloc ((hi + 1) * sizeof (char));
  bzero (seen_label, (hi + 1) * sizeof (char));

  issue_instruction ();
  printf_out ("  %s(temp);\n", (read_byte_p ? "GET_BYTE" : "GET_HALF"));
  printf_out ("  switch(temp)\n    {\n");
  for (t = targets; t != NULL; t = CDR (t))
    if (!seen_label [CAR (t)])
      {
	printf_out ("    case %d:", CAR (t));
	annotate_loop_edges (loop_anno, CAR (t), CDR (t) == NULL);
	printf_out (" goto L%d;\n", CAR (t));
	seen_label [CAR (t)] = 1;
      }
  printf_out ("    default: cjump_err();\n  }\n");

  free_list (targets);
  end_block_common (bid);

  cjump_block_cnt ++;
  free_list (loop_anno);
}


void
uneventful_inst (num_inst, size_inst)
     int num_inst, size_inst;
{
  int i;

  for (i = 0; i < num_inst; i ++)
    issue_instruction ();
}


void
store_inst (base, offset)
     char *base, *offset;
{
  char buf1 [80], buf2 [80];

  issue_instruction ();
  printf_out ("  WRITE_MEM(%s+%s);\n",
	      arg_out (base, buf1), arg_out (offset, buf2));
  store_cnt ++;
  free (base);
  free (offset);
}


void
store_d_inst (base, offset)
     char *base, *offset;
{
  char buf1 [80], buf2 [80];

  issue_instruction ();
  printf_out ("  WRITE_MEM(%s+%s);\n",
	      arg_out (base, buf1), arg_out (offset, buf2));
  printf_out ("  WRITE_MEM(%s+%d+4);\n",
	      arg_out (base, buf1), arg_out (offset, buf2));
  store_cnt ++;
  free (base);
  free (offset);
}


void
store_symbol_inst (offset, id)
     int offset;
     char *id;
{
  issue_instruction ();
  printf_out ("  WRITE_MEM(0x%x);\n", symbol_address_or_warn (id) + offset);
  free (id);
  store_cnt ++;
}


void
store_unknown_inst ()
{
  issue_instruction ();
  printf_out ("  GET_WORD(temp);\n");
  printf_out ("  WRITE_MEM(temp);\n");
  store_cnt ++;
  unknown_store_cnt ++;
}


void
load_inst (base, offset)
     char *base, *offset;
{
  char buf1 [80], buf2[80];

  issue_instruction ();
  printf_out ("  READ_MEM(%s+%s);\n",
	      arg_out (base, buf1), arg_out(offset, buf2));
  load_cnt ++;
  free (base);
  free (offset);
}


void
load_d_inst (base, offset)
     char *base, *offset;
{
  char buf1 [80], buf2 [80];

  issue_instruction ();
  printf_out ("  READ_MEM(%s+%s);\n",
	      arg_out (base, buf1), arg_out (offset, buf2));
  printf_out ("  READ_MEM(%s+%s+4);\n",
	      arg_out (base, buf1), arg_out (offset, buf2));
  load_cnt ++;
  free (base);
  free (offset);
}


void
load_unknown_inst ()
{
  issue_instruction ();
  printf_out ("  GET_WORD(temp);\n");
  printf_out ("  READ_MEM(temp);\n");
  load_cnt ++;
  unknown_load_cnt ++;
}


void
compute_defn (num_operands, dest_reg, operator, arg1, arg2)
     int num_operands;
     int dest_reg;
     char *operator;
     char *arg1, *arg2;
{
  char buf1 [80], buf2 [80], buf3 [80];
  char *a1, *a2, *a3;

  issue_instruction ();

  if (num_operands == 0)
    a1 = arg_out (arg1, buf1),
    a2 = a3 = "";
  else if (num_operands == 1)
    a1 = operator,
    a2 = arg_out (arg1, buf1),
    a3 = "";
  else
    a1 = arg_out (arg1, buf1),
    a2 = operator,
    a3 = arg_out (arg2, buf2);

  sprintf (buf3, "%s%s%s", a1, a2, a3);
  output_set_value (dest_reg, buf3);

  known_def_cnt ++;

  if (operator) free (operator);
  free (arg1);
  if (arg2) free (arg2);
}


void
unknown_defn (reg)
     int reg;
{
  issue_instruction ();
  if (debug)
    printf_out ("  GET_WORD(temp); SVI(R%d,temp);\n", reg);
  else
    printf_out ("  GET_WORD(R%d);\n", reg);
  unknown_def_cnt ++;
}


void
call_inst (callee)
     char *callee;
{
  char buf [80];
#ifdef CALL_DELAY_SLOTS
  int i;

  for (i = 0; i < CALL_DELAY_SLOTS; i ++)
    yyparse ();			/* Put delayed instruction first */
#endif

  issue_instruction ();
  printf_out ("  __ae_%s();\n", callee);
  printf_out ("  PC = in_pc+%d+%d;\n", pc_offset, PC_OFFSET_AFTER_CALL);

  lookup_symbol (callee)->used_in_call = 1; /* Create entry for callee */
  free (callee);
  call_cnt ++;
}


void
call_indirect_inst (reg)
     int reg;
{
  char buf [80];
#ifdef CALL_DELAY_SLOTS
  int i;

  for (i = 0; i < CALL_DELAY_SLOTS; i++)
    yyparse ();			/* Put delayed instruction first */
#endif

  indirect_call_seen = 1;
  issue_instruction ();
  printf_out ("  (address_to_callee(R%d))();\n", reg);
  printf_out ("  PC = in_pc+%d+%d;\n", pc_offset, PC_OFFSET_AFTER_CALL);
  indirect_call_cnt ++;
}



static int pending_instructions = 0;

void
issue_instruction ()
{
  pc_offset += 4;
  inst_cnt += 1;
  if (multiple_inst)
    pending_instructions += 1;
  else
    fprintf (out_file, "  ISSUE_INST(%d);\n", 1);
}


/*VARARGS0*/
void
printf_out (va_alist)
va_dcl
{
  va_list args;
  char *fmt;

  if (multiple_inst && pending_instructions > 0)
    fprintf (out_file, "  ISSUE_INST(%d);\n", pending_instructions);
  pending_instructions = 0;

  va_start(args);
  fmt = va_arg (args, char *);
  vfprintf (out_file, fmt, args);
}


char *
arg_out (arg, buf)
     char *arg, *buf;
{
  switch (*(arg + 1))
    {
    case 'R':
      if (debug)
	{
	  *arg = '*';		/* -> *Rn */
	  return arg;
	}
      else
	return arg + 1;

    case 'S':
      sprintf (buf, "0x%x", symbol_address_or_warn (arg + 2));
      return buf;

    case 'I':
      return arg + 2;

    default:
      fatal_error ("Unknown arg to output: %s\n", arg);
    }
}


void
output_set_value (dest_reg, value)
     int dest_reg;
     char *value;
{
  if (debug)
    printf_out ("  SVI(R%d, %s);\n", dest_reg, value);
  else
    printf_out ("  R%d = %s;\n", dest_reg, value);
}


/* Loop through the list of loop annotations to find those applying to
   the edge that goes to block BID.  If this is the LAST_TIME, then free
   the list of annotations. */

void
annotate_loop_edges (loop_anno, bid, last_time)
     register list loop_anno;
     int bid;
     int last_time;
{
  register list tmp = loop_anno;
  register char *start_seen, *cont_seen, *end_seen;

  while (tmp != NULL)
    {
      list entry = (list) CAR (tmp);
      int code = (int) CAR (entry);
      int target_bid = (int) CAR (CDR (entry));
      int loop_no = (int) CDR (CDR (entry));

      if (loop_no + 1 > loops_in_function)
	loops_in_function = loop_no + 1;
      tmp = CDR (tmp);
    }

  /* Watch out for duplicate annotations */
  start_seen = malloc (loops_in_function * sizeof (char));
  bzero (start_seen, loops_in_function * sizeof (char));
  cont_seen = malloc (loops_in_function * sizeof (char));
  bzero (cont_seen, loops_in_function * sizeof (char));
  end_seen = malloc (loops_in_function * sizeof (char));
  bzero (end_seen, loops_in_function * sizeof (char));

  while (loop_anno != NULL)
    {
      list entry = (list) CAR (loop_anno);
      int code = (int) CAR (entry);
      int target_bid = (int) CAR (CDR (entry));
      int loop_no = (int) CDR (CDR (entry));
      int real_loop_no = loop_cnt + loop_no;

      if (target_bid == bid)
	{
	  switch (code)
	    {
	    case START_LOOP:
	      if (!start_seen [loop_no])
		printf_out ("  LOOP_START(%d);", real_loop_no);
	      start_seen [loop_no] = 1;
	      break;

	    case CONT_LOOP:
	      if (!cont_seen [loop_no])
		printf_out ("  LOOP_CONT(%d);", real_loop_no);
	      cont_seen [loop_no] = 1;
	      break;

	    case END_LOOP:
	      if (!end_seen [loop_no])
		printf_out ("  LOOP_END(%d);", real_loop_no);
	      end_seen [loop_no] = 1;
	      break;
	    };
	}
      if (last_time)
	{
	  free (CDR (entry));
	  free (entry);
	}
      loop_anno = CDR (loop_anno);
    }

  if (start_seen != NULL)	/* NULL if loops_in_function == 0 */
    {
      free (start_seen);
      free (cont_seen);
      free (end_seen);
    }
}



#define HASHBITS 30

/* Return a SYMBOL whose name is TEXT (a null-terminated string).  If
   an identifier with that name has previously been looked-up, the same
   node is returned this time.  */


struct symbol *
lookup_symbol (text)
     register char *text;
{
  register int hi;
  register int i;
  register struct symbol *idp;
  register int len;

  /* Compute length of text in len.  */
  for (len = 0; text[len]; len++);

  /* Compute hash code */
  hi = len;
  for (i = 0; i < len; i++)
    hi = ((hi * 613) + (unsigned)(text[i]));

  hi &= (1 << HASHBITS) - 1;
  hi %= GLOBAL_HASH_TBL_SIZE;

  /* Search table for identifier */
  for (idp = global_hash_tbl [hi]; idp; idp = idp->next)
    if (streq (idp->name, text))
      return idp;		/* <-- return if found */

  /* Not found, create one, add to chain */
  idp = (struct symbol *) malloc (sizeof (struct symbol));
  idp->name = (char *) strcpy (malloc (strlen (text) + 1), text);
  idp->used_in_call = 0;
  idp->schema_seen_p = 0;
  idp->address = 0;
  idp->addr_missing_warning = 0;

  idp->next = global_hash_tbl [hi];
  global_hash_tbl [hi] = idp;
  return idp;			/* <-- return if created */
}
