From: Bernd Schmidt Date: Wed, 20 Dec 2000 17:19:39 +0000 (+0000) Subject: Support scheduling across extended basic blocks X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=18e720b324e90c02d4631174885b66f67d8014f2;p=platform%2Fupstream%2Fgcc.git Support scheduling across extended basic blocks From-SVN: r38400 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 10f00cb..1f19a80 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,22 @@ 2000-12-20 Bernd Schmidt + * Makefile.in (OBJS): Add sched-ebb.o. + (sched-ebb.o): New rule. + (sched-vis.o): Depend on hard-reg-set.h and $(BASIC_BLOCK_H). + (haifa-sched.o): Depend on insn-flags.h. + * haifa-sched.c: Include "insn-flags.h". + (priority): Don't access BLOCK_NUM, use the new contributes_to_priority + callback. + * rtl.h (schedule_ebbs): Declare. + * sched-int.h (struct sched_info): Add new members + contributes_to_priority and compute_jump_reg_dependencies. + * sched-rgn.c (contributes_to_priority, compute_jump_reg_dependencies): + New functions. + (region_sched_info): Add them. + * sched-vis.c: Include "hard-reg-set.h" and "basic-block.h". + * sched-ebb.c: New file. + * sched-deps.c (sched_analyze_insn): Add code to handle JUMP_INSNs. + * flow.c (ior_reg_cond, and_reg_cond, elim_reg_cond): Properly handle all relational operators. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index eadd5c4..1f6e925 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -738,7 +738,7 @@ OBJS = diagnostic.o version.o tree.o print-tree.o stor-layout.o fold-const.o \ mbchar.o splay-tree.o graph.o sbitmap.o resource.o hash.o predict.o \ lists.o ggc-common.o $(GGC) stringpool.o simplify-rtx.o ssa.o bb-reorder.o \ sibcall.o conflict.o timevar.o ifcvt.o dominance.o dependence.o dce.o \ - sched-vis.o sched-deps.o sched-rgn.o hashtab.o + sched-vis.o sched-deps.o sched-rgn.o sched-ebb.o hashtab.o BACKEND = toplev.o libbackend.a @@ -1457,15 +1457,18 @@ regmove.o : regmove.c $(CONFIG_H) system.h $(RTL_H) insn-config.h \ $(EXPR_H) insn-flags.h $(BASIC_BLOCK_H) toplev.h haifa-sched.o : haifa-sched.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \ $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \ - $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h + $(INSN_ATTR_H) insn-flags.h toplev.h $(RECOG_H) except.h sched-deps.o : haifa-sched.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \ $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \ $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h sched-rgn.o : haifa-sched.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \ $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \ $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h +sched-ebb.o : sched-ebb.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \ + $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h flags.h insn-config.h function.h \ + $(INSN_ATTR_H) toplev.h $(RECOG_H) except.h sched-vis.o : sched-vis.c $(CONFIG_H) system.h $(RTL_H) sched-int.h \ - $(INSN_ATTR_H) $(REGS_H) + hard-reg-set.h $(BASIC_BLOCK_H) $(INSN_ATTR_H) $(REGS_H) final.o : final.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h intl.h \ $(REGS_H) $(RECOG_H) conditions.h insn-config.h $(INSN_ATTR_H) function.h \ real.h output.h hard-reg-set.h insn-flags.h insn-codes.h gstab.h except.h \ diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c index 32ccf82..3498974 100644 --- a/gcc/haifa-sched.c +++ b/gcc/haifa-sched.c @@ -144,6 +144,7 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "flags.h" #include "insn-config.h" #include "insn-attr.h" +#include "insn-flags.h" #include "except.h" #include "toplev.h" #include "recog.h" @@ -740,7 +741,7 @@ priority (insn) next = XEXP (link, 0); /* Critical path is meaningful in block boundaries only. */ - if (BLOCK_NUM (next) != BLOCK_NUM (insn)) + if (! (*current_sched_info->contributes_to_priority) (next, insn)) continue; next_priority = insn_cost (insn, link, next) + priority (next); diff --git a/gcc/rtl.h b/gcc/rtl.h index 79dd106..ce35cd8 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -1754,6 +1754,7 @@ extern void dump_combine_total_stats PARAMS ((FILE *)); /* In sched.c. */ #ifdef BUFSIZ extern void schedule_insns PARAMS ((FILE *)); +extern void schedule_ebbs PARAMS ((FILE *)); #endif extern void fix_sched_param PARAMS ((const char *, const char *)); diff --git a/gcc/sched-deps.c b/gcc/sched-deps.c index b5f7328..396b519 100644 --- a/gcc/sched-deps.c +++ b/gcc/sched-deps.c @@ -913,6 +913,53 @@ sched_analyze_insn (deps, x, insn, loop_notes) sched_analyze_2 (deps, XEXP (link, 0), insn); } + if (GET_CODE (insn) == JUMP_INSN) + { + rtx next, u, pending, pending_mem; + next = next_nonnote_insn (insn); + if (next && GET_CODE (next) == BARRIER) + { + for (i = 0; i < maxreg; i++) + { + for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + for (u = deps->reg_last_clobbers[i]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + for (u = deps->reg_last_uses[i]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + } + } + else + { + regset_head tmp; + INIT_REG_SET (&tmp); + + (*current_sched_info->compute_jump_reg_dependencies) (insn, &tmp); + EXECUTE_IF_SET_IN_REG_SET + (&tmp, 0, i, + { + for (u = deps->reg_last_sets[i]; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + deps->reg_last_uses[i] + = alloc_INSN_LIST (insn, deps->reg_last_uses[i]); + }); + + CLEAR_REG_SET (&tmp); + } + pending = deps->pending_write_insns; + pending_mem = deps->pending_write_mems; + while (pending) + { + add_dependence (insn, XEXP (pending, 0), 0); + + pending = XEXP (pending, 1); + pending_mem = XEXP (pending_mem, 1); + } + + for (u = deps->last_pending_memory_flush; u; u = XEXP (u, 1)) + add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI); + } + /* If there is a {LOOP,EHREGION}_{BEG,END} note in the middle of a basic block, then we must be sure that no instructions are scheduled across it. Otherwise, the reg_n_refs info (which depends on loop_depth) would diff --git a/gcc/sched-ebb.c b/gcc/sched-ebb.c new file mode 100644 index 0000000..1ff7cea --- /dev/null +++ b/gcc/sched-ebb.c @@ -0,0 +1,365 @@ +/* Instruction scheduling pass. + Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000 Free Software Foundation, Inc. + Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by, + and currently maintained by, Jim Wilson (wilson@cygnus.com) + +This file is part of GNU CC. + +GNU CC is 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 2, or (at your option) any +later version. + +GNU CC is 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 the Free +the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +#include "config.h" +#include "system.h" +#include "toplev.h" +#include "rtl.h" +#include "tm_p.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "regs.h" +#include "function.h" +#include "flags.h" +#include "insn-config.h" +#include "insn-attr.h" +#include "except.h" +#include "toplev.h" +#include "recog.h" +#include "sched-int.h" + +/* The number of insns to be scheduled in total. */ +static int target_n_insns; +/* The number of insns scheduled so far. */ +static int sched_n_insns; + +/* Implementations of the sched_info functions for region scheduling. */ +static void init_ready_list PARAMS ((struct ready_list *)); +static int can_schedule_ready_p PARAMS ((rtx)); +static int new_ready PARAMS ((rtx)); +static int schedule_more_p PARAMS ((void)); +static const char *print_insn PARAMS ((rtx, int)); +static int rank PARAMS ((rtx, rtx)); +static int contributes_to_priority PARAMS ((rtx, rtx)); +static void compute_jump_reg_dependencies PARAMS ((rtx, regset)); +static void schedule_ebb PARAMS ((rtx, rtx)); + +/* Return nonzero if there are more insns that should be scheduled. */ + +static int +schedule_more_p () +{ + return sched_n_insns < target_n_insns; +} + +/* Add all insns that are initially ready to the ready list READY. Called + once before scheduling a set of insns. */ + +static void +init_ready_list (ready) + struct ready_list *ready; +{ + rtx prev_head = current_sched_info->prev_head; + rtx next_tail = current_sched_info->next_tail; + rtx insn; + + target_n_insns = 0; + sched_n_insns = 0; + +#if 0 + /* Print debugging information. */ + if (sched_verbose >= 5) + debug_dependencies (); +#endif + + /* Initialize ready list with all 'ready' insns in target block. + Count number of insns in the target block being scheduled. */ + for (insn = NEXT_INSN (prev_head); insn != next_tail; insn = NEXT_INSN (insn)) + { + rtx next; + + if (! INSN_P (insn)) + continue; + next = NEXT_INSN (insn); + + if (INSN_DEP_COUNT (insn) == 0 + && (SCHED_GROUP_P (next) == 0 || ! INSN_P (next))) + ready_add (ready, insn); + if (!(SCHED_GROUP_P (insn))) + target_n_insns++; + } +} + +/* Called after taking INSN from the ready list. Returns nonzero if this + insn can be scheduled, nonzero if we should silently discard it. */ + +static int +can_schedule_ready_p (insn) + rtx insn ATTRIBUTE_UNUSED; +{ + sched_n_insns++; + return 1; +} + +/* Called after INSN has all its dependencies resolved. Return nonzero + if it should be moved to the ready list or the queue, or zero if we + should silently discard it. */ +static int +new_ready (next) + rtx next ATTRIBUTE_UNUSED; +{ + return 1; +} + +/* Return a string that contains the insn uid and optionally anything else + necessary to identify this insn in an output. It's valid to use a + static buffer for this. The ALIGNED parameter should cause the string + to be formatted so that multiple output lines will line up nicely. */ + +static const char * +print_insn (insn, aligned) + rtx insn; + int aligned ATTRIBUTE_UNUSED; +{ + static char tmp[80]; + + sprintf (tmp, "%4d", INSN_UID (insn)); + return tmp; +} + +/* Compare priority of two insns. Return a positive number if the second + insn is to be preferred for scheduling, and a negative one if the first + is to be preferred. Zero if they are equally good. */ + +static int +rank (insn1, insn2) + rtx insn1 ATTRIBUTE_UNUSED, insn2 ATTRIBUTE_UNUSED; +{ + return 0; +} + +/* NEXT is an instruction that depends on INSN (a backward dependence); + return nonzero if we should include this dependence in priority + calculations. */ + +static int +contributes_to_priority (next, insn) + rtx next ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED; +{ + return 1; +} + +/* INSN is a JUMP_INSN. Store the set of registers that must be considered + to be set by this jump in SET. */ + +static void +compute_jump_reg_dependencies (insn, set) + rtx insn; + regset set; +{ + basic_block b = BLOCK_FOR_INSN (insn); + edge e; + for (e = b->succ; e; e = e->succ_next) + if ((e->flags & EDGE_FALLTHRU) == 0) + { + bitmap_operation (set, set, e->dest->global_live_at_start, + BITMAP_IOR); + } +} + +/* Used in schedule_insns to initialize current_sched_info for scheduling + regions (or single basic blocks). */ + +static struct sched_info ebb_sched_info = +{ + init_ready_list, + can_schedule_ready_p, + schedule_more_p, + new_ready, + rank, + print_insn, + contributes_to_priority, + compute_jump_reg_dependencies, + + NULL, NULL, + NULL, NULL, + 0 +}; + +/* Schedule a single extended basic block, defined by the boundaries HEAD + and TAIL. */ + +static void +schedule_ebb (head, tail) + rtx head, tail; +{ + int n_insns; + struct deps tmp_deps; + + if (no_real_insns_p (head, tail)) + return; + + init_deps_global (); + + /* Compute LOG_LINKS. */ + init_deps (&tmp_deps); + sched_analyze (&tmp_deps, head, tail); + free_deps (&tmp_deps); + + /* Compute INSN_DEPEND. */ + compute_forward_dependences (head, tail); + + /* Set priorities. */ + n_insns = set_priorities (head, tail); + + current_sched_info->prev_head = PREV_INSN (head); + current_sched_info->next_tail = NEXT_INSN (tail); + + if (write_symbols != NO_DEBUG) + { + save_line_notes (0, head, tail); + rm_line_notes (head, tail); + } + + /* rm_other_notes only removes notes which are _inside_ the + block---that is, it won't remove notes before the first real insn + or after the last real insn of the block. So if the first insn + has a REG_SAVE_NOTE which would otherwise be emitted before the + insn, it is redundant with the note before the start of the + block, and so we have to take it out. + + FIXME: Probably the same thing should be done with REG_SAVE_NOTEs + referencing NOTE_INSN_SETJMP at the end of the block. */ + if (INSN_P (head)) + { + rtx note; + + for (note = REG_NOTES (head); note; note = XEXP (note, 1)) + if (REG_NOTE_KIND (note) == REG_SAVE_NOTE) + { + if (INTVAL (XEXP (note, 0)) != NOTE_INSN_SETJMP) + { + remove_note (head, note); + note = XEXP (note, 1); + remove_note (head, note); + } + else + note = XEXP (note, 1); + } + } + + /* Remove remaining note insns from the block, save them in + note_list. These notes are restored at the end of + schedule_block (). */ + rm_other_notes (head, tail); + + current_sched_info->queue_must_finish_empty = 1; + + schedule_block (-1, n_insns); + + /* Sanity check: verify that all region insns were scheduled. */ + if (sched_n_insns != n_insns) + abort (); + head = current_sched_info->head; + tail = current_sched_info->tail; + + if (write_symbols != NO_DEBUG) + restore_line_notes (0, head, tail); + + finish_deps_global (); +} + +/* The one entry point in this file. DUMP_FILE is the dump file for + this pass. */ + +void +schedule_ebbs (dump_file) + FILE *dump_file; +{ + int i; + + /* Taking care of this degenerate case makes the rest of + this code simpler. */ + if (n_basic_blocks == 0) + return; + + sched_init (dump_file); + + current_sched_info = &ebb_sched_info; + + allocate_reg_life_data (); + compute_bb_for_insn (get_max_uid ()); + + /* Schedule every region in the subroutine. */ + for (i = 0; i < n_basic_blocks; i++) + { + rtx head = BASIC_BLOCK (i)->head; + rtx tail; + + for (;;) + { + basic_block b = BASIC_BLOCK (i); + edge e; + tail = b->end; + if (i + 1 == n_basic_blocks + || GET_CODE (BLOCK_HEAD (i + 1)) == CODE_LABEL) + break; + for (e = b->succ; e; e = e->succ_next) + if ((e->flags & EDGE_FALLTHRU) != 0) + break; + if (! e) + break; + if (GET_CODE (tail) == JUMP_INSN) + { + rtx x = find_reg_note (tail, REG_BR_PROB, 0); + if (x) + { + int pred_val = INTVAL (XEXP (x, 0)); + if (pred_val > REG_BR_PROB_BASE / 2) + break; + } + } + + i++; + } + + /* Blah. We should fix the rest of the code not to get confused by + a note or two. */ + while (head != tail) + { + if (GET_CODE (head) == NOTE) + head = NEXT_INSN (head); + else if (GET_CODE (tail) == NOTE) + tail = PREV_INSN (tail); + else if (GET_CODE (head) == CODE_LABEL) + head = NEXT_INSN (head); + else + break; + } + + schedule_ebb (head, tail); + } + + /* It doesn't make much sense to try and update life information here - we + probably messed up even the flow graph. */ + + /* Reposition the prologue and epilogue notes in case we moved the + prologue/epilogue insns. */ + if (reload_completed) + reposition_prologue_and_epilogue_notes (get_insns ()); + + if (write_symbols != NO_DEBUG) + rm_redundant_line_notes (); + + sched_finish (); +} diff --git a/gcc/sched-int.h b/gcc/sched-int.h index ffa23a1..f59f3a8 100644 --- a/gcc/sched-int.h +++ b/gcc/sched-int.h @@ -115,6 +115,13 @@ struct sched_info static buffer for this. The ALIGNED parameter should cause the string to be formatted so that multiple output lines will line up nicely. */ const char *(*print_insn) PARAMS ((rtx, int)); + /* Return nonzero if an insn should be included in priority + calculations. */ + int (*contributes_to_priority) PARAMS ((rtx, rtx)); + /* Called when computing dependencies for a JUMP_INSN. This function + should store the set of registers that must be considered as set by + the jump in the regset. */ + void (*compute_jump_reg_dependencies) PARAMS ((rtx, regset)); /* The boundaries of the set of insns to be scheduled. */ rtx prev_head, next_tail; diff --git a/gcc/sched-rgn.c b/gcc/sched-rgn.c index 9e1bfd9..db4cc87 100644 --- a/gcc/sched-rgn.c +++ b/gcc/sched-rgn.c @@ -2042,6 +2042,8 @@ static int new_ready PARAMS ((rtx)); static int schedule_more_p PARAMS ((void)); static const char *rgn_print_insn PARAMS ((rtx, int)); static int rgn_rank PARAMS ((rtx, rtx)); +static int contributes_to_priority PARAMS ((rtx, rtx)); +static void compute_jump_reg_dependencies PARAMS ((rtx, regset)); /* Return nonzero if there are more insns that should be scheduled. */ @@ -2302,6 +2304,29 @@ rgn_rank (insn1, insn2) return 0; } +/* NEXT is an instruction that depends on INSN (a backward dependence); + return nonzero if we should include this dependence in priority + calculations. */ + +static int +contributes_to_priority (next, insn) + rtx next, insn; +{ + return BLOCK_NUM (next) == BLOCK_NUM (insn); +} + +/* INSN is a JUMP_INSN. Store the set of registers that must be considered + to be set by this jump in SET. */ + +static void +compute_jump_reg_dependencies (insn, set) + rtx insn ATTRIBUTE_UNUSED; + regset set ATTRIBUTE_UNUSED; +{ + /* Nothing to do here, since we postprocess jumps in + add_branch_dependences. */ +} + /* Used in schedule_insns to initialize current_sched_info for scheduling regions (or single basic blocks). */ @@ -2313,6 +2338,8 @@ static struct sched_info region_sched_info = new_ready, rgn_rank, rgn_print_insn, + contributes_to_priority, + compute_jump_reg_dependencies, NULL, NULL, NULL, NULL, diff --git a/gcc/sched-vis.c b/gcc/sched-vis.c index 99489c9..764876c 100644 --- a/gcc/sched-vis.c +++ b/gcc/sched-vis.c @@ -27,6 +27,8 @@ the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "rtl.h" #include "tm_p.h" #include "regs.h" +#include "hard-reg-set.h" +#include "basic-block.h" #include "insn-attr.h" #include "sched-int.h"