This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: stupid.c, rip
On Sun, Jul 18, 1999 at 07:18:07PM -0700, Jason Merrill wrote:
> 11% seems pretty serious to me. We're already too slow.
With this change to global, the difference between two builds
of cc1 on x86 is in the noise:
109.81user 9.98system mainline
109.36user 9.66system patched
The EXECUTE_IF_SET_IN_REG_SET part of the change removes 1.3M
calls to bitmap_bit_p over the course of cccp.c, which was
about 6% of the runtime of the entire program. Not calling
back into global to try re-allocation takes care of most of
the rest of the time.
r~
* global.c (global_alloc): Set GLOBAL argument to reload to zero
if not optimizing.
(build_insn_chain): Restructure to iterate over basic blocks,
and use EXECUTE_IF_SET_IN_REG_SET.
Index: global.c
===================================================================
RCS file: /egcs/carton/cvsfiles/egcs/gcc/global.c,v
retrieving revision 1.27
diff -c -p -d -r1.27 global.c
*** global.c 1999/02/25 23:45:19 1.27
--- global.c 1999/07/19 17:48:16
*************** static void set_preference PROTO((rtx, r
*** 271,277 ****
static void dump_conflicts PROTO((FILE *));
static void reg_becomes_live PROTO((rtx, rtx));
static void reg_dies PROTO((int, enum machine_mode));
! static void build_insn_chain PROTO((rtx));
/* Perform allocation of pseudo-registers not allocated by local_alloc.
FILE is a file to output debugging information on,
--- 271,277 ----
static void dump_conflicts PROTO((FILE *));
static void reg_becomes_live PROTO((rtx, rtx));
static void reg_dies PROTO((int, enum machine_mode));
! static void build_insn_chain PROTO((void));
/* Perform allocation of pseudo-registers not allocated by local_alloc.
FILE is a file to output debugging information on,
*************** global_alloc (file)
*** 495,501 ****
/* If there is work to be done (at least one reg to allocate),
perform global conflict analysis and allocate the regs. */
! if (max_allocno > 0)
{
/* Scan all the insns and compute the conflicts among allocnos
and between allocnos and hard regs. */
--- 495,501 ----
/* If there is work to be done (at least one reg to allocate),
perform global conflict analysis and allocate the regs. */
! if (optimize && max_allocno > 0)
{
/* Scan all the insns and compute the conflicts among allocnos
and between allocnos and hard regs. */
*************** global_alloc (file)
*** 573,586 ****
/* Do the reloads now while the allocno data still exist, so that we can
try to assign new hard regs to any pseudo regs that are spilled. */
! #if 0 /* We need to eliminate regs even if there is no rtl code,
! for the sake of debugging information. */
! if (n_basic_blocks > 0)
! #endif
! {
! build_insn_chain (get_insns ());
! retval = reload (get_insns (), 1, file);
! }
free (conflicts);
return retval;
--- 573,580 ----
/* Do the reloads now while the allocno data still exist, so that we can
try to assign new hard regs to any pseudo regs that are spilled. */
! build_insn_chain ();
! retval = reload (get_insns (), optimize != 0, file);
free (conflicts);
return retval;
*************** reg_dies (regno, mode)
*** 1667,1762 ****
/* Walk the insns of the current function and build reload_insn_chain,
and record register life information. */
static void
! build_insn_chain (first)
! rtx first;
{
struct insn_chain **p = &reload_insn_chain;
struct insn_chain *prev = 0;
! int b = 0;
live_relevant_regs = ALLOCA_REG_SET ();
! for (; first; first = NEXT_INSN (first))
{
! struct insn_chain *c;
!
! if (first == BLOCK_HEAD (b))
! {
! int i;
! CLEAR_REG_SET (live_relevant_regs);
! for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
! if (REGNO_REG_SET_P (BASIC_BLOCK (b)->global_live_at_start, i)
! && ! TEST_HARD_REG_BIT (eliminable_regset, i))
! SET_REGNO_REG_SET (live_relevant_regs, i);
! for (; i < max_regno; i++)
! if (reg_renumber[i] >= 0
! && REGNO_REG_SET_P (BASIC_BLOCK (b)->global_live_at_start, i))
! SET_REGNO_REG_SET (live_relevant_regs, i);
! }
! if (GET_CODE (first) != NOTE && GET_CODE (first) != BARRIER)
{
! c = new_insn_chain ();
! c->prev = prev;
! prev = c;
! *p = c;
! p = &c->next;
! c->insn = first;
! c->block = b;
! COPY_REG_SET (c->live_before, live_relevant_regs);
! if (GET_RTX_CLASS (GET_CODE (first)) == 'i')
{
! rtx link;
!
! /* Mark the death of everything that dies in this instruction. */
! for (link = REG_NOTES (first); link; link = XEXP (link, 1))
! if (REG_NOTE_KIND (link) == REG_DEAD
! && GET_CODE (XEXP (link, 0)) == REG)
! reg_dies (REGNO (XEXP (link, 0)), GET_MODE (XEXP (link, 0)));
! /* Mark everything born in this instruction as live. */
! note_stores (PATTERN (first), reg_becomes_live);
! }
! /* Remember which registers are live at the end of the insn, before
! killing those with REG_UNUSED notes. */
! COPY_REG_SET (c->live_after, live_relevant_regs);
! if (GET_RTX_CLASS (GET_CODE (first)) == 'i')
! {
! rtx link;
! /* Mark anything that is set in this insn and then unused as dying. */
! for (link = REG_NOTES (first); link; link = XEXP (link, 1))
! if (REG_NOTE_KIND (link) == REG_UNUSED
! && GET_CODE (XEXP (link, 0)) == REG)
! reg_dies (REGNO (XEXP (link, 0)), GET_MODE (XEXP (link, 0)));
}
- }
-
- if (first == BLOCK_END (b))
- b++;
! /* Stop after we pass the end of the last basic block. Verify that
! no real insns are after the end of the last basic block.
!
! We may want to reorganize the loop somewhat since this test should
! always be the right exit test. */
! if (b == n_basic_blocks)
! {
! for (first = NEXT_INSN (first) ; first; first = NEXT_INSN (first))
! if (GET_RTX_CLASS (GET_CODE (first)) == 'i'
! && GET_CODE (PATTERN (first)) != USE)
! abort ();
! break;
}
}
FREE_REG_SET (live_relevant_regs);
*p = 0;
}
--- 1661,1748 ----
/* Walk the insns of the current function and build reload_insn_chain,
and record register life information. */
static void
! build_insn_chain ()
{
struct insn_chain **p = &reload_insn_chain;
struct insn_chain *prev = 0;
! int b;
live_relevant_regs = ALLOCA_REG_SET ();
! for (b = n_basic_blocks - 1; b >= 0; --b)
{
! basic_block bb = BASIC_BLOCK (b);
! rtx insn, end;
! int i;
! CLEAR_REG_SET (live_relevant_regs);
! EXECUTE_IF_SET_IN_REG_SET (bb->global_live_at_start, 0, i,
{
! if ((i < FIRST_PSEUDO_REGISTER
! && ! TEST_HARD_REG_BIT (eliminable_regset, i))
! || (i >= FIRST_PSEUDO_REGISTER
! && reg_renumber[i] >= 0))
! SET_REGNO_REG_SET (live_relevant_regs, i);
! });
! insn = bb->head, end = bb->end;
! while (1)
! {
! struct insn_chain *c;
! if (GET_CODE (insn) != NOTE)
{
! c = new_insn_chain ();
! c->prev = prev;
! prev = c;
! *p = c;
! p = &c->next;
! c->insn = insn;
! c->block = b;
! COPY_REG_SET (c->live_before, live_relevant_regs);
! if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
! {
! rtx link;
! /* Mark the death of everything that dies in this
! instruction. */
! for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
! if (REG_NOTE_KIND (link) == REG_DEAD
! && GET_CODE (XEXP (link, 0)) == REG)
! reg_dies (REGNO (XEXP (link, 0)),
! GET_MODE (XEXP (link, 0)));
! /* Mark everything born in this instruction as live. */
! note_stores (PATTERN (insn), reg_becomes_live);
! }
! /* Remember which registers are live at the end of the insn,
! before killing those with REG_UNUSED notes. */
! COPY_REG_SET (c->live_after, live_relevant_regs);
! if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
! {
! rtx link;
! /* Mark anything that is set in this insn and then unused
! as dying. */
! for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
! if (REG_NOTE_KIND (link) == REG_UNUSED
! && GET_CODE (XEXP (link, 0)) == REG)
! reg_dies (REGNO (XEXP (link, 0)),
! GET_MODE (XEXP (link, 0)));
! }
}
! if (insn == end)
! break;
! insn = NEXT_INSN (insn);
}
}
+
FREE_REG_SET (live_relevant_regs);
*p = 0;
}