This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
sched1 vs reload in the gp knockout match of the century
- To: gcc-patches at gcc dot gnu dot org
- Subject: sched1 vs reload in the gp knockout match of the century
- From: Richard Henderson <rth at cygnus dot com>
- Date: Tue, 13 Jun 2000 15:35:02 -0700
On ia64, the gp register is caller-saved, so calls are emitted like
(call (mem:qi (symbol_ref))
(set (reg:di gp) (reg:di tmp))
if we let sched1 have its way, the reload of gp can be shifted down
below an insn for which reload would like to spill constant data.
This because before reload, there's nothing to indicate that gp will
be needed. Which results in post-reload code like
(call (mem:qi (symbol_ref))
(set (reg:di r14) (plus:di (reg:di gp) (symbol_ref:di ".LC0")))
(set (reg:di f6) (mem:di (reg:di r14)))
(set (reg:di gp) (reg:di r35))
which doesn't work quite the way one might like.
This patch modifes sched1 such that a call plus any move insns
touching a hard register are considered a single scheduling group.
This not only fixes this ia64 gp problem but opens the possibility
of turning on sched1 for SMALL_REGISTER_CLASS machines. The only
open issue remaining there is for incomming register parameters,
which I've not considered here.
A related patch involves never removing sets of gp before reload.
Tested on {i686,alphaev6,ia64}-linux.
r~
* flow.c (insn_dead_p): Keep sets to PIC_OFFSET_TABLE_REGNUM
alive before reload.
* haifa-sched.c (struct deps): Add in_post_call_group_p.
(add_dependence): Handle notes between SCHED_GROUP_P insns.
(remove_dependence): Always define.
(set_sched_group_p): New.
(sched_analyze_2): Use it.
(sched_analyze_insn): Don't special-case naked uses. Look for
and extend in_post_call_group_p.
(sched_analyze): Clear stale SCHED_GROUP_P. Set in_post_call_group_p.
(init_deps): Clear in_post_call_group_p.
Index: flow.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/flow.c,v
retrieving revision 1.301
diff -c -p -d -r1.301 flow.c
*** flow.c 2000/06/13 21:47:38 1.301
--- flow.c 2000/06/13 22:17:51
*************** insn_dead_p (pbi, x, call_ok, notes)
*** 3955,3960 ****
--- 3955,3970 ----
return 0;
#endif
+ #ifdef PIC_OFFSET_TABLE_REGNUM
+ /* Before reload, do not allow sets of the pic register
+ to be deleted. Reload can insert references to
+ constant pool memory anywhere in the function, making
+ the PIC register live where it wasn't before. */
+ if (regno == PIC_OFFSET_TABLE_REGNUM && fixed_regs[regno]
+ && ! reload_completed)
+ return 0;
+ #endif
+
/* Otherwise, the set is dead. */
return 1;
}
Index: haifa-sched.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/haifa-sched.c,v
retrieving revision 1.154
diff -c -p -d -r1.154 haifa-sched.c
*** haifa-sched.c 2000/05/19 22:27:26 1.154
--- haifa-sched.c 2000/06/13 22:17:51
*************** struct deps
*** 277,286 ****
the last function call, must depend on this. */
rtx last_function_call;
! /* The LOG_LINKS field of this is a list of insns which use a pseudo register
! that does not already cross a call. We create dependencies between each
! of those insn and the next call insn, to ensure that they won't cross a call
! after scheduling is done. */
rtx sched_before_next_call;
/* Element N is the next insn that sets (hard or pseudo) register
--- 277,290 ----
the last function call, must depend on this. */
rtx last_function_call;
! /* Used to keep post-call psuedo/hard reg movements together with
! the call. */
! int in_post_call_group_p;
!
! /* The LOG_LINKS field of this is a list of insns which use a pseudo
! register that does not already cross a call. We create
! dependencies between each of those insn and the next call insn,
! to ensure that they won't cross a call after scheduling is done. */
rtx sched_before_next_call;
/* Element N is the next insn that sets (hard or pseudo) register
*************** static int q_size = 0;
*** 466,475 ****
/* Forward declarations. */
static void add_dependence PARAMS ((rtx, rtx, enum reg_note));
- #ifdef HAVE_cc0
static void remove_dependence PARAMS ((rtx, rtx));
- #endif
static rtx find_insn_list PARAMS ((rtx, rtx));
static int insn_unit PARAMS ((rtx));
static unsigned int blockage_range PARAMS ((int, rtx));
static void clear_units PARAMS ((void));
--- 470,478 ----
/* Forward declarations. */
static void add_dependence PARAMS ((rtx, rtx, enum reg_note));
static void remove_dependence PARAMS ((rtx, rtx));
static rtx find_insn_list PARAMS ((rtx, rtx));
+ static void set_sched_group_p PARAMS ((rtx));
static int insn_unit PARAMS ((rtx));
static unsigned int blockage_range PARAMS ((int, rtx));
static void clear_units PARAMS ((void));
*************** add_dependence (insn, elem, dep_type)
*** 822,848 ****
When HAVE_cc0, it is possible for NOTEs to exist between users and
setters of the condition codes, so we must skip past notes here.
Otherwise, NOTEs are impossible here. */
!
! next = NEXT_INSN (elem);
!
! #ifdef HAVE_cc0
! while (next && GET_CODE (next) == NOTE)
! next = NEXT_INSN (next);
! #endif
!
if (next && SCHED_GROUP_P (next)
&& GET_CODE (next) != CODE_LABEL)
{
/* Notes will never intervene here though, so don't bother checking
for them. */
/* We must reject CODE_LABELs, so that we don't get confused by one
that has LABEL_PRESERVE_P set, which is represented by the same
bit in the rtl as SCHED_GROUP_P. A CODE_LABEL can never be
SCHED_GROUP_P. */
- while (NEXT_INSN (next) && SCHED_GROUP_P (NEXT_INSN (next))
- && GET_CODE (NEXT_INSN (next)) != CODE_LABEL)
- next = NEXT_INSN (next);
/* Again, don't depend an insn on itself. */
if (insn == next)
return;
--- 825,848 ----
When HAVE_cc0, it is possible for NOTEs to exist between users and
setters of the condition codes, so we must skip past notes here.
Otherwise, NOTEs are impossible here. */
! next = next_nonnote_insn (elem);
if (next && SCHED_GROUP_P (next)
&& GET_CODE (next) != CODE_LABEL)
{
/* Notes will never intervene here though, so don't bother checking
for them. */
+ /* Hah! Wrong. */
/* We must reject CODE_LABELs, so that we don't get confused by one
that has LABEL_PRESERVE_P set, which is represented by the same
bit in the rtl as SCHED_GROUP_P. A CODE_LABEL can never be
SCHED_GROUP_P. */
+ rtx nnext;
+ while ((nnext = next_nonnote_insn (next)) != NULL
+ && SCHED_GROUP_P (nnext)
+ && GET_CODE (nnext) != CODE_LABEL)
+ next = nnext;
+
/* Again, don't depend an insn on itself. */
if (insn == next)
return;
*************** add_dependence (insn, elem, dep_type)
*** 861,867 ****
&& (INSN_BB (elem) != INSN_BB (insn)))
return;
-
/* If we already have a true dependency for ELEM, then we do not
need to do anything. Avoiding the list walk below can cut
compile times dramatically for some code. */
--- 861,866 ----
*************** add_dependence (insn, elem, dep_type)
*** 903,909 ****
#endif
}
- #ifdef HAVE_cc0
/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS
of INSN. Abort if not found. */
--- 902,907 ----
*************** remove_dependence (insn, elem)
*** 945,951 ****
abort ();
return;
}
! #endif /* HAVE_cc0 */
#ifndef INSN_SCHEDULING
void
--- 943,993 ----
abort ();
return;
}
!
! /* Return the INSN_LIST containing INSN in LIST, or NULL
! if LIST does not contain INSN. */
!
! static inline rtx
! find_insn_list (insn, list)
! rtx insn;
! rtx list;
! {
! while (list)
! {
! if (XEXP (list, 0) == insn)
! return list;
! list = XEXP (list, 1);
! }
! return 0;
! }
!
! /* Set SCHED_GROUP_P and care for the rest of the bookkeeping that
! goes along with that. */
!
! static void
! set_sched_group_p (insn)
! rtx insn;
! {
! rtx link, prev;
!
! SCHED_GROUP_P (insn) = 1;
!
! /* There may be a note before this insn now, but all notes will
! be removed before we actually try to schedule the insns, so
! it won't cause a problem later. We must avoid it here though. */
! prev = prev_nonnote_insn (insn);
!
! /* Make a copy of all dependencies on the immediately previous insn,
! and add to this insn. This is so that all the dependencies will
! apply to the group. Remove an explicit dependence on this insn
! as SCHED_GROUP_P now represents it. */
!
! if (find_insn_list (prev, LOG_LINKS (insn)))
! remove_dependence (insn, prev);
!
! for (link = LOG_LINKS (prev); link; link = XEXP (link, 1))
! add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
! }
#ifndef INSN_SCHEDULING
void
*************** is_exception_free (insn, bb_src, bb_trg)
*** 2711,2734 ****
We are careful to build only dependencies which actually exist, and
use transitivity to avoid building too many links. */
- /* Return the INSN_LIST containing INSN in LIST, or NULL
- if LIST does not contain INSN. */
-
- HAIFA_INLINE static rtx
- find_insn_list (insn, list)
- rtx insn;
- rtx list;
- {
- while (list)
- {
- if (XEXP (list, 0) == insn)
- return list;
- list = XEXP (list, 1);
- }
- return 0;
- }
-
-
/* Return 1 if the pair (insn, x) is found in (LIST, LIST1), or 0
otherwise. */
--- 2753,2758 ----
*************** sched_analyze_2 (deps, x, insn)
*** 3430,3459 ****
#ifdef HAVE_cc0
case CC0:
! {
! rtx link, prev;
!
! /* User of CC0 depends on immediately preceding insn. */
! SCHED_GROUP_P (insn) = 1;
!
! /* There may be a note before this insn now, but all notes will
! be removed before we actually try to schedule the insns, so
! it won't cause a problem later. We must avoid it here though. */
! prev = prev_nonnote_insn (insn);
!
! /* Make a copy of all dependencies on the immediately previous insn,
! and add to this insn. This is so that all the dependencies will
! apply to the group. Remove an explicit dependence on this insn
! as SCHED_GROUP_P now represents it. */
!
! if (find_insn_list (prev, LOG_LINKS (insn)))
! remove_dependence (insn, prev);
!
! for (link = LOG_LINKS (prev); link; link = XEXP (link, 1))
! add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
!
! return;
! }
#endif
case REG:
--- 3454,3462 ----
#ifdef HAVE_cc0
case CC0:
! /* User of CC0 depends on immediately preceding insn. */
! set_sched_group_p (insn);
! return;
#endif
case REG:
*************** sched_analyze_insn (deps, x, insn, loop_
*** 3778,3816 ****
reg_pending_sets_all = 0;
}
! /* Handle function calls and function returns created by the epilogue
! threading code. */
! if (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN)
! {
! rtx dep_insn;
! rtx prev_dep_insn;
! /* When scheduling instructions, we make sure calls don't lose their
! accompanying USE insns by depending them one on another in order.
! Also, we must do the same thing for returns created by the epilogue
! threading code. Note this code works only in this special case,
! because other passes make no guarantee that they will never emit
! an instruction between a USE and a RETURN. There is such a guarantee
! for USE instructions immediately before a call. */
! prev_dep_insn = insn;
! dep_insn = PREV_INSN (insn);
! while (GET_CODE (dep_insn) == INSN
! && GET_CODE (PATTERN (dep_insn)) == USE
! && GET_CODE (XEXP (PATTERN (dep_insn), 0)) == REG)
! {
! SCHED_GROUP_P (prev_dep_insn) = 1;
! /* Make a copy of all dependencies on dep_insn, and add to insn.
! This is so that all of the dependencies will apply to the
! group. */
! for (link = LOG_LINKS (dep_insn); link; link = XEXP (link, 1))
! add_dependence (insn, XEXP (link, 0), REG_NOTE_KIND (link));
! prev_dep_insn = dep_insn;
! dep_insn = PREV_INSN (dep_insn);
}
}
}
--- 3781,3829 ----
reg_pending_sets_all = 0;
}
! /* If a post-call group is still open, see if it should remain so.
! This insn must be a simple move of a hard reg to a pseudo or
! vice-versa.
! We must avoid moving these insns for correctness on
! SMALL_REGISTER_CLASS machines, and for special registers like
! PIC_OFFSET_TABLE_REGNUM. For simplicity, extend this to all
! hard regs for all targets. */
! if (deps->in_post_call_group_p)
! {
! rtx tmp, set = single_set (insn);
! int src_regno, dest_regno;
! if (set == NULL)
! goto end_call_group;
! tmp = SET_DEST (set);
! if (GET_CODE (tmp) == SUBREG)
! tmp = SUBREG_REG (tmp);
! if (GET_CODE (tmp) == REG)
! dest_regno = REGNO (tmp);
! else
! goto end_call_group;
! tmp = SET_SRC (set);
! if (GET_CODE (tmp) == SUBREG)
! tmp = SUBREG_REG (tmp);
! if (GET_CODE (tmp) == REG)
! src_regno = REGNO (tmp);
! else
! goto end_call_group;
! if (src_regno < FIRST_PSEUDO_REGISTER
! || dest_regno < FIRST_PSEUDO_REGISTER)
! {
! set_sched_group_p (insn);
! CANT_MOVE (insn) = 1;
! }
! else
! {
! end_call_group:
! deps->in_post_call_group_p = 0;
}
}
}
*************** sched_analyze (deps, head, tail)
*** 3834,3839 ****
--- 3847,3855 ----
/* Clear out the stale LOG_LINKS from flow. */
free_INSN_LIST_list (&LOG_LINKS (insn));
+ /* Clear out stale SCHED_GROUP_P. */
+ SCHED_GROUP_P (insn) = 0;
+
/* Make each JUMP_INSN a scheduling barrier for memory
references. */
if (GET_CODE (insn) == JUMP_INSN)
*************** sched_analyze (deps, head, tail)
*** 3847,3852 ****
--- 3863,3871 ----
rtx x;
register int i;
+ /* Clear out stale SCHED_GROUP_P. */
+ SCHED_GROUP_P (insn) = 0;
+
CANT_MOVE (insn) = 1;
/* Clear out the stale LOG_LINKS from flow. */
*************** sched_analyze (deps, head, tail)
*** 3933,3938 ****
--- 3952,3962 ----
/* last_function_call is now a list of insns. */
free_INSN_LIST_list (&deps->last_function_call);
deps->last_function_call = alloc_INSN_LIST (insn, NULL_RTX);
+
+ /* Before reload, begin a post-call group, so as to keep the
+ lifetimes of hard registers correct. */
+ if (! reload_completed)
+ deps->in_post_call_group_p = 1;
}
/* See comments on reemit_notes as to why we do this.
*************** init_deps (deps)
*** 6237,6242 ****
--- 6261,6267 ----
deps->pending_lists_length = 0;
deps->last_pending_memory_flush = 0;
deps->last_function_call = 0;
+ deps->in_post_call_group_p = 0;
deps->sched_before_next_call
= gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX,