This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Committed, MMIX: Close the gap between call-saved and call-clobberedregisters


MMIX would benefit from a more dynamic register allocation
scheme, but here's a fix for one of the most obvious wart.

With Knuth's ABI (the default), splitting up local registers
statically into call-saved ($0..$14) and call-clobbered
($15..$31) as mandated by GCC, naturally with call-saved lowest,
would make GCC use call-clobbered registers first.  There would
be a gap between call-saved registers and call-clobbered
registers, which has a cost for the first call-clobbered
register that is set.  As stated in the MMIX_OUTPUT_REGNO
comment, this makes unused call-saved registers be used as
call-clobbered registers.  It couldn't be done just by GCC's
existing register renumbering schemes AFAICT.  A slightly
complicating factor is that incoming parameters are in ($0..$15)
and outgoing in ($16..$31), but not much since outgoing
parameters are call-clobbered.  Capice?  Unfortunately you will
not notice with the non-pipelined mmix simulator since it only
counts a fixed number of cycles per insn.  Numbers were the same
before and after this change since I didn't realize that and
therefore didn't run the pipelined simulator for comparison
figures before this change. :-(  At least the assembly code looks
better and I'll settle for that now when the change is done. :-)
It tests ok of course.  Also a fix for a comment in mmix.md.

Committed (to trunk).

	* config/mmix/mmix.h (MMIX_LAST_STACK_REGISTER_REGNUM): Renamed
	from MMIX_LAST_REGISTER_FILE_REGNUM.
	(NO_IMPLICIT_EXTERN_C): Remove cryptic obsolete comment.
	(struct machine_function): New member highest_saved_stack_register
	previously static variable in mmix.c.
	(MACHINE_DEPENDENT_REORG): Define.
	* config/mmix/mmix.c (highest_saved_stack_register): Deleted.
	(MMIX_OUTPUT_REGNO): New.
	(mmix_target_asm_function_prologue): Move calculation of last used
	saved-stack-register into...
	(mmix_machine_dependent_reorg): New function.  Update to also handle
	!TARGET_ABI_GNU.
	(mmix_print_operand): Apply MMIX_OUTPUT_REGNO when emitting
	register names, simplify somewhat by new variable regno.
	<case 'p'>: Remove fixed FIXME.  Always emit highest used saved
	register.
	(mmix_print_operand_address): Apply MMIX_OUTPUT_REGNO when
	emitting register names.
	(mmix_asm_output_reg_push, mmix_asm_output_reg_pop): Ditto.
	(mmix_dbx_register_number): Apply MMIX_OUTPUT_REGNO here too.
	Remove fixed FIXME.
	* config/mmix/mmix-protos.h (mmix_machine_dependent_reorg):
	Declare.

	* config/mmix/mmix.md ("divmoddi4"): Update head comment.

Index: mmix-protos.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mmix/mmix-protos.h,v
retrieving revision 1.7
diff -p -c -r1.7 mmix-protos.h
*** mmix-protos.h	5 Mar 2002 02:16:39 -0000	1.7
--- mmix-protos.h	30 Apr 2002 18:40:40 -0000
*************** extern void mmix_print_operand PARAMS ((
*** 125,130 ****
--- 125,131 ----
  extern void mmix_print_operand_address PARAMS ((FILE *, rtx));
  extern int mmix_valid_comparison PARAMS ((RTX_CODE, enum machine_mode, rtx));
  extern rtx mmix_gen_compare_reg PARAMS ((enum rtx_code, rtx, rtx));
+ extern void mmix_machine_dependent_reorg PARAMS ((rtx));
  #endif /* RTX_CODE */

  extern int mmix_asm_preferred_eh_data_format PARAMS ((int, int));
Index: mmix.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mmix/mmix.c,v
retrieving revision 1.24
diff -p -c -r1.24 mmix.c
*** mmix.c	6 Apr 2002 08:20:35 -0000	1.24
--- mmix.c	30 Apr 2002 18:40:42 -0000
*************** Boston, MA 02111-1307, USA.  */
*** 62,67 ****
--- 62,80 ----
        || EH_RETURN_DATA_REGNO (2) == REGNO	\
        || EH_RETURN_DATA_REGNO (3) == REGNO))

+ /* For the default ABI, we rename registers at output-time to fill the gap
+    between the (statically partitioned) saved registers and call-clobbered
+    registers.  In effect this makes unused call-saved registers to be used
+    as call-clobbered registers.  The benefit comes from keeping the number
+    of local registers (value of rL) low, since there's a cost of
+    increasing rL and clearing unused (unset) registers with lower numbers.  */
+ #define MMIX_OUTPUT_REGNO(N)					\
+  (TARGET_ABI_GNU 						\
+   || (N) < MMIX_RETURN_VALUE_REGNUM				\
+   || (N) > MMIX_LAST_STACK_REGISTER_REGNUM			\
+   ? (N) : ((N) - MMIX_RETURN_VALUE_REGNUM			\
+ 	   + cfun->machine->highest_saved_stack_register + 1))
+
  /* The canonical saved comparison operands for non-cc0 machines, set in
     the compare expander.  */
  rtx mmix_compare_op0;
*************** const char *mmix_cc1_ignored_option;
*** 74,83 ****

  /* Declarations of locals.  */

- /* This is used in the prologue for what number to pass in a PUSHJ or
-    PUSHGO insn.  */
- static int mmix_highest_saved_stack_register;
-
  /* Intermediate for insn output.  */
  static int mmix_output_destination_register;

--- 87,92 ----
*************** mmix_target_asm_function_prologue (strea
*** 981,999 ****
  			       cfa_offset);
  	}
      }

    /* We put the number of the highest saved register-file register in a
       location convenient for the call-patterns to output.  Note that we
       don't tell dwarf2 about these registers, since it can't restore them
       anyway.  */
!   for (regno = MMIX_LAST_REGISTER_FILE_REGNUM;
         regno >= 0;
         regno--)
      if ((regs_ever_live[regno] && !call_used_regs[regno])
  	|| (regno == MMIX_FRAME_POINTER_REGNUM && frame_pointer_needed))
        break;

!   mmix_highest_saved_stack_register = regno;
  }

  /* TARGET_ASM_FUNCTION_EPILOGUE.  */
--- 990,1039 ----
  			       cfa_offset);
  	}
      }
+ }
+
+ /* MACHINE_DEPENDENT_REORG.
+    No actual rearrangements done here; just virtually by calculating the
+    highest saved stack register number used to modify the register numbers
+    at output time.  */
+
+ void
+ mmix_machine_dependent_reorg (first)
+      rtx first ATTRIBUTE_UNUSED;
+ {
+   int regno;

    /* We put the number of the highest saved register-file register in a
       location convenient for the call-patterns to output.  Note that we
       don't tell dwarf2 about these registers, since it can't restore them
       anyway.  */
!   for (regno = MMIX_LAST_STACK_REGISTER_REGNUM;
         regno >= 0;
         regno--)
      if ((regs_ever_live[regno] && !call_used_regs[regno])
  	|| (regno == MMIX_FRAME_POINTER_REGNUM && frame_pointer_needed))
        break;

!   /* Regardless of whether they're saved (they might be just read), we
!      mustn't include registers that carry parameters.  We could scan the
!      insns to see whether they're actually used (and indeed do other less
!      trivial register usage analysis and transformations), but it seems
!      wasteful to optimize for unused parameter registers.  As of
!      2002-04-30, regs_ever_live[n] seems to be set for only-reads too, but
!      that might change.  */
!   if (!TARGET_ABI_GNU && regno < current_function_args_info.regs - 1)
!     {
!       regno = current_function_args_info.regs - 1;
!
!       /* We don't want to let this cause us to go over the limit and make
! 	 incoming parameter registers be misnumbered and treating the last
! 	 parameter register and incoming return value register call-saved.
! 	 Stop things at the unmodified scheme.  */
!       if (regno > MMIX_RETURN_VALUE_REGNUM - 1)
! 	regno = MMIX_RETURN_VALUE_REGNUM - 1;
!     }
!
!   cfun->machine->highest_saved_stack_register = regno;
  }

  /* TARGET_ASM_FUNCTION_EPILOGUE.  */
*************** mmix_print_operand (stream, x, code)
*** 2165,2170 ****
--- 2205,2211 ----
    /* When we add support for different codes later, we can, when needed,
       drop through to the main handler with a modified operand.  */
    rtx modified_x = x;
+   int regno = x != NULL_RTX && REG_P (x) ? REGNO (x) : 0;

    switch (code)
      {
*************** mmix_print_operand (stream, x, code)
*** 2189,2199 ****
      case 'H':
        /* Highpart.  Must be general register, and not the last one, as
  	 that one cannot be part of a consecutive register pair.  */
!       if (REGNO (x) > MMIX_LAST_GENERAL_REGISTER - 1)
! 	internal_error ("MMIX Internal: Bad register: %d", REGNO (x));

        /* This is big-endian, so the high-part is the first one.  */
!       fprintf (stream, "%s", reg_names[REGNO (x)]);
        return;

      case 'L':
--- 2230,2240 ----
      case 'H':
        /* Highpart.  Must be general register, and not the last one, as
  	 that one cannot be part of a consecutive register pair.  */
!       if (regno > MMIX_LAST_GENERAL_REGISTER - 1)
! 	internal_error ("MMIX Internal: Bad register: %d", regno);

        /* This is big-endian, so the high-part is the first one.  */
!       fprintf (stream, "%s", reg_names[MMIX_OUTPUT_REGNO (regno)]);
        return;

      case 'L':
*************** mmix_print_operand (stream, x, code)
*** 2213,2223 ****
  	  return;
  	}

!       if (REGNO (x) > MMIX_LAST_GENERAL_REGISTER - 1)
! 	internal_error ("MMIX Internal: Bad register: %d", REGNO (x));

        /* This is big-endian, so the low-part is + 1.  */
!       fprintf (stream, "%s", reg_names[REGNO (x) + 1]);
        return;

        /* Can't use 'a' because that's a generic modifier for address
--- 2254,2264 ----
  	  return;
  	}

!       if (regno > MMIX_LAST_GENERAL_REGISTER - 1)
! 	internal_error ("MMIX Internal: Bad register: %d", regno);

        /* This is big-endian, so the low-part is + 1.  */
!       fprintf (stream, "%s", reg_names[MMIX_OUTPUT_REGNO (regno) + 1]);
        return;

        /* Can't use 'a' because that's a generic modifier for address
*************** mmix_print_operand (stream, x, code)
*** 2273,2291 ****
  	 by the prologue.  The actual operand contains the number of
  	 registers to pass, but we don't use it currently.  Anyway, we
  	 need to output the number of saved registers here.  */
!       if (TARGET_ABI_GNU)
! 	fprintf (stream, "%d", mmix_highest_saved_stack_register + 1);
!       else
! 	/* FIXME: Get the effect of renaming $16, $17.. to the first
! 	   unused call-saved reg.  */
! 	fprintf (stream, "15");
        return;

      case 'r':
        /* Store the register to output a constant to.  */
        if (! REG_P (x))
  	fatal_insn ("MMIX Internal: Expected a register, not this", x);
!       mmix_output_destination_register = REGNO (x);
        return;

      case 'I':
--- 2314,2328 ----
  	 by the prologue.  The actual operand contains the number of
  	 registers to pass, but we don't use it currently.  Anyway, we
  	 need to output the number of saved registers here.  */
!       fprintf (stream, "%d",
! 	       cfun->machine->highest_saved_stack_register + 1);
        return;

      case 'r':
        /* Store the register to output a constant to.  */
        if (! REG_P (x))
  	fatal_insn ("MMIX Internal: Expected a register, not this", x);
!       mmix_output_destination_register = MMIX_OUTPUT_REGNO (regno);
        return;

      case 'I':
*************** mmix_print_operand (stream, x, code)
*** 2332,2340 ****
    switch (GET_CODE (modified_x))
      {
      case REG:
!       if (REGNO (modified_x) >= FIRST_PSEUDO_REGISTER)
! 	internal_error ("MMIX Internal: Bad register: %d", REGNO (modified_x));
!       fprintf (stream, "%s", reg_names[REGNO (modified_x)]);
        return;

      case MEM:
--- 2369,2378 ----
    switch (GET_CODE (modified_x))
      {
      case REG:
!       regno = REGNO (modified_x);
!       if (regno >= FIRST_PSEUDO_REGISTER)
! 	internal_error ("MMIX Internal: Bad register: %d", regno);
!       fprintf (stream, "%s", reg_names[MMIX_OUTPUT_REGNO (regno)]);
        return;

      case MEM:
*************** mmix_print_operand_address (stream, x)
*** 2402,2408 ****
      {
        /* I find the generated assembly code harder to read without
  	 the ",0".  */
!       fprintf (stream, "%s,0",reg_names[REGNO (x)]);
        return;
      }
    else if (GET_CODE (x) == PLUS)
--- 2440,2446 ----
      {
        /* I find the generated assembly code harder to read without
  	 the ",0".  */
!       fprintf (stream, "%s,0", reg_names[MMIX_OUTPUT_REGNO (REGNO (x))]);
        return;
      }
    else if (GET_CODE (x) == PLUS)
*************** mmix_print_operand_address (stream, x)
*** 2420,2430 ****

        if (REG_P (x1))
  	{
! 	  fprintf (stream, "%s,", reg_names[REGNO (x1)]);

  	  if (REG_P (x2))
  	    {
! 	      fprintf (stream, "%s", reg_names[REGNO (x2)]);
  	      return;
  	    }
  	  else if (GET_CODE (x2) == CONST_INT
--- 2458,2469 ----

        if (REG_P (x1))
  	{
! 	  fprintf (stream, "%s,", reg_names[MMIX_OUTPUT_REGNO (REGNO (x1))]);

  	  if (REG_P (x2))
  	    {
! 	      fprintf (stream, "%s",
! 		       reg_names[MMIX_OUTPUT_REGNO (REGNO (x2))]);
  	      return;
  	    }
  	  else if (GET_CODE (x2) == CONST_INT
*************** mmix_asm_output_reg_push (stream, regno)
*** 2455,2461 ****
    fprintf (stream, "\tSUBU %s,%s,8\n\tSTOU %s,%s,0\n",
  	   reg_names[MMIX_STACK_POINTER_REGNUM],
  	   reg_names[MMIX_STACK_POINTER_REGNUM],
! 	   reg_names[regno],
  	   reg_names[MMIX_STACK_POINTER_REGNUM]);
  }

--- 2494,2500 ----
    fprintf (stream, "\tSUBU %s,%s,8\n\tSTOU %s,%s,0\n",
  	   reg_names[MMIX_STACK_POINTER_REGNUM],
  	   reg_names[MMIX_STACK_POINTER_REGNUM],
! 	   reg_names[MMIX_OUTPUT_REGNO (regno)],
  	   reg_names[MMIX_STACK_POINTER_REGNUM]);
  }

*************** mmix_asm_output_reg_pop (stream, regno)
*** 2467,2473 ****
       int regno;
  {
    fprintf (stream, "\tLDOU %s,%s,0\n\tINCL %s,8\n",
! 	   reg_names[regno],
  	   reg_names[MMIX_STACK_POINTER_REGNUM],
  	   reg_names[MMIX_STACK_POINTER_REGNUM]);
  }
--- 2506,2512 ----
       int regno;
  {
    fprintf (stream, "\tLDOU %s,%s,0\n\tINCL %s,8\n",
! 	   reg_names[MMIX_OUTPUT_REGNO (regno)],
  	   reg_names[MMIX_STACK_POINTER_REGNUM],
  	   reg_names[MMIX_STACK_POINTER_REGNUM]);
  }
*************** int
*** 2527,2534 ****
  mmix_dbx_register_number (regno)
       int regno;
  {
!   /* FIXME: Implement final register renumbering if necessary.  (Use
!      target state in cfun).  */

    /* We need to renumber registers to get the number of the return address
       register in the range 0..255.  It is also space-saving if registers
--- 2566,2576 ----
  mmix_dbx_register_number (regno)
       int regno;
  {
!   /* Adjust the register number to the one it will be output as, dammit.
!      It'd be nice if we could check the assumption that we're filling a
!      gap, but every register between the last saved register and parameter
!      registers might be a valid parameter register.  */
!   regno = MMIX_OUTPUT_REGNO (regno);

    /* We need to renumber registers to get the number of the return address
       register in the range 0..255.  It is also space-saving if registers
Index: mmix.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mmix/mmix.h,v
retrieving revision 1.23
diff -p -c -r1.23 mmix.h
*** mmix.h	6 Apr 2002 08:20:34 -0000	1.23
--- mmix.h	30 Apr 2002 18:40:43 -0000
*************** Boston, MA 02111-1307, USA.  */
*** 49,55 ****
  #define MMIX_HIMULT_REGNUM 258
  #define MMIX_REMAINDER_REGNUM 260
  #define MMIX_ARG_POINTER_REGNUM 261
! #define MMIX_LAST_REGISTER_FILE_REGNUM 31

  /* Four registers; "ideally, these registers should be call-clobbered", so
     just grab a bunch of the common clobbered registers.  FIXME: Last
--- 49,55 ----
  #define MMIX_HIMULT_REGNUM 258
  #define MMIX_REMAINDER_REGNUM 260
  #define MMIX_ARG_POINTER_REGNUM 261
! #define MMIX_LAST_STACK_REGISTER_REGNUM 31

  /* Four registers; "ideally, these registers should be call-clobbered", so
     just grab a bunch of the common clobbered registers.  FIXME: Last
*************** extern struct rtx_def *mmix_compare_op1;
*** 91,96 ****
--- 91,97 ----
  struct machine_function
   {
     int has_landing_pad;
+    int highest_saved_stack_register;
   };

  /* For these target macros, there is no generic documentation here.  You
*************** const_section ()						\
*** 1196,1202 ****

  #define FUNCTION_MODE QImode

- /* When in due time we *will* have some specific headers.  */
  #define NO_IMPLICIT_EXTERN_C

  #define HANDLE_SYSV_PRAGMA
--- 1197,1202 ----
*************** const_section ()						\
*** 1205,1210 ****
--- 1205,1214 ----
  #define DOLLARS_IN_IDENTIFIERS 0
  #define NO_DOLLAR_IN_LABEL
  #define NO_DOT_IN_LABEL
+
+ /* Calculate the highest used supposed saved stack register.  */
+ #define MACHINE_DEPENDENT_REORG(INSN) \
+  mmix_machine_dependent_reorg (INSN)

  #endif /* GCC_MMIX_H */
  /*
Index: mmix.md
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mmix/mmix.md,v
retrieving revision 1.11
diff -p -c -r1.11 mmix.md
*** mmix.md	31 Mar 2002 18:38:57 -0000	1.11
--- mmix.md	30 Apr 2002 18:40:44 -0000
***************
*** 241,248 ****
  ;; One day we might persuade GCC to expand divisions with constants the
  ;; way MMIX does; giving the remainder the sign of the divisor.  But even
  ;; then, it might be good to have an option to divide the way "everybody
! ;; else" does.  Perhaps then, this option can be on by default.  Until
! ;; then, we do division and modulus in a library function.

  (define_insn "divmoddi4"
    [(set (match_operand:DI 0 "register_operand" "=r")
--- 241,250 ----
  ;; One day we might persuade GCC to expand divisions with constants the
  ;; way MMIX does; giving the remainder the sign of the divisor.  But even
  ;; then, it might be good to have an option to divide the way "everybody
! ;; else" does.  Perhaps then, this option can be on by default.  However,
! ;; it's not likely to happen because major (C, C++, Fortran) language
! ;; standards in effect at 2002-04-29 reportedly demand that the sign of
! ;; the remainder must follow the sign of the dividend.

  (define_insn "divmoddi4"
    [(set (match_operand:DI 0 "register_operand" "=r")

brgds, H-P


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]