This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Committed, MMIX: Close the gap between call-saved and call-clobberedregisters
- From: Hans-Peter Nilsson <hp at bitrange dot com>
- To: <gcc-patches at gcc dot gnu dot org>
- Date: Tue, 30 Apr 2002 15:24:46 -0400 (EDT)
- Subject: 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