/* AE program profiling system.
   Machine-specific definitions for MIPS R2000/R3000 processors.
   Copyright (C) 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/AE/RCS/ae-mips.h,v 2.0 90/02/09 17:21:37 larus Exp Locker: larus $ */


/* Define the base and bounds of the AE buffer that accumulates events in
   the executing program. */


/* The pointer to the AE Buffer can either be in a register or in a
   variable stored in memory.  If it is in a register, AE_BUFFER_REG
   contains the register's name, as a string.  If it is in memory,
   AE_BUFFER_VAR contains the variable's name as a string.
   MAKE_AE_BUFFER_POINTER returns an rtx expression for this pointer. */


/* On MIPS, we use R23 (aka) as the pointer to AE buffer.  The limit
   is a fixed distance from the top of user memory, which is a negative
   number. */

#define AE_BUFFER_REG "s7"
#undef AE_BUFFER_VAR

#define MAKE_AE_BUFFER_POINTER() gen_rtx (REG, Pmode, 23) /* 23 = s7 */


/* The end of the AE Buffer can either be pointed to by a register or
   by a variable stored in memory.  If it is in a register,
   AE_BUFFER_BOUND_REG contains the register's name, as a string.  If it
   is in memory, AE_BUFFER_BOUND_VAR contains the variable's name as a
   string.  MAKE_AE_BOUND_POINTER returns an rtx expression for this
   pointer. */

#undef AE_BUFFER_BOUND_REG
#undef AE_BUFFER_BOUND_VAR

#define MAKE_AE_BOUND_POINTER()


/* Alternatively, the end of the AE buffer can be a fixed distance
   from the top of the stack. */

/* Here's the picture for MIPS:

   Top of memory      --> @0x80000000
   (Reserved on MIPS) -->  4K
   Environemnt        -->  10K max ??
   argv               -->  ?K max
   ae_start frame     -->  16 bytes
   ... padding  ...
   top of buffer      --> @0x80000000-(32K-4K)
 */

#define STACK_TOP 0x80000000


/* Less than 32K to fit insn's 15-bit field */

#define AE_BUFFER_STACK_OFFSET 32768-4096


/* Size of AE buffer (bytes). */

#define AE_BUFFER_SIZE 0x100000	/* 1MB */


/* Size of a stack frame for the routine AE_START. */

#define AE_START_FRAME_SIZE (16 * sizeof (int))


/* Name of stack pointer register. */

#define SP_REG "sp"



/* One plus maximum number of instructions combine by peephole optimizer. */

#define MAX_PEEP 3


/* Return non-zero if register number REGNO can be defined upon
   function entry. */

#define REGISTER_DEFINED_IN_CALL(REGNO) ((REGNO) == STACK_POINTER_REGNUM \
					 || (REGNO) == FRAME_POINTER_REGNUM \
					 || FUNCTION_ARG_REGNO_P ((REGNO)))


/* Define the characters that proceed comments and assembler directives. */

#define ASM_COMMENT_CHAR '#'

#define ASM_DIRECTIVE_CHAR '.'


/* Define the number of delayed instructions after a jump, conditional jump,
   or call instruction.  Do not define these values if the instructions have
   no delays or the assembler hides them by doing code reorganization. */

#undef JUMP_DELAY_SLOTS

#undef CJUMP_DELAY_SLOTS

#undef CALL_DELAY_SLOTS


/* The size of most assembly instructions (in bytes). */

#define STD_ASM_INSN_LENGTH 4


/* Set of instruction-size pairs for instructions whose size is not
   standard.  The table must be sorted by instruction name. */

#define ASM_INSN_SIZE_EXCEPTIONS			\
	"abs", (char *) 3,				\
	"bge", (char *) 2,				\
	"bgeu", (char *) 2,				\
	"bgt", (char *) 2,				\
	"bgtu", (char *) 2,				\
	"ble", (char *) 2,				\
	"bleu", (char *) 2,				\
	"blt", (char *) 2,				\
	"bltu", (char *) 2,				\
	"div", (char *) 10,		/* Guess */	\
	"divu", (char *) 10,		/* Guess */	\
	"la", (char *) 2,				\
	"li", (char *) 2,				\
	"mul", (char *) 2,				\
	"mulo", (char *)  5,				\
	"mulou", (char *) 4,				\
	"neg", (char *) 2,				\
	"negu", (char *) 3,				\
	"rem", (char *) 10,		/* Guess */	\
	"remu", (char *) 10,		/* Guess */	\
	"rol", (char *) 3,				\
	"ror", (char *) 3,				\
	"seq", (char *) 2,				\
	"sge", (char *) 2,				\
	"sgeu", (char *) 2,				\
	"sgt", (char *) 2,				\
	"sgtu", (char *) 2,				\
	"sle", (char *) 2,				\
	"sleu", (char *) 2,				\
	"sne", (char *) 2,				\
	"ulh", (char *) 2,				\
	"ulhu", (char *) 2,				\
	"ulw", (char *) 2,				\
	"ush", (char *) 2,				\
	"usw", (char *) 2


/* Return non-zero if the assembly instruction is a branch that does
   not execute its (normally) delayed slot instruction. */

#define BRANCH_IS_ANNULED(ASM_INSN) 0


/* Return a pointer to the function name if an assembly instruction is
   a subroutine invocation.  If it is not, return 0. */

#define ASM_INSN_IS_CALL(ASM_INSN) (strncmp ((ASM_INSN), "jal", 3) \
				    ? 0 : (ASM_INSN) + 3)




/* Produce the schema corresponding the the standard function prologue
   and epilogue.  Record values that are need upon function entry. */

#define SCHEMA_PROLOGUE(RECORD_REG_ON_ENTRY)			\
{								\
  /* Code from FUNCTION_PROLOGUE: */				\
  int frame_size = get_frame_size ();				\
  register int regno;						\
  register int mask = 0, fmask=0;				\
  extern char call_used_regs [];				\
  register int push_loc = 0, tsize = frame_size + 8;		\
								\
  for (regno = 0; regno < 32; regno++)				\
    if (MUST_SAVE_REG_LOGUES					\
	|| (regs_ever_live [regno] && !call_used_regs [regno]))	\
      {tsize += 4; mask |= 1 << regno;}				\
								\
  for (regno = 32; regno < FIRST_PSEUDO_REGISTER; regno += 2)	\
    if (regs_ever_live [regno] && !call_used_regs [regno])	\
      {tsize += 8; fmask |= 1 << (regno-32);}			\
								\
  if (THIS_VARARGS_SUSPECTED) tsize += 16;			\
								\
  regno = STACK_POINTER_REGNUM;					\
  tsize = AL_ADJUST_ALIGN (tsize);				\
								\
  push_loc = 0;							\
								\
  if (frame_pointer_needed					\
      || regs_ever_live [29] || regs_ever_live [30]		\
      || fmask || mask						\
      || (frame_size > 0))					\
    record_sp ();						\
								\
  for (regno = 31; regno >= 30; regno--)			\
    if (MUST_SAVE_REG_LOGUES					\
	|| (regs_ever_live [regno] && !call_used_regs [regno]))	\
      {								\
	store_schema_int_offset (STACK_POINTER_REGNUM, push_loc, 0);\
	push_loc += 4;						\
      }								\
								\
  if (THIS_VARARGS_SUSPECTED)					\
    {								\
      int fregno;						\
      simple_def_schema (9, 0, tsize);				\
      store_schema_int_offset (STACK_POINTER_REGNUM, tsize - 4, 0);\
      for (fregno = 44; fregno< 48; fregno += 2)		\
	{							\
	  store_schema_int_offset (STACK_POINTER_REGNUM, push_loc, 1);\
	  push_loc += 8;					\
	}							\
    }								\
								\
  for (regno = 29; regno >= 0; regno--)				\
    if (MUST_SAVE_REG_LOGUES					\
	|| (regs_ever_live [regno] && !call_used_regs [regno]))\
      {								\
	store_schema_int_offset (STACK_POINTER_REGNUM, push_loc, 0);\
	push_loc += 4;						\
      }								\
								\
  for (regno = 32; regno < FIRST_PSEUDO_REGISTER; regno += 2)	\
    if (regs_ever_live [regno] && !call_used_regs [regno])	\
      {								\
	store_schema_int_offset (STACK_POINTER_REGNUM, push_loc, 1);\
	push_loc += 8;						\
      }								\
								\
  if (frame_pointer_needed)					\
    simple_def_schema (FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM, tsize);\
								\
  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno ++)	\
    if (RECORD_REG_ON_ENTRY [regno]				\
	&& regno != STACK_POINTER_REGNUM			\
	&& regno != FRAME_POINTER_REGNUM)			\
      {								\
	unknown_def_schema (regno);				\
	issue_event (gen_rtx (REG, Pmode, regno));		\
      }								\
}


#define SCHEMA_EPILOGUE()					\
{								\
  /* Code from FUNCTION_EPILOGUE: */				\
  int frame_size = get_frame_size ();				\
  register int regno;						\
  register int mask = 0, fmask = 0;				\
  extern char call_used_regs [];				\
  register int push_loc ;					\
								\
  extern int current_function_total_framesize;			\
								\
  push_loc = 0;							\
  regno = STACK_POINTER_REGNUM;					\
								\
  if (frame_pointer_needed)					\
    simple_def_schema (8, FRAME_POINTER_REGNUM, 0);		\
								\
  for (regno = 0; regno < 32; regno++)				\
    if (MUST_SAVE_REG_LOGUES					\
	|| (regs_ever_live [regno] && !call_used_regs [regno]))	\
      mask |= 1 << regno;					\
								\
  for (regno = 31; regno >= 0; regno--)				\
    {								\
      if (MUST_SAVE_REG_LOGUES					\
	  || (regs_ever_live [regno] && !call_used_regs [regno]))\
	{							\
	  load_schema_int_offset ((frame_pointer_needed ? 8 : STACK_POINTER_REGNUM),\
		       (frame_pointer_needed ?			\
			push_loc - current_function_total_framesize:\
			push_loc),				\
		       0);					\
	  push_loc += 4;					\
	}							\
      if (THIS_VARARGS_SUSPECTED && (regno == 30))		\
	push_loc += 16;						\
    }								\
								\
  for (regno = 32; regno < FIRST_PSEUDO_REGISTER; regno += 2)	\
    if (regs_ever_live [regno] && !call_used_regs [regno])	\
      fmask |= 1 <<  (regno-32);				\
								\
  for (regno = 32; regno < FIRST_PSEUDO_REGISTER; regno += 2)	\
    if (regs_ever_live [regno] && !call_used_regs [regno])	\
      {								\
	load_schema_int_offset ((frame_pointer_needed ? 8 : STACK_POINTER_REGNUM),\
		     (frame_pointer_needed ?			\
		      push_loc - current_function_total_framesize:\
		      push_loc),				\
								\
		     1);					\
	push_loc += 8;						\
      }								\
								\
  if (frame_pointer_needed)					\
    simple_def_schema (STACK_POINTER_REGNUM, 8, 0);		\
  else								\
    if (regs_ever_live [29]|| regs_ever_live [30]		\
	|| fmask || mask					\
	||  (frame_size > 0))					\
      simple_def_schema (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,\
			current_function_total_framesize);	\
}



/* Produce and write the the assembly output file code to record the
   various types of events. */


/* Check that the AE buffer has SIZE bytes free.  If not, empty the
   buffer. */

#define GENERATE_SPACE_CHECK(COMMENT, SIZE)			\
{								\
  rtx xops [1];							\
  rtx label = gen_label_rtx ();					\
  char buffer [256];						\
								\
  sprintf (buffer, ".set noat\t\t\t\t# %s Event w/ check", COMMENT);\
  output_asm_insn (buffer, xops);				\
								\
  xops [0] = ae_buffer_pointer;					\
  sprintf (buffer, "addu $1,%%0,%d", AE_BUFFER_STACK_OFFSET + CHUNK_SIZE);\
  output_asm_insn (buffer, xops);				\
								\
  xops [0] = label;						\
  output_asm_insn ("bgtz $1,%l0", xops);			\
  output_asm_insn (".set at", xops);				\
								\
  output_asm_insn ("jal ae_flush_buffer", xops);		\
								\
  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (label));\
}


/* Generate an event to record VALUE (an rtx register). */

#define GENERATE_EVENT(VALUE)					\
{								\
  rtx xops [2];							\
								\
  xops [0] = VALUE;						\
  xops [1] = ae_buffer_pointer;					\
  output_asm_insn ("usw\t%0,0(%1)", xops);			\
								\
  xops [0] = ae_buffer_pointer;					\
  output_asm_insn ("addiu\t%0,%0,4\t\t# End Event", xops);	\
}


/* Generate an event to record the integer VALUE, which should be
   stored in BYTES bytes. */

#define GENERATE_SHORT_EVENT(VALUE, BYTES)			\
{								\
  rtx xops [2];							\
								\
  output_asm_insn (".set noat", xops);				\
  xops [0] = gen_rtx (CONST_INT, VOIDmode, VALUE);		\
  output_asm_insn ("addiu\t$1,$0,%0", xops);			\
								\
  xops [0] = ae_buffer_pointer;					\
  if (BYTES == 1)						\
    output_asm_insn ("sb\t$1,0(%0)", xops);			\
  else								\
    output_asm_insn ("ush\t$1,0(%0)", xops);			\
  output_asm_insn (".set at", xops);				\
								\
  xops [0] = ae_buffer_pointer;					\
  xops [1] = gen_rtx (CONST_INT, VOIDmode, BYTES);		\
  output_asm_insn ("addiu\t%0,%0,%1\t\t# End Short Event", xops);\
}


/* Generate an event to record ADDRESS, which is made computed from BASE and
   OFFSET. */

#define GENERATE_ADDRESS_EVENT(ADDRESS, BASE, OFFSET)		\
{								\
  rtx xops [1];							\
								\
  output_asm_insn (".set noat", xops);				\
  xops [0] = ADDRESS;						\
  /* Causes assembler warning since LA is a macro, but produces	\
     correct code. */						\
  output_asm_insn ("la\t$1,%0", xops);				\
								\
  xops [0] = ae_buffer_pointer;					\
  output_asm_insn ("usw\t$1,0(%0)", xops);			\
  output_asm_insn (".set at", xops);				\
								\
  xops [0] = ae_buffer_pointer;					\
  output_asm_insn ("addiu\t%0,%0,4\t\t# End Address Event", xops);\
}



/* Assembly code routines for aecrt0.o. */

#ifdef AE_START_ASM
	.align 2
	.globl ae_start
	.ent ae_start
ae_start:
	.set nomove		/* Do not allow the assembler to move insns.*/
	lw	$4,0($sp)
	la	$gp,_gp
	addiu	$5,$sp,4
	addiu	$6,$5,4
	sll	$2,$4,2
	addu	$6,$6,$2
	addiu	$sp,$sp,-24
	sw	$0,20($sp)
	sw	$6,environ
	sw	$5,__Argv
	sw	$4,__Argc
	sw	$0,errno
	jal	ae_initialize	/* Addition */
	lw	$5,__Argv	/* ditto */
	lw	$4,__Argc	/* ditto */
	jal	main
	jal	exit
	move	$4,$2
	break	0
	.set move
	.end ae_start
#endif


#ifdef AE_FLUSH_BUFFER_ASM
	.align 2
	.globl ae_flush_buffer
	.ent	ae_flush_buffer
ae_flush_buffer:
 /*PROLOGUE */
 /*	.mask	0xc0010000 */
	subu	$29,152	/*temp=  128,saveregs=   24, sfo=   -8 */
	sw	$31,0($29)
	sw	$30,4($29)
	sw	$16,8($29)
 /*	.fmask	0x0 */
	addiu	$30,$29,152	/*Establish FramePTR */
 /*END PROLOGUE */
	.set noat
	addiu	$16,$30,0xff78	/*addsi3	$30,-136 -> $16 */
	sw $1,4($16)
	.set at
	sw $2,8($16)
	sw $3,12($16)
	sw $4,16($16)
	sw $5,20($16)
	sw $6,24($16)
	sw $7,28($16)
	sw $8,32($16)
	sw $9,36($16)
	sw $10,40($16)
	sw $11,44($16)
	sw $12,48($16)
	sw $13,52($16)
	sw $14,56($16)
	sw $15,60($16)
	sw $17,68($16)
	sw $18,72($16)
	sw $19,76($16)
	sw $20,80($16)
	sw $21,84($16)
	sw $22,88($16)
	sw $24,96($16)
	sw $25,100($16)
	sw $26,104($16)
	sw $27,108($16)
	sw $28,112($16)
	addiu	$29,$29,0xfff0	/*subsi3	$29,16 -> $29 */
	lw	$4,ae_fd	/*movsi ae_fd -> $4 */
	lw	$5,ae_buffer_base	/*movsi ae_buffer_base -> $5 */
	subu	$6,$23,$5	/*subsi3 $23,$5 -> $6 */
	jal	write	/* call  write  regle 2-call (VOIDmode) */
	lw	$23,ae_buffer_base	/*movsi ae_buffer_base -> $23 */
	.set noat
	lw $1,4($16)
	.set at
	lw $2,8($16)
	lw $3,12($16)
	lw $4,16($16)
	lw $5,20($16)
	lw $6,24($16)
	lw $7,28($16)
	lw $8,32($16)
	lw $9,36($16)
	lw $10,40($16)
	lw $11,44($16)
	lw $12,48($16)
	lw $13,52($16)
	lw $14,56($16)
	lw $15,60($16)
	lw $17,68($16)
	lw $18,72($16)
	lw $19,76($16)
	lw $20,80($16)
	lw $21,84($16)
	lw $22,88($16)
	lw $24,96($16)
	lw $25,100($16)
	lw $26,104($16)
	lw $27,108($16)
	lw $28,112($16)
	addiu	$29,$29,0x10	/*addsi3	$29,16 -> $29 */
 /*EPILOGUE */
	addu	$29,$0,$30	/* sp not trusted  here */
 /*	.mask	0xc0010000 */
	lw	$31,-152($29)
	lw	$30,-148($29)
	lw	$16,-144($29)
 /*	.fmask	0x0 */
	j	$31
 /*END EPILOGUE */
 	.end	ae_flush_buffer
	.comm	ae_fd,8
	.comm	ae_buffer_base,8
#endif



/* Definitions for AEC. */

/* a.out file format is ECOFF, with the LDFCN library to find symbols. */

#define ECOFF_AOUT
#undef BSD_AOUT


/* Function call returns this many bytes after call instruction. */

/* We don't see delayed instructions on MIPS, so return to the next
   instruction.*/

#define PC_OFFSET_AFTER_CALL 4


/* Return non-zero if register N is local to a function, e.g. can
   have distinct values in different functions. */

#define REG_LOCAL_TO_FUNCTION(N) (!call_used_regs [(N)]		\
				  || (N) == STACK_POINTER_REGNUM)


/* Initialize registers before the generation program begins. */

#define INITIALIZE_REGISTERS()					\
	{							\
	  char buf [80];					\
	  output_set_value (0, "0");				\
	  sprintf (buf, "0x%x", symbol_address_or_die ("_gp"));	\
	  output_set_value (28, buf);				\
	}
