summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rwxr-xr-xgcc/Makefile.in4
-rwxr-xr-xgcc/combine.c8
-rwxr-xr-xgcc/config/arm/thumb.h8
-rwxr-xr-xgcc/config/arm/thumb.h.orig8
-rwxr-xr-xgcc/config/arm/thumb_000513.h8
-rwxr-xr-xgcc/config/arm/thumb_020422.h8
-rwxr-xr-xgcc/config/arm/thumb_020428.h8
-rwxr-xr-xgcc/final.c716
-rwxr-xr-xgcc/flags.h20
-rwxr-xr-xgcc/function.BAK8
-rwxr-xr-xgcc/function.c8
-rwxr-xr-xgcc/function_990206.c8
-rwxr-xr-xgcc/haifa-sched.c8744
-rwxr-xr-xgcc/integrate.c9
-rwxr-xr-xgcc/jump.c29
-rwxr-xr-xgcc/profile.c1701
-rwxr-xr-xgcc/reorg.c20
-rwxr-xr-xgcc/rtl.c4
-rwxr-xr-xgcc/rtl.h29
-rwxr-xr-xgcc/rtl_020422.c4
-rwxr-xr-xgcc/rtl_020422.h29
-rwxr-xr-xgcc/sched.c7
-rwxr-xr-xgcc/toplev.c127
23 files changed, 25 insertions, 11490 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6fe3738..39446a0 100755
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -612,7 +612,7 @@ OBJS = toplev.o version.o tree.o print-tree.o stor-layout.o fold-const.o \
insn-opinit.o insn-recog.o insn-extract.o insn-output.o insn-emit.o \
$(CYGNUS-LOCAL-lcm) lcm.o \
$(CYGNUS-LOCAL-range) range.o \
- profile.o insn-attrtab.o $(out_object_file) getpwd.o $(EXTRA_OBJS) convert.o \
+ insn-attrtab.o $(out_object_file) getpwd.o $(EXTRA_OBJS) convert.o \
mbchar.o dyn-string.o splay-tree.o graph.o sbitmap.o resource.o
# GEN files are listed separately, so they can be built before doing parallel
@@ -1417,8 +1417,6 @@ resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h system.h \
# CYGNUS LOCAL lcm
lcm.o : lcm.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \
real.h insn-config.h $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H)
-profile.o : profile.c $(CONFIG_H) system.h $(RTL_H) flags.h insn-flags.h \
- $(TREE_H) output.h $(REGS_H) toplev.h insn-config.h
loop.o : loop.c $(CONFIG_H) system.h $(RTL_H) flags.h loop.h insn-config.h \
insn-flags.h $(REGS_H) hard-reg-set.h $(RECOG_H) $(EXPR_H) real.h \
toplev.h varray.h
diff --git a/gcc/combine.c b/gcc/combine.c
index ea7859b..3fd6fe9 100755
--- a/gcc/combine.c
+++ b/gcc/combine.c
@@ -11531,14 +11531,6 @@ distribute_notes (notes, from_insn, i3, i2, elim_i2, elim_i1)
next_note = XEXP (note, 1);
switch (REG_NOTE_KIND (note))
{
- case REG_BR_PROB:
- case REG_EXEC_COUNT:
- /* Doesn't matter much where we put this, as long as it's somewhere.
- It is preferable to keep these notes on branches, which is most
- likely to be i3. */
- place = i3;
- break;
-
case REG_EH_REGION:
/* This note must remain with the call. It should not be possible
for both I2 and I3 to be a call. */
diff --git a/gcc/config/arm/thumb.h b/gcc/config/arm/thumb.h
index 528b250..9cd719a 100755
--- a/gcc/config/arm/thumb.h
+++ b/gcc/config/arm/thumb.h
@@ -744,14 +744,6 @@ int thumb_shiftable_const ();
#define FUNCTION_EPILOGUE(FILE,SIZE) thumb_function_epilogue((FILE),(SIZE))
-/* Generating code for profiling */
-#define FUNCTION_PROFILER(STREAM,LABELNO) \
-{ \
- fprintf ((STREAM), "\tmov\\tip, lr\n"); \
- fprintf ((STREAM), "\tbl\tmcount\n"); \
- fprintf ((STREAM), "\t.word\tLP%d\n", (LABELNO)); \
-}
-
/* Implementing the Varargs Macros */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
diff --git a/gcc/config/arm/thumb.h.orig b/gcc/config/arm/thumb.h.orig
index 528b250..9cd719a 100755
--- a/gcc/config/arm/thumb.h.orig
+++ b/gcc/config/arm/thumb.h.orig
@@ -744,14 +744,6 @@ int thumb_shiftable_const ();
#define FUNCTION_EPILOGUE(FILE,SIZE) thumb_function_epilogue((FILE),(SIZE))
-/* Generating code for profiling */
-#define FUNCTION_PROFILER(STREAM,LABELNO) \
-{ \
- fprintf ((STREAM), "\tmov\\tip, lr\n"); \
- fprintf ((STREAM), "\tbl\tmcount\n"); \
- fprintf ((STREAM), "\t.word\tLP%d\n", (LABELNO)); \
-}
-
/* Implementing the Varargs Macros */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
diff --git a/gcc/config/arm/thumb_000513.h b/gcc/config/arm/thumb_000513.h
index fd38325..a5c25b9 100755
--- a/gcc/config/arm/thumb_000513.h
+++ b/gcc/config/arm/thumb_000513.h
@@ -736,14 +736,6 @@ int thumb_shiftable_const ();
#define FUNCTION_EPILOGUE(FILE,SIZE) thumb_function_epilogue((FILE),(SIZE))
-/* Generating code for profiling */
-#define FUNCTION_PROFILER(STREAM,LABELNO) \
-{ \
- fprintf ((STREAM), "\tmov\\tip, lr\n"); \
- fprintf ((STREAM), "\tbl\tmcount\n"); \
- fprintf ((STREAM), "\t.word\tLP%d\n", (LABELNO)); \
-}
-
/* Implementing the Varargs Macros */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
diff --git a/gcc/config/arm/thumb_020422.h b/gcc/config/arm/thumb_020422.h
index e709738..554ed1d 100755
--- a/gcc/config/arm/thumb_020422.h
+++ b/gcc/config/arm/thumb_020422.h
@@ -772,14 +772,6 @@ typedef struct
#define FUNCTION_EPILOGUE(FILE,SIZE) thumb_function_epilogue((FILE),(SIZE))
-/* Generating code for profiling */
-#define FUNCTION_PROFILER(STREAM,LABELNO) \
-{ \
- fprintf ((STREAM), "\tmov\\tip, lr\n"); \
- fprintf ((STREAM), "\tbl\tmcount\n"); \
- fprintf ((STREAM), "\t.word\tLP%d\n", (LABELNO)); \
-}
-
/* Implementing the Varargs Macros */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
diff --git a/gcc/config/arm/thumb_020428.h b/gcc/config/arm/thumb_020428.h
index 648d4b8..8bba8d0 100755
--- a/gcc/config/arm/thumb_020428.h
+++ b/gcc/config/arm/thumb_020428.h
@@ -772,14 +772,6 @@ typedef struct
#define FUNCTION_EPILOGUE(FILE,SIZE) thumb_function_epilogue((FILE),(SIZE))
-/* Generating code for profiling */
-#define FUNCTION_PROFILER(STREAM,LABELNO) \
-{ \
- fprintf ((STREAM), "\tmov\\tip, lr\n"); \
- fprintf ((STREAM), "\tbl\tmcount\n"); \
- fprintf ((STREAM), "\t.word\tLP%d\n", (LABELNO)); \
-}
-
/* Implementing the Varargs Macros */
#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL) \
diff --git a/gcc/final.c b/gcc/final.c
index 70022e0..7e3e8e5 100755
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -74,19 +74,7 @@ extern struct obstack *rtl_obstack;
/* END CYGNUS LOCAL */
/* Get N_SLINE and N_SOL from stab.h if we can expect the file to exist. */
-#if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
-#include "dbxout.h"
-#if defined (USG) || !defined (HAVE_STAB_H)
-#include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */
-#else
-#include <stab.h>
-#endif
-#endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
-
-#ifdef XCOFF_DEBUGGING_INFO
-#include "xcoffout.h"
-#endif
#ifdef DWARF_DEBUGGING_INFO
#include "dwarfout.h"
@@ -96,9 +84,6 @@ extern struct obstack *rtl_obstack;
#include "dwarf2out.h"
#endif
-#ifdef SDB_DEBUGGING_INFO
-#include "sdbout.h"
-#endif
/* .stabd code for line number. */
#ifndef N_SLINE
@@ -158,13 +143,6 @@ static int high_function_linenum;
/* Filename of last NOTE. */
static char *last_filename;
-/* Number of basic blocks seen so far;
- used if profile_block_flag is set. */
-static int count_basic_blocks;
-
-/* Number of instrumented arcs when profile_arc_flag is set. */
-extern int count_instrumented_arcs;
-
extern int length_unit_log; /* This is defined in insn-attrtab.c. */
/* Nonzero while outputting an `asm' with operands.
@@ -179,10 +157,6 @@ static unsigned int insn_noperands;
static rtx last_ignored_compare = 0;
-/* Flag indicating this insn is the start of a new basic block. */
-
-static int new_block = 1;
-
/* All the symbol-blocks (levels of scoping) in the compilation
are assigned sequence numbers in order of appearance of the
beginnings of the symbol-blocks. Both final and dbxout do this,
@@ -239,10 +213,6 @@ char regs_ever_live[FIRST_PSEUDO_REGISTER];
int frame_pointer_needed;
-/* Assign unique numbers to labels generated for profiling. */
-
-int profile_label_no;
-
/* Length so far allocated in PENDING_BLOCKS. */
static int max_block_depth;
@@ -284,29 +254,6 @@ static int dialect_number;
static char *line_note_exists;
-/* Linked list to hold line numbers for each basic block. */
-
-struct bb_list {
- struct bb_list *next; /* pointer to next basic block */
- int line_num; /* line number */
- int file_label_num; /* LPBC<n> label # for stored filename */
- int func_label_num; /* LPBC<n> label # for stored function name */
-};
-
-static struct bb_list *bb_head = 0; /* Head of basic block list */
-static struct bb_list **bb_tail = &bb_head; /* Ptr to store next bb ptr */
-static int bb_file_label_num = -1; /* Current label # for file */
-static int bb_func_label_num = -1; /* Current label # for func */
-
-/* Linked list to hold the strings for each file and function name output. */
-
-struct bb_str {
- struct bb_str *next; /* pointer to next string */
- char *string; /* string */
- int label_num; /* label number */
- int length; /* string length */
-};
-
/* CYGNUS LOCAL LRS */
/* Current marker number for live ranges. */
extern int range_max_number;
@@ -314,17 +261,9 @@ extern int range_max_number;
extern rtx peephole PROTO((rtx));
-static struct bb_str *sbb_head = 0; /* Head of string list. */
-static struct bb_str **sbb_tail = &sbb_head; /* Ptr to store next bb str */
-static int sbb_label_num = 0; /* Last label used */
-
#ifdef HAVE_ATTR_length
static int asm_insn_count PROTO((rtx));
#endif
-static void profile_function PROTO((FILE *));
-static void profile_after_prologue PROTO((FILE *));
-static void add_bb PROTO((FILE *));
-static int add_bb_string PROTO((char *, int));
static void output_source_line PROTO((FILE *, rtx));
static rtx walk_alter_subreg PROTO((rtx));
static void output_asm_name PROTO((void));
@@ -365,248 +304,6 @@ void
end_final (filename)
char *filename;
{
- int i;
-
- if (profile_block_flag || profile_arc_flag)
- {
- char name[20];
- int align = exact_log2 (BIGGEST_ALIGNMENT / BITS_PER_UNIT);
- int size, rounded;
- struct bb_list *ptr;
- struct bb_str *sptr;
- int long_bytes = LONG_TYPE_SIZE / BITS_PER_UNIT;
- int pointer_bytes = POINTER_SIZE / BITS_PER_UNIT;
-
- if (profile_block_flag)
- size = long_bytes * count_basic_blocks;
- else
- size = long_bytes * count_instrumented_arcs;
- rounded = size;
-
- rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
- rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
- * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
-
- data_section ();
-
- /* Output the main header, of 11 words:
- 0: 1 if this file is initialized, else 0.
- 1: address of file name (LPBX1).
- 2: address of table of counts (LPBX2).
- 3: number of counts in the table.
- 4: always 0, for compatibility with Sun.
-
- The following are GNU extensions:
-
- 5: address of table of start addrs of basic blocks (LPBX3).
- 6: Number of bytes in this header.
- 7: address of table of function names (LPBX4).
- 8: address of table of line numbers (LPBX5) or 0.
- 9: address of table of file names (LPBX6) or 0.
- 10: space reserved for basic block profiling. */
-
- ASM_OUTPUT_ALIGN (asm_out_file, align);
-
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 0);
- /* zero word */
- assemble_integer (const0_rtx, long_bytes, 1);
-
- /* address of filename */
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 1);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes, 1);
-
- /* address of count table */
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes, 1);
-
- /* count of the # of basic blocks or # of instrumented arcs */
- if (profile_block_flag)
- assemble_integer (GEN_INT (count_basic_blocks), long_bytes, 1);
- else
- assemble_integer (GEN_INT (count_instrumented_arcs), long_bytes,
- 1);
-
- /* zero word (link field) */
- assemble_integer (const0_rtx, pointer_bytes, 1);
-
- /* address of basic block start address table */
- if (profile_block_flag)
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes,
- 1);
- }
- else
- assemble_integer (const0_rtx, pointer_bytes, 1);
-
- /* byte count for extended structure. */
- assemble_integer (GEN_INT (10 * UNITS_PER_WORD), long_bytes, 1);
-
- /* address of function name table */
- if (profile_block_flag)
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 4);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes,
- 1);
- }
- else
- assemble_integer (const0_rtx, pointer_bytes, 1);
-
- /* address of line number and filename tables if debugging. */
- if (write_symbols != NO_DEBUG && profile_block_flag)
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 5);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes, 1);
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 6);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes, 1);
- }
- else
- {
- assemble_integer (const0_rtx, pointer_bytes, 1);
- assemble_integer (const0_rtx, pointer_bytes, 1);
- }
-
- /* space for extension ptr (link field) */
- assemble_integer (const0_rtx, UNITS_PER_WORD, 1);
-
- /* Output the file name changing the suffix to .d for Sun tcov
- compatibility. */
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 1);
- {
- char *cwd = getpwd ();
- int len = strlen (filename) + strlen (cwd) + 1;
- char *data_file = (char *) alloca (len + 4);
-
- strcpy (data_file, cwd);
- strcat (data_file, "/");
- strcat (data_file, filename);
- strip_off_ending (data_file, len);
- if (profile_block_flag)
- strcat (data_file, ".d");
- else
- strcat (data_file, ".da");
- assemble_string (data_file, strlen (data_file) + 1);
- }
-
- /* Make space for the table of counts. */
- if (size == 0)
- {
- /* Realign data section. */
- ASM_OUTPUT_ALIGN (asm_out_file, align);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 2);
- if (size != 0)
- assemble_zeros (size);
- }
- else
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
-#ifdef ASM_OUTPUT_SHARED_LOCAL
- if (flag_shared_data)
- ASM_OUTPUT_SHARED_LOCAL (asm_out_file, name, size, rounded);
- else
-#endif
-#ifdef ASM_OUTPUT_ALIGNED_DECL_LOCAL
- ASM_OUTPUT_ALIGNED_DECL_LOCAL (asm_out_file, NULL_TREE, name, size,
- BIGGEST_ALIGNMENT);
-#else
-#ifdef ASM_OUTPUT_ALIGNED_LOCAL
- ASM_OUTPUT_ALIGNED_LOCAL (asm_out_file, name, size,
- BIGGEST_ALIGNMENT);
-#else
- ASM_OUTPUT_LOCAL (asm_out_file, name, size, rounded);
-#endif
-#endif
- }
-
- /* Output any basic block strings */
- if (profile_block_flag)
- {
- readonly_data_section ();
- if (sbb_head)
- {
- ASM_OUTPUT_ALIGN (asm_out_file, align);
- for (sptr = sbb_head; sptr != 0; sptr = sptr->next)
- {
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBC",
- sptr->label_num);
- assemble_string (sptr->string, sptr->length);
- }
- }
- }
-
- /* Output the table of addresses. */
- if (profile_block_flag)
- {
- /* Realign in new section */
- ASM_OUTPUT_ALIGN (asm_out_file, align);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 3);
- for (i = 0; i < count_basic_blocks; i++)
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPB", i);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name),
- pointer_bytes, 1);
- }
- }
-
- /* Output the table of function names. */
- if (profile_block_flag)
- {
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 4);
- for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
- {
- if (ptr->func_label_num >= 0)
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBC",
- ptr->func_label_num);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name),
- pointer_bytes, 1);
- }
- else
- assemble_integer (const0_rtx, pointer_bytes, 1);
- }
-
- for ( ; i < count_basic_blocks; i++)
- assemble_integer (const0_rtx, pointer_bytes, 1);
- }
-
- if (write_symbols != NO_DEBUG && profile_block_flag)
- {
- /* Output the table of line numbers. */
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 5);
- for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
- assemble_integer (GEN_INT (ptr->line_num), long_bytes, 1);
-
- for ( ; i < count_basic_blocks; i++)
- assemble_integer (const0_rtx, long_bytes, 1);
-
- /* Output the table of file names. */
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "LPBX", 6);
- for ((ptr = bb_head), (i = 0); ptr != 0; (ptr = ptr->next), i++)
- {
- if (ptr->file_label_num >= 0)
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBC",
- ptr->file_label_num);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name),
- pointer_bytes, 1);
- }
- else
- assemble_integer (const0_rtx, pointer_bytes, 1);
- }
-
- for ( ; i < count_basic_blocks; i++)
- assemble_integer (const0_rtx, pointer_bytes, 1);
- }
-
- /* End with the address of the table of addresses,
- so we can find it easily, as the last word in the file's text. */
- if (profile_block_flag)
- {
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 3);
- assemble_integer (gen_rtx_SYMBOL_REF (Pmode, name), pointer_bytes,
- 1);
- }
- }
}
/* Enable APP processing of subsequent output.
@@ -1658,20 +1355,6 @@ final_start_function (first, file, optimize)
dwarf2out_begin_prologue ();
#endif
- /* For SDB and XCOFF, the function beginning must be marked between
- the function label and the prologue. We always need this, even when
- -g1 was used. Defer on MIPS systems so that parameter descriptions
- follow function entry. */
-#if defined(SDB_DEBUGGING_INFO) && !defined(MIPS_DEBUGGING_INFO)
- if (write_symbols == SDB_DEBUG)
- sdbout_begin_function (last_linenum);
- else
-#endif
-#ifdef XCOFF_DEBUGGING_INFO
- if (write_symbols == XCOFF_DEBUG)
- xcoffout_begin_function (file, last_linenum);
- else
-#endif
/* But only output line number for other debug info types if -g2
or better. */
if (NOTE_LINE_NUMBER (first) != NOTE_INSN_DELETED)
@@ -1682,13 +1365,6 @@ final_start_function (first, file, optimize)
leaf_renumber_regs (first);
#endif
- /* The Sun386i and perhaps other machines don't work right
- if the profiling code comes after the prologue. */
-#ifdef PROFILE_BEFORE_PROLOGUE
- if (profile_flag)
- profile_function (file);
-#endif /* PROFILE_BEFORE_PROLOGUE */
-
#if defined (DWARF2_UNWIND_INFO) && defined (HAVE_prologue)
if (dwarf2out_do_frame ())
dwarf2out_frame_debug (NULL_RTX);
@@ -1698,117 +1374,6 @@ final_start_function (first, file, optimize)
/* First output the function prologue: code to set up the stack frame. */
FUNCTION_PROLOGUE (file, get_frame_size ());
#endif
-
-#if defined (SDB_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
- if (write_symbols == SDB_DEBUG || write_symbols == XCOFF_DEBUG)
- next_block_index = 1;
-#endif
-
- /* If the machine represents the prologue as RTL, the profiling code must
- be emitted when NOTE_INSN_PROLOGUE_END is scanned. */
-#ifdef HAVE_prologue
- if (! HAVE_prologue)
-#endif
- profile_after_prologue (file);
-
- profile_label_no++;
-
- /* If we are doing basic block profiling, remember a printable version
- of the function name. */
- if (profile_block_flag)
- {
- bb_func_label_num
- = add_bb_string ((*decl_printable_name) (current_function_decl, 2), FALSE);
- }
-}
-
-static void
-profile_after_prologue (file)
- FILE *file;
-{
-#ifdef FUNCTION_BLOCK_PROFILER
- if (profile_block_flag)
- {
- FUNCTION_BLOCK_PROFILER (file, count_basic_blocks);
- }
-#endif /* FUNCTION_BLOCK_PROFILER */
-
-#ifndef PROFILE_BEFORE_PROLOGUE
- if (profile_flag)
- profile_function (file);
-#endif /* not PROFILE_BEFORE_PROLOGUE */
-}
-
-static void
-profile_function (file)
- FILE *file;
-{
- int align = MIN (BIGGEST_ALIGNMENT, LONG_TYPE_SIZE);
-#if defined(ASM_OUTPUT_REG_PUSH)
-#if defined(STRUCT_VALUE_INCOMING_REGNUM) || defined(STRUCT_VALUE_REGNUM)
- int sval = current_function_returns_struct;
-#endif
-#if defined(STATIC_CHAIN_INCOMING_REGNUM) || defined(STATIC_CHAIN_REGNUM)
- int cxt = current_function_needs_context;
-#endif
-#endif /* ASM_OUTPUT_REG_PUSH */
-
- data_section ();
- ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
- ASM_OUTPUT_INTERNAL_LABEL (file, "LP", profile_label_no);
- assemble_integer (const0_rtx, LONG_TYPE_SIZE / BITS_PER_UNIT, 1);
-
- function_section (current_function_decl);
-
-#if defined(STRUCT_VALUE_INCOMING_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (sval)
- ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_INCOMING_REGNUM);
-#else
-#if defined(STRUCT_VALUE_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (sval)
- {
- ASM_OUTPUT_REG_PUSH (file, STRUCT_VALUE_REGNUM);
- }
-#endif
-#endif
-
-#if defined(STATIC_CHAIN_INCOMING_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (cxt)
- ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_INCOMING_REGNUM);
-#else
-#if defined(STATIC_CHAIN_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (cxt)
- {
- ASM_OUTPUT_REG_PUSH (file, STATIC_CHAIN_REGNUM);
- }
-#endif
-#endif
-
- FUNCTION_PROFILER (file, profile_label_no);
-
-#if defined(STATIC_CHAIN_INCOMING_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (cxt)
- ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_INCOMING_REGNUM);
-#else
-#if defined(STATIC_CHAIN_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (cxt)
- {
- ASM_OUTPUT_REG_POP (file, STATIC_CHAIN_REGNUM);
- }
-#endif
-#endif
-
-#if defined(STRUCT_VALUE_INCOMING_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (sval)
- ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_INCOMING_REGNUM);
-#else
-#if defined(STRUCT_VALUE_REGNUM) && defined(ASM_OUTPUT_REG_PUSH)
- if (sval)
- {
- ASM_OUTPUT_REG_POP (file, STRUCT_VALUE_REGNUM);
- }
-#endif
-#endif
}
/* Output assembler code for the end of a function.
@@ -1827,32 +1392,17 @@ final_end_function (first, file, optimize)
app_on = 0;
}
-#ifdef SDB_DEBUGGING_INFO
- if (write_symbols == SDB_DEBUG)
- sdbout_end_function (high_function_linenum);
-#endif
-
#ifdef DWARF_DEBUGGING_INFO
if (write_symbols == DWARF_DEBUG)
dwarfout_end_function ();
#endif
-#ifdef XCOFF_DEBUGGING_INFO
- if (write_symbols == XCOFF_DEBUG)
- xcoffout_end_function (file, high_function_linenum);
-#endif
-
#ifdef FUNCTION_EPILOGUE
/* Finally, output the function epilogue:
code to restore the stack frame and return to the caller. */
FUNCTION_EPILOGUE (file, get_frame_size ());
#endif
-#ifdef SDB_DEBUGGING_INFO
- if (write_symbols == SDB_DEBUG)
- sdbout_end_epilogue ();
-#endif
-
#ifdef DWARF_DEBUGGING_INFO
if (write_symbols == DWARF_DEBUG)
dwarfout_end_epilogue ();
@@ -1863,100 +1413,10 @@ final_end_function (first, file, optimize)
dwarf2out_end_epilogue ();
#endif
-#ifdef XCOFF_DEBUGGING_INFO
- if (write_symbols == XCOFF_DEBUG)
- xcoffout_end_epilogue (file);
-#endif
-
- bb_func_label_num = -1; /* not in function, nuke label # */
-
/* If FUNCTION_EPILOGUE is not defined, then the function body
itself contains return instructions wherever needed. */
}
-
-/* Add a block to the linked list that remembers the current line/file/function
- for basic block profiling. Emit the label in front of the basic block and
- the instructions that increment the count field. */
-
-static void
-add_bb (file)
- FILE *file;
-{
- struct bb_list *ptr = (struct bb_list *) permalloc (sizeof (struct bb_list));
-
- /* Add basic block to linked list. */
- ptr->next = 0;
- ptr->line_num = last_linenum;
- ptr->file_label_num = bb_file_label_num;
- ptr->func_label_num = bb_func_label_num;
- *bb_tail = ptr;
- bb_tail = &ptr->next;
-
- /* Enable the table of basic-block use counts
- to point at the code it applies to. */
- ASM_OUTPUT_INTERNAL_LABEL (file, "LPB", count_basic_blocks);
-
- /* Before first insn of this basic block, increment the
- count of times it was entered. */
-#ifdef BLOCK_PROFILER
- BLOCK_PROFILER (file, count_basic_blocks);
-#endif
-#ifdef HAVE_cc0
- CC_STATUS_INIT;
-#endif
-
- new_block = 0;
- count_basic_blocks++;
-}
-
-/* Add a string to be used for basic block profiling. */
-
-static int
-add_bb_string (string, perm_p)
- char *string;
- int perm_p;
-{
- int len;
- struct bb_str *ptr = 0;
-
- if (!string)
- {
- string = "<unknown>";
- perm_p = TRUE;
- }
-
- /* Allocate a new string if the current string isn't permanent. If
- the string is permanent search for the same string in other
- allocations. */
-
- len = strlen (string) + 1;
- if (!perm_p)
- {
- char *p = (char *) permalloc (len);
- bcopy (string, p, len);
- string = p;
- }
- else
- for (ptr = sbb_head; ptr != (struct bb_str *) 0; ptr = ptr->next)
- if (ptr->string == string)
- break;
-
- /* Allocate a new string block if we need to. */
- if (!ptr)
- {
- ptr = (struct bb_str *) permalloc (sizeof (*ptr));
- ptr->next = 0;
- ptr->length = len;
- ptr->label_num = sbb_label_num++;
- ptr->string = string;
- *sbb_tail = ptr;
- sbb_tail = &ptr->next;
- }
-
- return ptr->label_num;
-}
-
/* Output assembler code for some insns: all or part of a function.
For description of args, see `final_start_function', above.
@@ -1980,7 +1440,6 @@ final (first, file, optimize, prescan)
int max_uid = 0;
last_ignored_compare = 0;
- new_block = 1;
check_exception_handler_labels ();
@@ -1989,39 +1448,10 @@ final (first, file, optimize, prescan)
block_nodes = identify_blocks (DECL_INITIAL (current_function_decl), first);
/* END CYGNUS LOCAL */
- /* Make a map indicating which line numbers appear in this function.
- When producing SDB debugging info, delete troublesome line number
- notes from inlined functions in other files as well as duplicate
- line number notes. */
-#ifdef SDB_DEBUGGING_INFO
- if (write_symbols == SDB_DEBUG)
- {
- rtx last = 0;
- for (insn = first; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
- {
- if ((RTX_INTEGRATED_P (insn)
- && strcmp (NOTE_SOURCE_FILE (insn), main_input_filename) != 0)
- || (last != 0
- && NOTE_LINE_NUMBER (insn) == NOTE_LINE_NUMBER (last)
- && NOTE_SOURCE_FILE (insn) == NOTE_SOURCE_FILE (last)))
- {
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
- continue;
- }
- last = insn;
- if (NOTE_LINE_NUMBER (insn) > max_line)
- max_line = NOTE_LINE_NUMBER (insn);
- }
- }
- else
-#endif
- {
- for (insn = first; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > max_line)
- max_line = NOTE_LINE_NUMBER (insn);
- }
+ /* Make a map indicating which line numbers appear in this function. */
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > max_line)
+ max_line = NOTE_LINE_NUMBER (insn);
line_note_exists = (char *) oballoc (max_line + 1);
bzero (line_note_exists, max_line + 1);
@@ -2063,11 +1493,6 @@ final (first, file, optimize, prescan)
insn = final_scan_insn (insn, file, optimize, prescan, 0);
}
- /* Do basic-block profiling here
- if the last insn was a conditional branch. */
- if (profile_block_flag && new_block)
- add_bb (file);
-
/* CYGNUS LOCAL LRS */
if (write_symbols != NO_DEBUG)
free ((char *)block_nodes);
@@ -2150,17 +1575,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
live_range_print (file, NOTE_RANGE_INFO (insn), "\t",
ASM_COMMENT_START);
#endif
-#ifdef DBX_DEBUGGING_INFO
- if (write_symbols == DBX_DEBUG && LIVE_RANGE_GDBSTAB_P ())
- {
- rtx ri = NOTE_RANGE_INFO (insn);
- if (!RANGE_INFO_MARKER_START (ri))
- {
- RANGE_INFO_MARKER_START (ri) = ++range_max_number;
- dbxout_live_range (range_max_number);
- }
- }
-#endif
break;
}
@@ -2171,17 +1585,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
fprintf (file, "\t%s range #%d end\n", ASM_COMMENT_START,
RANGE_INFO_UNIQUE (NOTE_RANGE_INFO (insn)));
#endif
-#ifdef DBX_DEBUGGING_INFO
- if (write_symbols == DBX_DEBUG && LIVE_RANGE_GDBSTAB_P ())
- {
- rtx ri = NOTE_RANGE_INFO (insn);
- if (!RANGE_INFO_MARKER_END (ri))
- {
- RANGE_INFO_MARKER_END (ri) = ++range_max_number;
- dbxout_live_range (range_max_number);
- }
- }
-#endif
break;
}
/* END CYGNUS LOCAL */
@@ -2191,7 +1594,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
#ifdef FUNCTION_END_PROLOGUE
FUNCTION_END_PROLOGUE (file);
#endif
- profile_after_prologue (file);
break;
}
@@ -2207,13 +1609,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
break;
if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
{
-#if defined(SDB_DEBUGGING_INFO) && defined(MIPS_DEBUGGING_INFO)
- /* MIPS stabs require the parameter descriptions to be after the
- function entry point rather than before. */
- if (write_symbols == SDB_DEBUG)
- sdbout_begin_function (last_linenum);
- else
-#endif
#ifdef DWARF_DEBUGGING_INFO
/* This outputs a marker where the function body starts, so it
must be after the prologue. */
@@ -2258,18 +1653,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
/* Output debugging info about the symbol-block beginning. */
-#ifdef SDB_DEBUGGING_INFO
- if (write_symbols == SDB_DEBUG)
- sdbout_begin_block (file, last_linenum, next_block_index);
-#endif
-#ifdef XCOFF_DEBUGGING_INFO
- if (write_symbols == XCOFF_DEBUG)
- xcoffout_begin_block (file, last_linenum, next_block_index);
-#endif
-#ifdef DBX_DEBUGGING_INFO
- if (write_symbols == DBX_DEBUG)
- ASM_OUTPUT_INTERNAL_LABEL (file, "LBB", next_block_index);
-#endif
#ifdef DWARF_DEBUGGING_INFO
if (write_symbols == DWARF_DEBUG)
dwarfout_begin_block (next_block_index);
@@ -2295,21 +1678,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
abort ();
/* CYGNUS LOCAL LRS */
-#ifdef XCOFF_DEBUGGING_INFO
- if (write_symbols == XCOFF_DEBUG)
- xcoffout_end_block (file, high_block_linenum,
- pending_blocks[block_depth].number);
-#endif
-#ifdef DBX_DEBUGGING_INFO
- if (write_symbols == DBX_DEBUG)
- ASM_OUTPUT_INTERNAL_LABEL (file, "LBE",
- pending_blocks[block_depth].number);
-#endif
-#ifdef SDB_DEBUGGING_INFO
- if (write_symbols == SDB_DEBUG)
- sdbout_end_block (file, high_block_linenum,
- pending_blocks[block_depth].number);
-#endif
#ifdef DWARF_DEBUGGING_INFO
if (write_symbols == DWARF_DEBUG)
dwarfout_end_block (pending_blocks[block_depth].number);
@@ -2435,16 +1803,11 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
#endif
if (prescan > 0)
break;
- new_block = 1;
#ifdef FINAL_PRESCAN_LABEL
FINAL_PRESCAN_INSN (insn, NULL_PTR, 0);
#endif
-#ifdef SDB_DEBUGGING_INFO
- if (write_symbols == SDB_DEBUG && LABEL_NAME (insn))
- sdbout_label (insn);
-#endif
#ifdef DWARF_DEBUGGING_INFO
if (write_symbols == DWARF_DEBUG && LABEL_NAME (insn))
dwarfout_label (insn);
@@ -2604,11 +1967,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
break;
}
- /* Do basic-block profiling when we reach a new block.
- Done here to avoid jump tables. */
- if (profile_block_flag && new_block)
- add_bb (file);
-
if (GET_CODE (body) == ASM_INPUT)
{
/* There's no telling what that did to the condition codes. */
@@ -2707,22 +2065,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
{
CC_STATUS_INIT;
}
-
- /* Following a conditional branch sequence, we have a new basic
- block. */
- if (profile_block_flag)
- {
- rtx insn = XVECEXP (body, 0, 0);
- rtx body = PATTERN (insn);
-
- if ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET
- && GET_CODE (SET_SRC (body)) != LABEL_REF)
- || (GET_CODE (insn) == JUMP_INSN
- && GET_CODE (body) == PARALLEL
- && GET_CODE (XVECEXP (body, 0, 0)) == SET
- && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF))
- new_block = 1;
- }
break;
}
@@ -2781,17 +2123,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
}
#endif
- /* Following a conditional branch, we have a new basic block.
- But if we are inside a sequence, the new block starts after the
- last insn of the sequence. */
- if (profile_block_flag && final_sequence == 0
- && ((GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == SET
- && GET_CODE (SET_SRC (body)) != LABEL_REF)
- || (GET_CODE (insn) == JUMP_INSN && GET_CODE (body) == PARALLEL
- && GET_CODE (XVECEXP (body, 0, 0)) == SET
- && GET_CODE (SET_SRC (XVECEXP (body, 0, 0))) != LABEL_REF)))
- new_block = 1;
-
#ifndef STACK_REGS
/* Don't bother outputting obvious no-ops, even without -O.
This optimization is fast and doesn't interfere with debugging.
@@ -3003,7 +2334,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
{
if (prev_nonnote_insn (insn) != last_ignored_compare)
abort ();
- new_block = 0;
return prev_nonnote_insn (insn);
}
}
@@ -3025,7 +2355,6 @@ final_scan_insn (insn, file, optimize, prescan, nopeepholes)
abort ();
#endif
- new_block = 0;
return new;
}
@@ -3077,14 +2406,6 @@ output_source_line (file, insn)
{
register char *filename = NOTE_SOURCE_FILE (insn);
- /* Remember filename for basic block profiling.
- Filenames are allocated on the permanent obstack
- or are passed in ARGV, so we don't have to save
- the string. */
-
- if (profile_block_flag && last_filename != filename)
- bb_file_label_num = add_bb_string (filename, TRUE);
-
last_filename = filename;
last_linenum = NOTE_LINE_NUMBER (insn);
high_block_linenum = MAX (last_linenum, high_block_linenum);
@@ -3092,34 +2413,8 @@ output_source_line (file, insn)
if (write_symbols != NO_DEBUG)
{
-#ifdef SDB_DEBUGGING_INFO
- if (write_symbols == SDB_DEBUG
-#if 0 /* People like having line numbers even in wrong file! */
- /* COFF can't handle multiple source files--lose, lose. */
- && !strcmp (filename, main_input_filename)
-#endif
- /* COFF relative line numbers must be positive. */
- && last_linenum > sdb_begin_function_line)
- {
-#ifdef ASM_OUTPUT_SOURCE_LINE
- ASM_OUTPUT_SOURCE_LINE (file, last_linenum);
-#else
- fprintf (file, "\t.ln\t%d\n",
- ((sdb_begin_function_line > -1)
- ? last_linenum - sdb_begin_function_line : 1));
-#endif
- }
-#endif
-#if defined (DBX_DEBUGGING_INFO)
- if (write_symbols == DBX_DEBUG)
- dbxout_source_line (file, filename, NOTE_LINE_NUMBER (insn));
-#endif
-#if defined (XCOFF_DEBUGGING_INFO)
- if (write_symbols == XCOFF_DEBUG)
- xcoffout_source_line (file, filename, insn);
-#endif
#ifdef DWARF_DEBUGGING_INFO
if (write_symbols == DWARF_DEBUG)
@@ -4074,9 +3369,6 @@ leaf_function_p ()
{
rtx insn;
- if (profile_flag || profile_block_flag || profile_arc_flag)
- return 0;
-
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (GET_CODE (insn) == CALL_INSN)
diff --git a/gcc/flags.h b/gcc/flags.h
index dac3235..77f839b 100755
--- a/gcc/flags.h
+++ b/gcc/flags.h
@@ -137,26 +137,6 @@ extern unsigned larger_than_size;
extern int warn_aggregate_return;
-/* Nonzero if generating code to do profiling. */
-
-extern int profile_flag;
-
-/* Nonzero if generating code to do profiling on the basis of basic blocks. */
-
-extern int profile_block_flag;
-
-/* Nonzero if generating code to profile program flow graph arcs. */
-
-extern int profile_arc_flag;
-
-/* Nonzero if generating info for gcov to calculate line test coverage. */
-
-extern int flag_test_coverage;
-
-/* Nonzero indicates that branch taken probabilities should be calculated. */
-
-extern int flag_branch_probabilities;
-
/* Nonzero for -pedantic switch: warn about anything
that standard C forbids. */
diff --git a/gcc/function.BAK b/gcc/function.BAK
index c724c5e..3c99619 100755
--- a/gcc/function.BAK
+++ b/gcc/function.BAK
@@ -6233,14 +6233,6 @@ expand_function_end (filename, line, end_bindings)
without returning a value. */
emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END);
- /* Must mark the last line number note in the function, so that the test
- coverage code can avoid counting the last line twice. This just tells
- the code to ignore the immediately following line note, since there
- already exists a copy of this note somewhere above. This line number
- note is still needed for debugging though, so we can't delete it. */
- if (flag_test_coverage)
- emit_note (NULL_PTR, NOTE_REPEATED_LINE_NUMBER);
-
/* Output a linenumber for the end of the function.
SDB depends on this. */
emit_line_note_force (filename, line);
diff --git a/gcc/function.c b/gcc/function.c
index aa96592..d55149d 100755
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -6233,14 +6233,6 @@ expand_function_end (filename, line, end_bindings)
without returning a value. */
emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END);
- /* Must mark the last line number note in the function, so that the test
- coverage code can avoid counting the last line twice. This just tells
- the code to ignore the immediately following line note, since there
- already exists a copy of this note somewhere above. This line number
- note is still needed for debugging though, so we can't delete it. */
- if (flag_test_coverage)
- emit_note (NULL_PTR, NOTE_REPEATED_LINE_NUMBER);
-
/* Output a linenumber for the end of the function.
SDB depends on this. */
emit_line_note_force (filename, line);
diff --git a/gcc/function_990206.c b/gcc/function_990206.c
index 8ed07be..bee8f12 100755
--- a/gcc/function_990206.c
+++ b/gcc/function_990206.c
@@ -6163,14 +6163,6 @@ expand_function_end (filename, line, end_bindings)
without returning a value. */
emit_note (NULL_PTR, NOTE_INSN_FUNCTION_END);
- /* Must mark the last line number note in the function, so that the test
- coverage code can avoid counting the last line twice. This just tells
- the code to ignore the immediately following line note, since there
- already exists a copy of this note somewhere above. This line number
- note is still needed for debugging though, so we can't delete it. */
- if (flag_test_coverage)
- emit_note (NULL_PTR, NOTE_REPEATED_LINE_NUMBER);
-
/* Output a linenumber for the end of the function.
SDB depends on this. */
emit_line_note_force (filename, line);
diff --git a/gcc/haifa-sched.c b/gcc/haifa-sched.c
deleted file mode 100755
index e422762..0000000
--- a/gcc/haifa-sched.c
+++ /dev/null
@@ -1,8744 +0,0 @@
-/* Instruction scheduling pass.
- Copyright (C) 1992, 93-98, 1999 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. */
-
-
-/* Instruction scheduling pass.
-
- This pass implements list scheduling within basic blocks. It is
- run twice: (1) after flow analysis, but before register allocation,
- and (2) after register allocation.
-
- The first run performs interblock scheduling, moving insns between
- different blocks in the same "region", and the second runs only
- basic block scheduling.
-
- Interblock motions performed are useful motions and speculative
- motions, including speculative loads. Motions requiring code
- duplication are not supported. The identification of motion type
- and the check for validity of speculative motions requires
- construction and analysis of the function's control flow graph.
- The scheduler works as follows:
-
- We compute insn priorities based on data dependencies. Flow
- analysis only creates a fraction of the data-dependencies we must
- observe: namely, only those dependencies which the combiner can be
- expected to use. For this pass, we must therefore create the
- remaining dependencies we need to observe: register dependencies,
- memory dependencies, dependencies to keep function calls in order,
- and the dependence between a conditional branch and the setting of
- condition codes are all dealt with here.
-
- The scheduler first traverses the data flow graph, starting with
- the last instruction, and proceeding to the first, assigning values
- to insn_priority as it goes. This sorts the instructions
- topologically by data dependence.
-
- Once priorities have been established, we order the insns using
- list scheduling. This works as follows: starting with a list of
- all the ready insns, and sorted according to priority number, we
- schedule the insn from the end of the list by placing its
- predecessors in the list according to their priority order. We
- consider this insn scheduled by setting the pointer to the "end" of
- the list to point to the previous insn. When an insn has no
- predecessors, we either queue it until sufficient time has elapsed
- or add it to the ready list. As the instructions are scheduled or
- when stalls are introduced, the queue advances and dumps insns into
- the ready list. When all insns down to the lowest priority have
- been scheduled, the critical path of the basic block has been made
- as short as possible. The remaining insns are then scheduled in
- remaining slots.
-
- Function unit conflicts are resolved during forward list scheduling
- by tracking the time when each insn is committed to the schedule
- and from that, the time the function units it uses must be free.
- As insns on the ready list are considered for scheduling, those
- that would result in a blockage of the already committed insns are
- queued until no blockage will result.
-
- The following list shows the order in which we want to break ties
- among insns in the ready list:
-
- 1. choose insn with the longest path to end of bb, ties
- broken by
- 2. choose insn with least contribution to register pressure,
- ties broken by
- 3. prefer in-block upon interblock motion, ties broken by
- 4. prefer useful upon speculative motion, ties broken by
- 5. choose insn with largest control flow probability, ties
- broken by
- 6. choose insn with the least dependences upon the previously
- scheduled insn, or finally
- 7 choose the insn which has the most insns dependent on it.
- 8. choose insn with lowest UID.
-
- Memory references complicate matters. Only if we can be certain
- that memory references are not part of the data dependency graph
- (via true, anti, or output dependence), can we move operations past
- memory references. To first approximation, reads can be done
- independently, while writes introduce dependencies. Better
- approximations will yield fewer dependencies.
-
- Before reload, an extended analysis of interblock data dependences
- is required for interblock scheduling. This is performed in
- compute_block_backward_dependences ().
-
- Dependencies set up by memory references are treated in exactly the
- same way as other dependencies, by using LOG_LINKS backward
- dependences. LOG_LINKS are translated into INSN_DEPEND forward
- dependences for the purpose of forward list scheduling.
-
- Having optimized the critical path, we may have also unduly
- extended the lifetimes of some registers. If an operation requires
- that constants be loaded into registers, it is certainly desirable
- to load those constants as early as necessary, but no earlier.
- I.e., it will not do to load up a bunch of registers at the
- beginning of a basic block only to use them at the end, if they
- could be loaded later, since this may result in excessive register
- utilization.
-
- Note that since branches are never in basic blocks, but only end
- basic blocks, this pass will not move branches. But that is ok,
- since we can use GNU's delayed branch scheduling pass to take care
- of this case.
-
- Also note that no further optimizations based on algebraic
- identities are performed, so this pass would be a good one to
- perform instruction splitting, such as breaking up a multiply
- instruction into shifts and adds where that is profitable.
-
- Given the memory aliasing analysis that this pass should perform,
- it should be possible to remove redundant stores to memory, and to
- load values from registers instead of hitting memory.
-
- Before reload, speculative insns are moved only if a 'proof' exists
- that no exception will be caused by this, and if no live registers
- exist that inhibit the motion (live registers constraints are not
- represented by data dependence edges).
-
- This pass must update information that subsequent passes expect to
- be correct. Namely: reg_n_refs, reg_n_sets, reg_n_deaths,
- reg_n_calls_crossed, and reg_live_length. Also, BLOCK_HEAD,
- BLOCK_END.
-
- The information in the line number notes is carefully retained by
- this pass. Notes that refer to the starting and ending of
- exception regions are also carefully retained by this pass. All
- other NOTE insns are grouped in their same relative order at the
- beginning of basic blocks and regions that have been scheduled.
-
- The main entry point for this pass is schedule_insns(), called for
- each function. The work of the scheduler is organized in three
- levels: (1) function level: insns are subject to splitting,
- control-flow-graph is constructed, regions are computed (after
- reload, each region is of one block), (2) region level: control
- flow graph attributes required for interblock scheduling are
- computed (dominators, reachability, etc.), data dependences and
- priorities are computed, and (3) block level: insns in the block
- are actually scheduled. */
-
-#include "config.h"
-#include "system.h"
-#include "rtl.h"
-#include "basic-block.h"
-#include "regs.h"
-#include "hard-reg-set.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "insn-attr.h"
-#include "except.h"
-#include "toplev.h"
-#include "recog.h"
-
-extern char *reg_known_equiv_p;
-extern rtx *reg_known_value;
-
-#ifdef INSN_SCHEDULING
-
-/* target_units bitmask has 1 for each unit in the cpu. It should be
- possible to compute this variable from the machine description.
- But currently it is computed by examinning the insn list. Since
- this is only needed for visualization, it seems an acceptable
- solution. (For understanding the mapping of bits to units, see
- definition of function_units[] in "insn-attrtab.c") */
-
-static int target_units = 0;
-
-/* issue_rate is the number of insns that can be scheduled in the same
- machine cycle. It can be defined in the config/mach/mach.h file,
- otherwise we set it to 1. */
-
-static int issue_rate;
-
-#ifndef ISSUE_RATE
-#define ISSUE_RATE 1
-#endif
-
-/* sched-verbose controls the amount of debugging output the
- scheduler prints. It is controlled by -fsched-verbose-N:
- N>0 and no -DSR : the output is directed to stderr.
- N>=10 will direct the printouts to stderr (regardless of -dSR).
- N=1: same as -dSR.
- N=2: bb's probabilities, detailed ready list info, unit/insn info.
- N=3: rtl at abort point, control-flow, regions info.
- N=5: dependences info. */
-
-#define MAX_RGN_BLOCKS 10
-#define MAX_RGN_INSNS 100
-
-static int sched_verbose_param = 0;
-static int sched_verbose = 0;
-
-/* nr_inter/spec counts interblock/speculative motion for the function */
-static int nr_inter, nr_spec;
-
-
-/* debugging file. all printouts are sent to dump, which is always set,
- either to stderr, or to the dump listing file (-dRS). */
-static FILE *dump = 0;
-
-/* fix_sched_param() is called from toplev.c upon detection
- of the -fsched-***-N options. */
-
-void
-fix_sched_param (param, val)
- char *param, *val;
-{
- if (!strcmp (param, "verbose"))
- sched_verbose_param = atoi (val);
- else
- warning ("fix_sched_param: unknown param: %s", param);
-}
-
-
-/* Arrays set up by scheduling for the same respective purposes as
- similar-named arrays set up by flow analysis. We work with these
- arrays during the scheduling pass so we can compare values against
- unscheduled code.
-
- Values of these arrays are copied at the end of this pass into the
- arrays set up by flow analysis. */
-static int *sched_reg_n_calls_crossed;
-static int *sched_reg_live_length;
-static int *sched_reg_basic_block;
-
-/* We need to know the current block number during the post scheduling
- update of live register information so that we can also update
- REG_BASIC_BLOCK if a register changes blocks. */
-static int current_block_num;
-
-/* Element N is the next insn that sets (hard or pseudo) register
- N within the current basic block; or zero, if there is no
- such insn. Needed for new registers which may be introduced
- by splitting insns. */
-static rtx *reg_last_uses;
-static rtx *reg_last_sets;
-static regset reg_pending_sets;
-static int reg_pending_sets_all;
-
-/* Vector indexed by INSN_UID giving the original ordering of the insns. */
-static int *insn_luid;
-#define INSN_LUID(INSN) (insn_luid[INSN_UID (INSN)])
-
-/* Vector indexed by INSN_UID giving each instruction a priority. */
-static int *insn_priority;
-#define INSN_PRIORITY(INSN) (insn_priority[INSN_UID (INSN)])
-
-static short *insn_costs;
-#define INSN_COST(INSN) insn_costs[INSN_UID (INSN)]
-
-/* Vector indexed by INSN_UID giving an encoding of the function units
- used. */
-static short *insn_units;
-#define INSN_UNIT(INSN) insn_units[INSN_UID (INSN)]
-
-/* Vector indexed by INSN_UID giving each instruction a register-weight.
- This weight is an estimation of the insn contribution to registers pressure. */
-static int *insn_reg_weight;
-#define INSN_REG_WEIGHT(INSN) (insn_reg_weight[INSN_UID (INSN)])
-
-/* Vector indexed by INSN_UID giving list of insns which
- depend upon INSN. Unlike LOG_LINKS, it represents forward dependences. */
-static rtx *insn_depend;
-#define INSN_DEPEND(INSN) insn_depend[INSN_UID (INSN)]
-
-/* Vector indexed by INSN_UID. Initialized to the number of incoming
- edges in forward dependence graph (= number of LOG_LINKS). As
- scheduling procedes, dependence counts are decreased. An
- instruction moves to the ready list when its counter is zero. */
-static int *insn_dep_count;
-#define INSN_DEP_COUNT(INSN) (insn_dep_count[INSN_UID (INSN)])
-
-/* Vector indexed by INSN_UID giving an encoding of the blockage range
- function. The unit and the range are encoded. */
-static unsigned int *insn_blockage;
-#define INSN_BLOCKAGE(INSN) insn_blockage[INSN_UID (INSN)]
-#define UNIT_BITS 5
-#define BLOCKAGE_MASK ((1 << BLOCKAGE_BITS) - 1)
-#define ENCODE_BLOCKAGE(U, R) \
-((((U) << UNIT_BITS) << BLOCKAGE_BITS \
- | MIN_BLOCKAGE_COST (R)) << BLOCKAGE_BITS \
- | MAX_BLOCKAGE_COST (R))
-#define UNIT_BLOCKED(B) ((B) >> (2 * BLOCKAGE_BITS))
-#define BLOCKAGE_RANGE(B) \
- (((((B) >> BLOCKAGE_BITS) & BLOCKAGE_MASK) << (HOST_BITS_PER_INT / 2)) \
- | ((B) & BLOCKAGE_MASK))
-
-/* Encodings of the `<name>_unit_blockage_range' function. */
-#define MIN_BLOCKAGE_COST(R) ((R) >> (HOST_BITS_PER_INT / 2))
-#define MAX_BLOCKAGE_COST(R) ((R) & ((1 << (HOST_BITS_PER_INT / 2)) - 1))
-
-#define DONE_PRIORITY -1
-#define MAX_PRIORITY 0x7fffffff
-#define TAIL_PRIORITY 0x7ffffffe
-#define LAUNCH_PRIORITY 0x7f000001
-#define DONE_PRIORITY_P(INSN) (INSN_PRIORITY (INSN) < 0)
-#define LOW_PRIORITY_P(INSN) ((INSN_PRIORITY (INSN) & 0x7f000000) == 0)
-
-/* Vector indexed by INSN_UID giving number of insns referring to this insn. */
-static int *insn_ref_count;
-#define INSN_REF_COUNT(INSN) (insn_ref_count[INSN_UID (INSN)])
-
-/* Vector indexed by INSN_UID giving line-number note in effect for each
- insn. For line-number notes, this indicates whether the note may be
- reused. */
-static rtx *line_note;
-#define LINE_NOTE(INSN) (line_note[INSN_UID (INSN)])
-
-/* Vector indexed by basic block number giving the starting line-number
- for each basic block. */
-static rtx *line_note_head;
-
-/* List of important notes we must keep around. This is a pointer to the
- last element in the list. */
-static rtx note_list;
-
-/* Regsets telling whether a given register is live or dead before the last
- scheduled insn. Must scan the instructions once before scheduling to
- determine what registers are live or dead at the end of the block. */
-static regset bb_live_regs;
-
-/* Regset telling whether a given register is live after the insn currently
- being scheduled. Before processing an insn, this is equal to bb_live_regs
- above. This is used so that we can find registers that are newly born/dead
- after processing an insn. */
-static regset old_live_regs;
-
-/* The chain of REG_DEAD notes. REG_DEAD notes are removed from all insns
- during the initial scan and reused later. If there are not exactly as
- many REG_DEAD notes in the post scheduled code as there were in the
- prescheduled code then we trigger an abort because this indicates a bug. */
-static rtx dead_notes;
-
-/* Queues, etc. */
-
-/* An instruction is ready to be scheduled when all insns preceding it
- have already been scheduled. It is important to ensure that all
- insns which use its result will not be executed until its result
- has been computed. An insn is maintained in one of four structures:
-
- (P) the "Pending" set of insns which cannot be scheduled until
- their dependencies have been satisfied.
- (Q) the "Queued" set of insns that can be scheduled when sufficient
- time has passed.
- (R) the "Ready" list of unscheduled, uncommitted insns.
- (S) the "Scheduled" list of insns.
-
- Initially, all insns are either "Pending" or "Ready" depending on
- whether their dependencies are satisfied.
-
- Insns move from the "Ready" list to the "Scheduled" list as they
- are committed to the schedule. As this occurs, the insns in the
- "Pending" list have their dependencies satisfied and move to either
- the "Ready" list or the "Queued" set depending on whether
- sufficient time has passed to make them ready. As time passes,
- insns move from the "Queued" set to the "Ready" list. Insns may
- move from the "Ready" list to the "Queued" set if they are blocked
- due to a function unit conflict.
-
- The "Pending" list (P) are the insns in the INSN_DEPEND of the unscheduled
- insns, i.e., those that are ready, queued, and pending.
- The "Queued" set (Q) is implemented by the variable `insn_queue'.
- The "Ready" list (R) is implemented by the variables `ready' and
- `n_ready'.
- The "Scheduled" list (S) is the new insn chain built by this pass.
-
- The transition (R->S) is implemented in the scheduling loop in
- `schedule_block' when the best insn to schedule is chosen.
- The transition (R->Q) is implemented in `queue_insn' when an
- insn is found to have a function unit conflict with the already
- committed insns.
- The transitions (P->R and P->Q) are implemented in `schedule_insn' as
- insns move from the ready list to the scheduled list.
- The transition (Q->R) is implemented in 'queue_to_insn' as time
- passes or stalls are introduced. */
-
-/* Implement a circular buffer to delay instructions until sufficient
- time has passed. INSN_QUEUE_SIZE is a power of two larger than
- MAX_BLOCKAGE and MAX_READY_COST computed by genattr.c. This is the
- longest time an isnsn may be queued. */
-static rtx insn_queue[INSN_QUEUE_SIZE];
-static int q_ptr = 0;
-static int q_size = 0;
-#define NEXT_Q(X) (((X)+1) & (INSN_QUEUE_SIZE-1))
-#define NEXT_Q_AFTER(X, C) (((X)+C) & (INSN_QUEUE_SIZE-1))
-
-/* Vector indexed by INSN_UID giving the minimum clock tick at which
- the insn becomes ready. This is used to note timing constraints for
- insns in the pending list. */
-static int *insn_tick;
-#define INSN_TICK(INSN) (insn_tick[INSN_UID (INSN)])
-
-/* Data structure for keeping track of register information
- during that register's life. */
-
-struct sometimes
- {
- int regno;
- int live_length;
- int calls_crossed;
- };
-
-/* Forward declarations. */
-static void add_dependence PROTO ((rtx, rtx, enum reg_note));
-static void remove_dependence PROTO ((rtx, rtx));
-static rtx find_insn_list PROTO ((rtx, rtx));
-static int insn_unit PROTO ((rtx));
-static unsigned int blockage_range PROTO ((int, rtx));
-static void clear_units PROTO ((void));
-static int actual_hazard_this_instance PROTO ((int, int, rtx, int, int));
-static void schedule_unit PROTO ((int, rtx, int));
-static int actual_hazard PROTO ((int, rtx, int, int));
-static int potential_hazard PROTO ((int, rtx, int));
-static int insn_cost PROTO ((rtx, rtx, rtx));
-static int priority PROTO ((rtx));
-static void free_pending_lists PROTO ((void));
-static void add_insn_mem_dependence PROTO ((rtx *, rtx *, rtx, rtx));
-static void flush_pending_lists PROTO ((rtx, int));
-static void sched_analyze_1 PROTO ((rtx, rtx));
-static void sched_analyze_2 PROTO ((rtx, rtx));
-static void sched_analyze_insn PROTO ((rtx, rtx, rtx));
-static void sched_analyze PROTO ((rtx, rtx));
-static void sched_note_set PROTO ((rtx, int));
-static int rank_for_schedule PROTO ((const GENERIC_PTR, const GENERIC_PTR));
-static void swap_sort PROTO ((rtx *, int));
-static void queue_insn PROTO ((rtx, int));
-static int schedule_insn PROTO ((rtx, rtx *, int, int));
-static void create_reg_dead_note PROTO ((rtx, rtx));
-static void attach_deaths PROTO ((rtx, rtx, int));
-static void attach_deaths_insn PROTO ((rtx));
-static int new_sometimes_live PROTO ((struct sometimes *, int, int));
-static void finish_sometimes_live PROTO ((struct sometimes *, int));
-static int schedule_block PROTO ((int, int));
-static void split_hard_reg_notes PROTO ((rtx, rtx, rtx));
-static void new_insn_dead_notes PROTO ((rtx, rtx, rtx, rtx));
-static void update_n_sets PROTO ((rtx, int));
-static char *safe_concat PROTO ((char *, char *, char *));
-static int insn_issue_delay PROTO ((rtx));
-static int birthing_insn_p PROTO ((rtx));
-static void adjust_priority PROTO ((rtx));
-
-/* Mapping of insns to their original block prior to scheduling. */
-static int *insn_orig_block;
-#define INSN_BLOCK(insn) (insn_orig_block[INSN_UID (insn)])
-
-/* Some insns (e.g. call) are not allowed to move across blocks. */
-static char *cant_move;
-#define CANT_MOVE(insn) (cant_move[INSN_UID (insn)])
-
-/* Control flow graph edges are kept in circular lists. */
-typedef struct
- {
- int from_block;
- int to_block;
- int next_in;
- int next_out;
- }
-edge;
-static edge *edge_table;
-
-#define NEXT_IN(edge) (edge_table[edge].next_in)
-#define NEXT_OUT(edge) (edge_table[edge].next_out)
-#define FROM_BLOCK(edge) (edge_table[edge].from_block)
-#define TO_BLOCK(edge) (edge_table[edge].to_block)
-
-/* Number of edges in the control flow graph. (in fact larger than
- that by 1, since edge 0 is unused.) */
-static int nr_edges;
-
-/* Circular list of incoming/outgoing edges of a block */
-static int *in_edges;
-static int *out_edges;
-
-#define IN_EDGES(block) (in_edges[block])
-#define OUT_EDGES(block) (out_edges[block])
-
-/* List of labels which cannot be deleted, needed for control
- flow graph construction. */
-extern rtx forced_labels;
-
-
-static int is_cfg_nonregular PROTO ((void));
-static int build_control_flow PROTO ((int_list_ptr *, int_list_ptr *,
- int *, int *));
-static void new_edge PROTO ((int, int));
-
-
-/* A region is the main entity for interblock scheduling: insns
- are allowed to move between blocks in the same region, along
- control flow graph edges, in the 'up' direction. */
-typedef struct
- {
- int rgn_nr_blocks; /* number of blocks in region */
- int rgn_blocks; /* blocks in the region (actually index in rgn_bb_table) */
- }
-region;
-
-/* Number of regions in the procedure */
-static int nr_regions;
-
-/* Table of region descriptions */
-static region *rgn_table;
-
-/* Array of lists of regions' blocks */
-static int *rgn_bb_table;
-
-/* Topological order of blocks in the region (if b2 is reachable from
- b1, block_to_bb[b2] > block_to_bb[b1]).
- Note: A basic block is always referred to by either block or b,
- while its topological order name (in the region) is refered to by
- bb.
- */
-static int *block_to_bb;
-
-/* The number of the region containing a block. */
-static int *containing_rgn;
-
-#define RGN_NR_BLOCKS(rgn) (rgn_table[rgn].rgn_nr_blocks)
-#define RGN_BLOCKS(rgn) (rgn_table[rgn].rgn_blocks)
-#define BLOCK_TO_BB(block) (block_to_bb[block])
-#define CONTAINING_RGN(block) (containing_rgn[block])
-
-void debug_regions PROTO ((void));
-static void find_single_block_region PROTO ((void));
-static void find_rgns PROTO ((int_list_ptr *, int_list_ptr *,
- int *, int *, sbitmap *));
-static int too_large PROTO ((int, int *, int *));
-
-extern void debug_live PROTO ((int, int));
-
-/* Blocks of the current region being scheduled. */
-static int current_nr_blocks;
-static int current_blocks;
-
-/* The mapping from bb to block */
-#define BB_TO_BLOCK(bb) (rgn_bb_table[current_blocks + (bb)])
-
-
-/* Bit vectors and bitset operations are needed for computations on
- the control flow graph. */
-
-typedef unsigned HOST_WIDE_INT *bitset;
-typedef struct
- {
- int *first_member; /* pointer to the list start in bitlst_table. */
- int nr_members; /* the number of members of the bit list. */
- }
-bitlst;
-
-static int bitlst_table_last;
-static int bitlst_table_size;
-static int *bitlst_table;
-
-static char bitset_member PROTO ((bitset, int, int));
-static void extract_bitlst PROTO ((bitset, int, bitlst *));
-
-/* target info declarations.
-
- The block currently being scheduled is referred to as the "target" block,
- while other blocks in the region from which insns can be moved to the
- target are called "source" blocks. The candidate structure holds info
- about such sources: are they valid? Speculative? Etc. */
-typedef bitlst bblst;
-typedef struct
- {
- char is_valid;
- char is_speculative;
- int src_prob;
- bblst split_bbs;
- bblst update_bbs;
- }
-candidate;
-
-static candidate *candidate_table;
-
-/* A speculative motion requires checking live information on the path
- from 'source' to 'target'. The split blocks are those to be checked.
- After a speculative motion, live information should be modified in
- the 'update' blocks.
-
- Lists of split and update blocks for each candidate of the current
- target are in array bblst_table */
-static int *bblst_table, bblst_size, bblst_last;
-
-#define IS_VALID(src) ( candidate_table[src].is_valid )
-#define IS_SPECULATIVE(src) ( candidate_table[src].is_speculative )
-#define SRC_PROB(src) ( candidate_table[src].src_prob )
-
-/* The bb being currently scheduled. */
-static int target_bb;
-
-/* List of edges. */
-typedef bitlst edgelst;
-
-/* target info functions */
-static void split_edges PROTO ((int, int, edgelst *));
-static void compute_trg_info PROTO ((int));
-void debug_candidate PROTO ((int));
-void debug_candidates PROTO ((int));
-
-
-/* Bit-set of bbs, where bit 'i' stands for bb 'i'. */
-typedef bitset bbset;
-
-/* Number of words of the bbset. */
-static int bbset_size;
-
-/* Dominators array: dom[i] contains the bbset of dominators of
- bb i in the region. */
-static bbset *dom;
-
-/* bb 0 is the only region entry */
-#define IS_RGN_ENTRY(bb) (!bb)
-
-/* Is bb_src dominated by bb_trg. */
-#define IS_DOMINATED(bb_src, bb_trg) \
-( bitset_member (dom[bb_src], bb_trg, bbset_size) )
-
-/* Probability: Prob[i] is a float in [0, 1] which is the probability
- of bb i relative to the region entry. */
-static float *prob;
-
-/* The probability of bb_src, relative to bb_trg. Note, that while the
- 'prob[bb]' is a float in [0, 1], this macro returns an integer
- in [0, 100]. */
-#define GET_SRC_PROB(bb_src, bb_trg) ((int) (100.0 * (prob[bb_src] / \
- prob[bb_trg])))
-
-/* Bit-set of edges, where bit i stands for edge i. */
-typedef bitset edgeset;
-
-/* Number of edges in the region. */
-static int rgn_nr_edges;
-
-/* Array of size rgn_nr_edges. */
-static int *rgn_edges;
-
-/* Number of words in an edgeset. */
-static int edgeset_size;
-
-/* Mapping from each edge in the graph to its number in the rgn. */
-static int *edge_to_bit;
-#define EDGE_TO_BIT(edge) (edge_to_bit[edge])
-
-/* The split edges of a source bb is different for each target
- bb. In order to compute this efficiently, the 'potential-split edges'
- are computed for each bb prior to scheduling a region. This is actually
- the split edges of each bb relative to the region entry.
-
- pot_split[bb] is the set of potential split edges of bb. */
-static edgeset *pot_split;
-
-/* For every bb, a set of its ancestor edges. */
-static edgeset *ancestor_edges;
-
-static void compute_dom_prob_ps PROTO ((int));
-
-#define ABS_VALUE(x) (((x)<0)?(-(x)):(x))
-#define INSN_PROBABILITY(INSN) (SRC_PROB (BLOCK_TO_BB (INSN_BLOCK (INSN))))
-#define IS_SPECULATIVE_INSN(INSN) (IS_SPECULATIVE (BLOCK_TO_BB (INSN_BLOCK (INSN))))
-#define INSN_BB(INSN) (BLOCK_TO_BB (INSN_BLOCK (INSN)))
-
-/* parameters affecting the decision of rank_for_schedule() */
-#define MIN_DIFF_PRIORITY 2
-#define MIN_PROBABILITY 40
-#define MIN_PROB_DIFF 10
-
-/* speculative scheduling functions */
-static int check_live_1 PROTO ((int, rtx));
-static void update_live_1 PROTO ((int, rtx));
-static int check_live PROTO ((rtx, int));
-static void update_live PROTO ((rtx, int));
-static void set_spec_fed PROTO ((rtx));
-static int is_pfree PROTO ((rtx, int, int));
-static int find_conditional_protection PROTO ((rtx, int));
-static int is_conditionally_protected PROTO ((rtx, int, int));
-static int may_trap_exp PROTO ((rtx, int));
-static int haifa_classify_insn PROTO ((rtx));
-static int is_prisky PROTO ((rtx, int, int));
-static int is_exception_free PROTO ((rtx, int, int));
-
-static char find_insn_mem_list PROTO ((rtx, rtx, rtx, rtx));
-static void compute_block_forward_dependences PROTO ((int));
-static void init_rgn_data_dependences PROTO ((int));
-static void add_branch_dependences PROTO ((rtx, rtx));
-static void compute_block_backward_dependences PROTO ((int));
-void debug_dependencies PROTO ((void));
-
-/* Notes handling mechanism:
- =========================
- Generally, NOTES are saved before scheduling and restored after scheduling.
- The scheduler distinguishes between three types of notes:
-
- (1) LINE_NUMBER notes, generated and used for debugging. Here,
- before scheduling a region, a pointer to the LINE_NUMBER note is
- added to the insn following it (in save_line_notes()), and the note
- is removed (in rm_line_notes() and unlink_line_notes()). After
- scheduling the region, this pointer is used for regeneration of
- the LINE_NUMBER note (in restore_line_notes()).
-
- (2) LOOP_BEGIN, LOOP_END, SETJMP, EHREGION_BEG, EHREGION_END notes:
- Before scheduling a region, a pointer to the note is added to the insn
- that follows or precedes it. (This happens as part of the data dependence
- computation). After scheduling an insn, the pointer contained in it is
- used for regenerating the corresponding note (in reemit_notes).
-
- (3) All other notes (e.g. INSN_DELETED): Before scheduling a block,
- these notes are put in a list (in rm_other_notes() and
- unlink_other_notes ()). After scheduling the block, these notes are
- inserted at the beginning of the block (in schedule_block()). */
-
-static rtx unlink_other_notes PROTO ((rtx, rtx));
-static rtx unlink_line_notes PROTO ((rtx, rtx));
-static void rm_line_notes PROTO ((int));
-static void save_line_notes PROTO ((int));
-static void restore_line_notes PROTO ((int));
-static void rm_redundant_line_notes PROTO ((void));
-static void rm_other_notes PROTO ((rtx, rtx));
-static rtx reemit_notes PROTO ((rtx, rtx));
-
-static void get_block_head_tail PROTO ((int, rtx *, rtx *));
-
-static void find_pre_sched_live PROTO ((int));
-static void find_post_sched_live PROTO ((int));
-static void update_reg_usage PROTO ((void));
-static int queue_to_ready PROTO ((rtx [], int));
-
-/* CYGNUS LOCAL - static qualifier removed from debug_ready_list */
-void debug_ready_list PROTO ((rtx[], int));
-static void init_target_units PROTO ((void));
-static void insn_print_units PROTO ((rtx));
-static int get_visual_tbl_length PROTO ((void));
-static void init_block_visualization PROTO ((void));
-static void print_block_visualization PROTO ((int, char *));
-static void visualize_scheduled_insns PROTO ((int, int));
-static void visualize_no_unit PROTO ((rtx));
-static void visualize_stall_cycles PROTO ((int, int));
-static void print_exp PROTO ((char *, rtx, int));
-static void print_value PROTO ((char *, rtx, int));
-static void print_pattern PROTO ((char *, rtx, int));
-static void print_insn PROTO ((char *, rtx, int));
-void debug_reg_vector PROTO ((regset));
-
-static rtx move_insn1 PROTO ((rtx, rtx));
-static rtx move_insn PROTO ((rtx, rtx));
-static rtx group_leader PROTO ((rtx));
-static int set_priorities PROTO ((int));
-static void init_rtx_vector PROTO ((rtx **, rtx *, int, int));
-static void schedule_region PROTO ((int));
-
-#endif /* INSN_SCHEDULING */
-
-#define SIZE_FOR_MODE(X) (GET_MODE_SIZE (GET_MODE (X)))
-
-/* Helper functions for instruction scheduling. */
-
-/* An INSN_LIST containing all INSN_LISTs allocated but currently unused. */
-static rtx unused_insn_list;
-
-/* An EXPR_LIST containing all EXPR_LISTs allocated but currently unused. */
-static rtx unused_expr_list;
-
-static void free_list PROTO ((rtx *, rtx *));
-static rtx alloc_INSN_LIST PROTO ((rtx, rtx));
-static rtx alloc_EXPR_LIST PROTO ((int, rtx, rtx));
-
-static void
-free_list (listp, unused_listp)
- rtx *listp, *unused_listp;
-{
- register rtx link, prev_link;
-
- if (*listp == 0)
- return;
-
- prev_link = *listp;
- link = XEXP (prev_link, 1);
-
- while (link)
- {
- prev_link = link;
- link = XEXP (link, 1);
- }
-
- XEXP (prev_link, 1) = *unused_listp;
- *unused_listp = *listp;
- *listp = 0;
-}
-
-static rtx
-alloc_INSN_LIST (val, next)
- rtx val, next;
-{
- rtx r;
-
- if (unused_insn_list)
- {
- r = unused_insn_list;
- unused_insn_list = XEXP (r, 1);
- XEXP (r, 0) = val;
- XEXP (r, 1) = next;
- PUT_REG_NOTE_KIND (r, VOIDmode);
- }
- else
- r = gen_rtx_INSN_LIST (VOIDmode, val, next);
-
- return r;
-}
-
-static rtx
-alloc_EXPR_LIST (kind, val, next)
- int kind;
- rtx val, next;
-{
- rtx r;
-
- if (unused_expr_list)
- {
- r = unused_expr_list;
- unused_expr_list = XEXP (r, 1);
- XEXP (r, 0) = val;
- XEXP (r, 1) = next;
- PUT_REG_NOTE_KIND (r, kind);
- }
- else
- r = gen_rtx_EXPR_LIST (kind, val, next);
-
- return r;
-}
-
-/* Add ELEM wrapped in an INSN_LIST with reg note kind DEP_TYPE to the
- LOG_LINKS of INSN, if not already there. DEP_TYPE indicates the type
- of dependence that this link represents. */
-
-static void
-add_dependence (insn, elem, dep_type)
- rtx insn;
- rtx elem;
- enum reg_note dep_type;
-{
- rtx link, next;
-
- /* Don't depend an insn on itself. */
- if (insn == elem)
- return;
-
- /* We can get a dependency on deleted insns due to optimizations in
- the register allocation and reloading or due to splitting. Any
- such dependency is useless and can be ignored. */
- if (GET_CODE (elem) == NOTE)
- return;
-
- /* If elem is part of a sequence that must be scheduled together, then
- make the dependence point to the last insn of the sequence.
- 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;
-
- /* Make the dependence to NEXT, the last insn of the group, instead
- of the original ELEM. */
- elem = next;
- }
-
-#ifdef INSN_SCHEDULING
- /* (This code is guarded by INSN_SCHEDULING, otherwise INSN_BB is undefined.)
- No need for interblock dependences with calls, since
- calls are not moved between blocks. Note: the edge where
- elem is a CALL is still required. */
- if (GET_CODE (insn) == CALL_INSN
- && (INSN_BB (elem) != INSN_BB (insn)))
- return;
-
-#endif
-
- /* Check that we don't already have this dependence. */
- for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
- if (XEXP (link, 0) == elem)
- {
- /* If this is a more restrictive type of dependence than the existing
- one, then change the existing dependence to this type. */
- if ((int) dep_type < (int) REG_NOTE_KIND (link))
- PUT_REG_NOTE_KIND (link, dep_type);
- return;
- }
- /* Might want to check one level of transitivity to save conses. */
-
- link = alloc_INSN_LIST (elem, LOG_LINKS (insn));
- LOG_LINKS (insn) = link;
-
- /* Insn dependency, not data dependency. */
- PUT_REG_NOTE_KIND (link, dep_type);
-}
-
-/* Remove ELEM wrapped in an INSN_LIST from the LOG_LINKS
- of INSN. Abort if not found. */
-
-static void
-remove_dependence (insn, elem)
- rtx insn;
- rtx elem;
-{
- rtx prev, link, next;
- int found = 0;
-
- for (prev = 0, link = LOG_LINKS (insn); link; link = next)
- {
- next = XEXP (link, 1);
- if (XEXP (link, 0) == elem)
- {
- if (prev)
- XEXP (prev, 1) = next;
- else
- LOG_LINKS (insn) = next;
-
- XEXP (link, 1) = unused_insn_list;
- unused_insn_list = link;
-
- found = 1;
- }
- else
- prev = link;
- }
-
- if (!found)
- abort ();
- return;
-}
-
-#ifndef INSN_SCHEDULING
-void
-schedule_insns (dump_file)
- FILE *dump_file;
-{
-}
-#else
-#ifndef __GNUC__
-#define __inline
-#endif
-
-#ifndef HAIFA_INLINE
-#define HAIFA_INLINE __inline
-#endif
-
-/* Computation of memory dependencies. */
-
-/* The *_insns and *_mems are paired lists. Each pending memory operation
- will have a pointer to the MEM rtx on one list and a pointer to the
- containing insn on the other list in the same place in the list. */
-
-/* We can't use add_dependence like the old code did, because a single insn
- may have multiple memory accesses, and hence needs to be on the list
- once for each memory access. Add_dependence won't let you add an insn
- to a list more than once. */
-
-/* An INSN_LIST containing all insns with pending read operations. */
-static rtx pending_read_insns;
-
-/* An EXPR_LIST containing all MEM rtx's which are pending reads. */
-static rtx pending_read_mems;
-
-/* An INSN_LIST containing all insns with pending write operations. */
-static rtx pending_write_insns;
-
-/* An EXPR_LIST containing all MEM rtx's which are pending writes. */
-static rtx pending_write_mems;
-
-/* Indicates the combined length of the two pending lists. We must prevent
- these lists from ever growing too large since the number of dependencies
- produced is at least O(N*N), and execution time is at least O(4*N*N), as
- a function of the length of these pending lists. */
-
-static int pending_lists_length;
-
-/* The last insn upon which all memory references must depend.
- This is an insn which flushed the pending lists, creating a dependency
- between it and all previously pending memory references. This creates
- a barrier (or a checkpoint) which no memory reference is allowed to cross.
-
- This includes all non constant CALL_INSNs. When we do interprocedural
- alias analysis, this restriction can be relaxed.
- This may also be an INSN that writes memory if the pending lists grow
- too large. */
-
-static rtx last_pending_memory_flush;
-
-/* The last function call we have seen. All hard regs, and, of course,
- the last function call, must depend on this. */
-
-static 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. */
-
-static rtx sched_before_next_call;
-
-/* Pointer to the last instruction scheduled. Used by rank_for_schedule,
- so that insns independent of the last scheduled insn will be preferred
- over dependent instructions. */
-
-static rtx last_scheduled_insn;
-
-/* Data structures for the computation of data dependences in a regions. We
- keep one copy of each of the declared above variables for each bb in the
- region. Before analyzing the data dependences for a bb, its variables
- are initialized as a function of the variables of its predecessors. When
- the analysis for a bb completes, we save the contents of each variable X
- to a corresponding bb_X[bb] variable. For example, pending_read_insns is
- copied to bb_pending_read_insns[bb]. Another change is that few
- variables are now a list of insns rather than a single insn:
- last_pending_memory_flash, last_function_call, reg_last_sets. The
- manipulation of these variables was changed appropriately. */
-
-static rtx **bb_reg_last_uses;
-static rtx **bb_reg_last_sets;
-
-static rtx *bb_pending_read_insns;
-static rtx *bb_pending_read_mems;
-static rtx *bb_pending_write_insns;
-static rtx *bb_pending_write_mems;
-static int *bb_pending_lists_length;
-
-static rtx *bb_last_pending_memory_flush;
-static rtx *bb_last_function_call;
-static rtx *bb_sched_before_next_call;
-
-/* functions for construction of the control flow graph. */
-
-/* Return 1 if control flow graph should not be constructed, 0 otherwise.
-
- We decide not to build the control flow graph if there is possibly more
- than one entry to the function, if computed branches exist, of if we
- have nonlocal gotos. */
-
-static int
-is_cfg_nonregular ()
-{
- int b;
- rtx insn;
- RTX_CODE code;
-
- /* If we have a label that could be the target of a nonlocal goto, then
- the cfg is not well structured. */
- if (nonlocal_label_rtx_list () != NULL)
- return 1;
-
- /* If we have any forced labels, then the cfg is not well structured. */
- if (forced_labels)
- return 1;
-
- /* If this function has a computed jump, then we consider the cfg
- not well structured. */
- if (current_function_has_computed_jump)
- return 1;
-
- /* If we have exception handlers, then we consider the cfg not well
- structured. ?!? We should be able to handle this now that flow.c
- computes an accurate cfg for EH. */
- if (exception_handler_labels)
- return 1;
-
- /* If we have non-jumping insns which refer to labels, then we consider
- the cfg not well structured. */
- /* check for labels referred to other thn by jumps */
- for (b = 0; b < n_basic_blocks; b++)
- for (insn = BLOCK_HEAD (b);; insn = NEXT_INSN (insn))
- {
- code = GET_CODE (insn);
- if (GET_RTX_CLASS (code) == 'i')
- {
- rtx note;
-
- for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
- if (REG_NOTE_KIND (note) == REG_LABEL)
- return 1;
- }
-
- if (insn == BLOCK_END (b))
- break;
- }
-
- /* All the tests passed. Consider the cfg well structured. */
- return 0;
-}
-
-/* Build the control flow graph and set nr_edges.
-
- Instead of trying to build a cfg ourselves, we rely on flow to
- do it for us. Stamp out useless code (and bug) duplication.
-
- Return nonzero if an irregularity in the cfg is found which would
- prevent cross block scheduling. */
-
-static int
-build_control_flow (s_preds, s_succs, num_preds, num_succs)
- int_list_ptr *s_preds;
- int_list_ptr *s_succs;
- int *num_preds;
- int *num_succs;
-{
- int i;
- int_list_ptr succ;
- int unreachable;
-
- /* Count the number of edges in the cfg. */
- nr_edges = 0;
- unreachable = 0;
- for (i = 0; i < n_basic_blocks; i++)
- {
- nr_edges += num_succs[i];
-
- /* Unreachable loops with more than one basic block are detected
- during the DFS traversal in find_rgns.
-
- Unreachable loops with a single block are detected here. This
- test is redundant with the one in find_rgns, but it's much
- cheaper to go ahead and catch the trivial case here. */
- if (num_preds[i] == 0
- || (num_preds[i] == 1 && INT_LIST_VAL (s_preds[i]) == i))
- unreachable = 1;
- }
-
- /* Account for entry/exit edges. */
- nr_edges += 2;
-
- in_edges = (int *) xmalloc (n_basic_blocks * sizeof (int));
- out_edges = (int *) xmalloc (n_basic_blocks * sizeof (int));
- bzero ((char *) in_edges, n_basic_blocks * sizeof (int));
- bzero ((char *) out_edges, n_basic_blocks * sizeof (int));
-
- edge_table = (edge *) xmalloc ((nr_edges) * sizeof (edge));
- bzero ((char *) edge_table, ((nr_edges) * sizeof (edge)));
-
- nr_edges = 0;
- for (i = 0; i < n_basic_blocks; i++)
- for (succ = s_succs[i]; succ; succ = succ->next)
- {
- if (INT_LIST_VAL (succ) != EXIT_BLOCK)
- new_edge (i, INT_LIST_VAL (succ));
- }
-
- /* increment by 1, since edge 0 is unused. */
- nr_edges++;
-
- return unreachable;
-}
-
-
-/* Record an edge in the control flow graph from SOURCE to TARGET.
-
- In theory, this is redundant with the s_succs computed above, but
- we have not converted all of haifa to use information from the
- integer lists. */
-
-static void
-new_edge (source, target)
- int source, target;
-{
- int e, next_edge;
- int curr_edge, fst_edge;
-
- /* check for duplicates */
- fst_edge = curr_edge = OUT_EDGES (source);
- while (curr_edge)
- {
- if (FROM_BLOCK (curr_edge) == source
- && TO_BLOCK (curr_edge) == target)
- {
- return;
- }
-
- curr_edge = NEXT_OUT (curr_edge);
-
- if (fst_edge == curr_edge)
- break;
- }
-
- e = ++nr_edges;
-
- FROM_BLOCK (e) = source;
- TO_BLOCK (e) = target;
-
- if (OUT_EDGES (source))
- {
- next_edge = NEXT_OUT (OUT_EDGES (source));
- NEXT_OUT (OUT_EDGES (source)) = e;
- NEXT_OUT (e) = next_edge;
- }
- else
- {
- OUT_EDGES (source) = e;
- NEXT_OUT (e) = e;
- }
-
- if (IN_EDGES (target))
- {
- next_edge = NEXT_IN (IN_EDGES (target));
- NEXT_IN (IN_EDGES (target)) = e;
- NEXT_IN (e) = next_edge;
- }
- else
- {
- IN_EDGES (target) = e;
- NEXT_IN (e) = e;
- }
-}
-
-
-/* BITSET macros for operations on the control flow graph. */
-
-/* Compute bitwise union of two bitsets. */
-#define BITSET_UNION(set1, set2, len) \
-do { register bitset tp = set1, sp = set2; \
- register int i; \
- for (i = 0; i < len; i++) \
- *(tp++) |= *(sp++); } while (0)
-
-/* Compute bitwise intersection of two bitsets. */
-#define BITSET_INTER(set1, set2, len) \
-do { register bitset tp = set1, sp = set2; \
- register int i; \
- for (i = 0; i < len; i++) \
- *(tp++) &= *(sp++); } while (0)
-
-/* Compute bitwise difference of two bitsets. */
-#define BITSET_DIFFER(set1, set2, len) \
-do { register bitset tp = set1, sp = set2; \
- register int i; \
- for (i = 0; i < len; i++) \
- *(tp++) &= ~*(sp++); } while (0)
-
-/* Inverts every bit of bitset 'set' */
-#define BITSET_INVERT(set, len) \
-do { register bitset tmpset = set; \
- register int i; \
- for (i = 0; i < len; i++, tmpset++) \
- *tmpset = ~*tmpset; } while (0)
-
-/* Turn on the index'th bit in bitset set. */
-#define BITSET_ADD(set, index, len) \
-{ \
- if (index >= HOST_BITS_PER_WIDE_INT * len) \
- abort (); \
- else \
- set[index/HOST_BITS_PER_WIDE_INT] |= \
- 1 << (index % HOST_BITS_PER_WIDE_INT); \
-}
-
-/* Turn off the index'th bit in set. */
-#define BITSET_REMOVE(set, index, len) \
-{ \
- if (index >= HOST_BITS_PER_WIDE_INT * len) \
- abort (); \
- else \
- set[index/HOST_BITS_PER_WIDE_INT] &= \
- ~(1 << (index%HOST_BITS_PER_WIDE_INT)); \
-}
-
-
-/* Check if the index'th bit in bitset set is on. */
-
-static char
-bitset_member (set, index, len)
- bitset set;
- int index, len;
-{
- if (index >= HOST_BITS_PER_WIDE_INT * len)
- abort ();
- return (set[index / HOST_BITS_PER_WIDE_INT] &
- 1 << (index % HOST_BITS_PER_WIDE_INT)) ? 1 : 0;
-}
-
-
-/* Translate a bit-set SET to a list BL of the bit-set members. */
-
-static void
-extract_bitlst (set, len, bl)
- bitset set;
- int len;
- bitlst *bl;
-{
- int i, j, offset;
- unsigned HOST_WIDE_INT word;
-
- /* bblst table space is reused in each call to extract_bitlst */
- bitlst_table_last = 0;
-
- bl->first_member = &bitlst_table[bitlst_table_last];
- bl->nr_members = 0;
-
- for (i = 0; i < len; i++)
- {
- word = set[i];
- offset = i * HOST_BITS_PER_WIDE_INT;
- for (j = 0; word; j++)
- {
- if (word & 1)
- {
- bitlst_table[bitlst_table_last++] = offset;
- (bl->nr_members)++;
- }
- word >>= 1;
- ++offset;
- }
- }
-
-}
-
-
-/* functions for the construction of regions */
-
-/* Print the regions, for debugging purposes. Callable from debugger. */
-
-void
-debug_regions ()
-{
- int rgn, bb;
-
- fprintf (dump, "\n;; ------------ REGIONS ----------\n\n");
- for (rgn = 0; rgn < nr_regions; rgn++)
- {
- fprintf (dump, ";;\trgn %d nr_blocks %d:\n", rgn,
- rgn_table[rgn].rgn_nr_blocks);
- fprintf (dump, ";;\tbb/block: ");
-
- for (bb = 0; bb < rgn_table[rgn].rgn_nr_blocks; bb++)
- {
- current_blocks = RGN_BLOCKS (rgn);
-
- if (bb != BLOCK_TO_BB (BB_TO_BLOCK (bb)))
- abort ();
-
- fprintf (dump, " %d/%d ", bb, BB_TO_BLOCK (bb));
- }
-
- fprintf (dump, "\n\n");
- }
-}
-
-
-/* Build a single block region for each basic block in the function.
- This allows for using the same code for interblock and basic block
- scheduling. */
-
-static void
-find_single_block_region ()
-{
- int i;
-
- for (i = 0; i < n_basic_blocks; i++)
- {
- rgn_bb_table[i] = i;
- RGN_NR_BLOCKS (i) = 1;
- RGN_BLOCKS (i) = i;
- CONTAINING_RGN (i) = i;
- BLOCK_TO_BB (i) = 0;
- }
- nr_regions = n_basic_blocks;
-}
-
-
-/* Update number of blocks and the estimate for number of insns
- in the region. Return 1 if the region is "too large" for interblock
- scheduling (compile time considerations), otherwise return 0. */
-
-static int
-too_large (block, num_bbs, num_insns)
- int block, *num_bbs, *num_insns;
-{
- (*num_bbs)++;
- (*num_insns) += (INSN_LUID (BLOCK_END (block)) -
- INSN_LUID (BLOCK_HEAD (block)));
- if ((*num_bbs > MAX_RGN_BLOCKS) || (*num_insns > MAX_RGN_INSNS))
- return 1;
- else
- return 0;
-}
-
-
-/* Update_loop_relations(blk, hdr): Check if the loop headed by max_hdr[blk]
- is still an inner loop. Put in max_hdr[blk] the header of the most inner
- loop containing blk. */
-#define UPDATE_LOOP_RELATIONS(blk, hdr) \
-{ \
- if (max_hdr[blk] == -1) \
- max_hdr[blk] = hdr; \
- else if (dfs_nr[max_hdr[blk]] > dfs_nr[hdr]) \
- RESET_BIT (inner, hdr); \
- else if (dfs_nr[max_hdr[blk]] < dfs_nr[hdr]) \
- { \
- RESET_BIT (inner,max_hdr[blk]); \
- max_hdr[blk] = hdr; \
- } \
-}
-
-
-/* Find regions for interblock scheduling.
-
- A region for scheduling can be:
-
- * A loop-free procedure, or
-
- * A reducible inner loop, or
-
- * A basic block not contained in any other region.
-
-
- ?!? In theory we could build other regions based on extended basic
- blocks or reverse extended basic blocks. Is it worth the trouble?
-
- Loop blocks that form a region are put into the region's block list
- in topological order.
-
- This procedure stores its results into the following global (ick) variables
-
- * rgn_nr
- * rgn_table
- * rgn_bb_table
- * block_to_bb
- * containing region
-
-
- We use dominator relationships to avoid making regions out of non-reducible
- loops.
-
- This procedure needs to be converted to work on pred/succ lists instead
- of edge tables. That would simplify it somewhat. */
-
-static void
-find_rgns (s_preds, s_succs, num_preds, num_succs, dom)
- int_list_ptr *s_preds;
- int_list_ptr *s_succs;
- int *num_preds;
- int *num_succs;
- sbitmap *dom;
-{
- int *max_hdr, *dfs_nr, *stack, *queue, *degree;
- char no_loops = 1;
- int node, child, loop_head, i, head, tail;
- int count = 0, sp, idx = 0, current_edge = out_edges[0];
- int num_bbs, num_insns, unreachable;
- int too_large_failure;
-
- /* Note if an edge has been passed. */
- sbitmap passed;
-
- /* Note if a block is a natural loop header. */
- sbitmap header;
-
- /* Note if a block is an natural inner loop header. */
- sbitmap inner;
-
- /* Note if a block is in the block queue. */
- sbitmap in_queue;
-
- /* Note if a block is in the block queue. */
- sbitmap in_stack;
-
- /* Perform a DFS traversal of the cfg. Identify loop headers, inner loops
- and a mapping from block to its loop header (if the block is contained
- in a loop, else -1).
-
- Store results in HEADER, INNER, and MAX_HDR respectively, these will
- be used as inputs to the second traversal.
-
- STACK, SP and DFS_NR are only used during the first traversal. */
-
- /* Allocate and initialize variables for the first traversal. */
- max_hdr = (int *) alloca (n_basic_blocks * sizeof (int));
- dfs_nr = (int *) alloca (n_basic_blocks * sizeof (int));
- bzero ((char *) dfs_nr, n_basic_blocks * sizeof (int));
- stack = (int *) alloca (nr_edges * sizeof (int));
-
- inner = sbitmap_alloc (n_basic_blocks);
- sbitmap_ones (inner);
-
- header = sbitmap_alloc (n_basic_blocks);
- sbitmap_zero (header);
-
- passed = sbitmap_alloc (nr_edges);
- sbitmap_zero (passed);
-
- in_queue = sbitmap_alloc (n_basic_blocks);
- sbitmap_zero (in_queue);
-
- in_stack = sbitmap_alloc (n_basic_blocks);
- sbitmap_zero (in_stack);
-
- for (i = 0; i < n_basic_blocks; i++)
- max_hdr[i] = -1;
-
- /* DFS traversal to find inner loops in the cfg. */
-
- sp = -1;
- while (1)
- {
- if (current_edge == 0 || TEST_BIT (passed, current_edge))
- {
- /* We have reached a leaf node or a node that was already
- processed. Pop edges off the stack until we find
- an edge that has not yet been processed. */
- while (sp >= 0
- && (current_edge == 0 || TEST_BIT (passed, current_edge)))
- {
- /* Pop entry off the stack. */
- current_edge = stack[sp--];
- node = FROM_BLOCK (current_edge);
- child = TO_BLOCK (current_edge);
- RESET_BIT (in_stack, child);
- if (max_hdr[child] >= 0 && TEST_BIT (in_stack, max_hdr[child]))
- UPDATE_LOOP_RELATIONS (node, max_hdr[child]);
- current_edge = NEXT_OUT (current_edge);
- }
-
- /* See if have finished the DFS tree traversal. */
- if (sp < 0 && TEST_BIT (passed, current_edge))
- break;
-
- /* Nope, continue the traversal with the popped node. */
- continue;
- }
-
- /* Process a node. */
- node = FROM_BLOCK (current_edge);
- child = TO_BLOCK (current_edge);
- SET_BIT (in_stack, node);
- dfs_nr[node] = ++count;
-
- /* If the successor is in the stack, then we've found a loop.
- Mark the loop, if it is not a natural loop, then it will
- be rejected during the second traversal. */
- if (TEST_BIT (in_stack, child))
- {
- no_loops = 0;
- SET_BIT (header, child);
- UPDATE_LOOP_RELATIONS (node, child);
- SET_BIT (passed, current_edge);
- current_edge = NEXT_OUT (current_edge);
- continue;
- }
-
- /* If the child was already visited, then there is no need to visit
- it again. Just update the loop relationships and restart
- with a new edge. */
- if (dfs_nr[child])
- {
- if (max_hdr[child] >= 0 && TEST_BIT (in_stack, max_hdr[child]))
- UPDATE_LOOP_RELATIONS (node, max_hdr[child]);
- SET_BIT (passed, current_edge);
- current_edge = NEXT_OUT (current_edge);
- continue;
- }
-
- /* Push an entry on the stack and continue DFS traversal. */
- stack[++sp] = current_edge;
- SET_BIT (passed, current_edge);
- current_edge = OUT_EDGES (child);
- }
-
- /* Another check for unreachable blocks. The earlier test in
- is_cfg_nonregular only finds unreachable blocks that do not
- form a loop.
-
- The DFS traversal will mark every block that is reachable from
- the entry node by placing a nonzero value in dfs_nr. Thus if
- dfs_nr is zero for any block, then it must be unreachable. */
- unreachable = 0;
- for (i = 0; i < n_basic_blocks; i++)
- if (dfs_nr[i] == 0)
- {
- unreachable = 1;
- break;
- }
-
- /* Gross. To avoid wasting memory, the second pass uses the dfs_nr array
- to hold degree counts. */
- degree = dfs_nr;
-
- /* Compute the in-degree of every block in the graph */
- for (i = 0; i < n_basic_blocks; i++)
- degree[i] = num_preds[i];
-
- /* Do not perform region scheduling if there are any unreachable
- blocks. */
- if (!unreachable)
- {
- if (no_loops)
- SET_BIT (header, 0);
-
- /* Second travsersal:find reducible inner loops and topologically sort
- block of each region. */
-
- queue = (int *) alloca (n_basic_blocks * sizeof (int));
-
- /* Find blocks which are inner loop headers. We still have non-reducible
- loops to consider at this point. */
- for (i = 0; i < n_basic_blocks; i++)
- {
- if (TEST_BIT (header, i) && TEST_BIT (inner, i))
- {
- int_list_ptr ps;
- int j;
-
- /* Now check that the loop is reducible. We do this separate
- from finding inner loops so that we do not find a reducible
- loop which contains an inner non-reducible loop.
-
- A simple way to find reducible/natrual loops is to verify
- that each block in the loop is dominated by the loop
- header.
-
- If there exists a block that is not dominated by the loop
- header, then the block is reachable from outside the loop
- and thus the loop is not a natural loop. */
- for (j = 0; j < n_basic_blocks; j++)
- {
- /* First identify blocks in the loop, except for the loop
- entry block. */
- if (i == max_hdr[j] && i != j)
- {
- /* Now verify that the block is dominated by the loop
- header. */
- if (!TEST_BIT (dom[j], i))
- break;
- }
- }
-
- /* If we exited the loop early, then I is the header of a non
- reducible loop and we should quit processing it now. */
- if (j != n_basic_blocks)
- continue;
-
- /* I is a header of an inner loop, or block 0 in a subroutine
- with no loops at all. */
- head = tail = -1;
- too_large_failure = 0;
- loop_head = max_hdr[i];
-
- /* Decrease degree of all I's successors for topological
- ordering. */
- for (ps = s_succs[i]; ps; ps = ps->next)
- if (INT_LIST_VAL (ps) != EXIT_BLOCK
- && INT_LIST_VAL (ps) != ENTRY_BLOCK)
- --degree[INT_LIST_VAL(ps)];
-
- /* Estimate # insns, and count # blocks in the region. */
- num_bbs = 1;
- num_insns = (INSN_LUID (BLOCK_END (i))
- - INSN_LUID (BLOCK_HEAD (i)));
-
-
- /* Find all loop latches (blocks which back edges to the loop
- header) or all the leaf blocks in the cfg has no loops.
-
- Place those blocks into the queue. */
- if (no_loops)
- {
- for (j = 0; j < n_basic_blocks; j++)
- /* Leaf nodes have only a single successor which must
- be EXIT_BLOCK. */
- if (num_succs[j] == 1
- && INT_LIST_VAL (s_succs[j]) == EXIT_BLOCK)
- {
- queue[++tail] = j;
- SET_BIT (in_queue, j);
-
- if (too_large (j, &num_bbs, &num_insns))
- {
- too_large_failure = 1;
- break;
- }
- }
- }
- else
- {
- int_list_ptr ps;
-
- for (ps = s_preds[i]; ps; ps = ps->next)
- {
- node = INT_LIST_VAL (ps);
-
- if (node == ENTRY_BLOCK || node == EXIT_BLOCK)
- continue;
-
- if (max_hdr[node] == loop_head && node != i)
- {
- /* This is a loop latch. */
- queue[++tail] = node;
- SET_BIT (in_queue, node);
-
- if (too_large (node, &num_bbs, &num_insns))
- {
- too_large_failure = 1;
- break;
- }
- }
-
- }
- }
-
- /* Now add all the blocks in the loop to the queue.
-
- We know the loop is a natural loop; however the algorithm
- above will not always mark certain blocks as being in the
- loop. Consider:
- node children
- a b,c
- b c
- c a,d
- d b
-
-
- The algorithm in the DFS traversal may not mark B & D as part
- of the loop (ie they will not have max_hdr set to A).
-
- We know they can not be loop latches (else they would have
- had max_hdr set since they'd have a backedge to a dominator
- block). So we don't need them on the initial queue.
-
- We know they are part of the loop because they are dominated
- by the loop header and can be reached by a backwards walk of
- the edges starting with nodes on the initial queue.
-
- It is safe and desirable to include those nodes in the
- loop/scheduling region. To do so we would need to decrease
- the degree of a node if it is the target of a backedge
- within the loop itself as the node is placed in the queue.
-
- We do not do this because I'm not sure that the actual
- scheduling code will properly handle this case. ?!? */
-
- while (head < tail && !too_large_failure)
- {
- int_list_ptr ps;
- child = queue[++head];
-
- for (ps = s_preds[child]; ps; ps = ps->next)
- {
- node = INT_LIST_VAL (ps);
-
- /* See discussion above about nodes not marked as in
- this loop during the initial DFS traversal. */
- if (node == ENTRY_BLOCK || node == EXIT_BLOCK
- || max_hdr[node] != loop_head)
- {
- tail = -1;
- break;
- }
- else if (!TEST_BIT (in_queue, node) && node != i)
- {
- queue[++tail] = node;
- SET_BIT (in_queue, node);
-
- if (too_large (node, &num_bbs, &num_insns))
- {
- too_large_failure = 1;
- break;
- }
- }
- }
- }
-
- if (tail >= 0 && !too_large_failure)
- {
- /* Place the loop header into list of region blocks. */
- degree[i] = -1;
- rgn_bb_table[idx] = i;
- RGN_NR_BLOCKS (nr_regions) = num_bbs;
- RGN_BLOCKS (nr_regions) = idx++;
- CONTAINING_RGN (i) = nr_regions;
- BLOCK_TO_BB (i) = count = 0;
-
- /* Remove blocks from queue[] when their in degree becomes
- zero. Repeat until no blocks are left on the list. This
- produces a topological list of blocks in the region. */
- while (tail >= 0)
- {
- int_list_ptr ps;
-
- if (head < 0)
- head = tail;
- child = queue[head];
- if (degree[child] == 0)
- {
- degree[child] = -1;
- rgn_bb_table[idx++] = child;
- BLOCK_TO_BB (child) = ++count;
- CONTAINING_RGN (child) = nr_regions;
- queue[head] = queue[tail--];
-
- for (ps = s_succs[child]; ps; ps = ps->next)
- if (INT_LIST_VAL (ps) != ENTRY_BLOCK
- && INT_LIST_VAL (ps) != EXIT_BLOCK)
- --degree[INT_LIST_VAL (ps)];
- }
- else
- --head;
- }
- ++nr_regions;
- }
- }
- }
- }
-
- /* Any block that did not end up in a region is placed into a region
- by itself. */
- for (i = 0; i < n_basic_blocks; i++)
- if (degree[i] >= 0)
- {
- rgn_bb_table[idx] = i;
- RGN_NR_BLOCKS (nr_regions) = 1;
- RGN_BLOCKS (nr_regions) = idx++;
- CONTAINING_RGN (i) = nr_regions++;
- BLOCK_TO_BB (i) = 0;
- }
-
- free (passed);
- free (header);
- free (inner);
- free (in_queue);
- free (in_stack);
-}
-
-
-/* functions for regions scheduling information */
-
-/* Compute dominators, probability, and potential-split-edges of bb.
- Assume that these values were already computed for bb's predecessors. */
-
-static void
-compute_dom_prob_ps (bb)
- int bb;
-{
- int nxt_in_edge, fst_in_edge, pred;
- int fst_out_edge, nxt_out_edge, nr_out_edges, nr_rgn_out_edges;
-
- prob[bb] = 0.0;
- if (IS_RGN_ENTRY (bb))
- {
- BITSET_ADD (dom[bb], 0, bbset_size);
- prob[bb] = 1.0;
- return;
- }
-
- fst_in_edge = nxt_in_edge = IN_EDGES (BB_TO_BLOCK (bb));
-
- /* intialize dom[bb] to '111..1' */
- BITSET_INVERT (dom[bb], bbset_size);
-
- do
- {
- pred = FROM_BLOCK (nxt_in_edge);
- BITSET_INTER (dom[bb], dom[BLOCK_TO_BB (pred)], bbset_size);
-
- BITSET_UNION (ancestor_edges[bb], ancestor_edges[BLOCK_TO_BB (pred)],
- edgeset_size);
-
- BITSET_ADD (ancestor_edges[bb], EDGE_TO_BIT (nxt_in_edge), edgeset_size);
-
- nr_out_edges = 1;
- nr_rgn_out_edges = 0;
- fst_out_edge = OUT_EDGES (pred);
- nxt_out_edge = NEXT_OUT (fst_out_edge);
- BITSET_UNION (pot_split[bb], pot_split[BLOCK_TO_BB (pred)],
- edgeset_size);
-
- BITSET_ADD (pot_split[bb], EDGE_TO_BIT (fst_out_edge), edgeset_size);
-
- /* the successor doesn't belong the region? */
- if (CONTAINING_RGN (TO_BLOCK (fst_out_edge)) !=
- CONTAINING_RGN (BB_TO_BLOCK (bb)))
- ++nr_rgn_out_edges;
-
- while (fst_out_edge != nxt_out_edge)
- {
- ++nr_out_edges;
- /* the successor doesn't belong the region? */
- if (CONTAINING_RGN (TO_BLOCK (nxt_out_edge)) !=
- CONTAINING_RGN (BB_TO_BLOCK (bb)))
- ++nr_rgn_out_edges;
- BITSET_ADD (pot_split[bb], EDGE_TO_BIT (nxt_out_edge), edgeset_size);
- nxt_out_edge = NEXT_OUT (nxt_out_edge);
-
- }
-
- /* now nr_rgn_out_edges is the number of region-exit edges from pred,
- and nr_out_edges will be the number of pred out edges not leaving
- the region. */
- nr_out_edges -= nr_rgn_out_edges;
- if (nr_rgn_out_edges > 0)
- prob[bb] += 0.9 * prob[BLOCK_TO_BB (pred)] / nr_out_edges;
- else
- prob[bb] += prob[BLOCK_TO_BB (pred)] / nr_out_edges;
- nxt_in_edge = NEXT_IN (nxt_in_edge);
- }
- while (fst_in_edge != nxt_in_edge);
-
- BITSET_ADD (dom[bb], bb, bbset_size);
- BITSET_DIFFER (pot_split[bb], ancestor_edges[bb], edgeset_size);
-
- if (sched_verbose >= 2)
- fprintf (dump, ";; bb_prob(%d, %d) = %3d\n", bb, BB_TO_BLOCK (bb), (int) (100.0 * prob[bb]));
-} /* compute_dom_prob_ps */
-
-/* functions for target info */
-
-/* Compute in BL the list of split-edges of bb_src relatively to bb_trg.
- Note that bb_trg dominates bb_src. */
-
-static void
-split_edges (bb_src, bb_trg, bl)
- int bb_src;
- int bb_trg;
- edgelst *bl;
-{
- int es = edgeset_size;
- edgeset src = (edgeset) alloca (es * sizeof (HOST_WIDE_INT));
-
- while (es--)
- src[es] = (pot_split[bb_src])[es];
- BITSET_DIFFER (src, pot_split[bb_trg], edgeset_size);
- extract_bitlst (src, edgeset_size, bl);
-}
-
-
-/* Find the valid candidate-source-blocks for the target block TRG, compute
- their probability, and check if they are speculative or not.
- For speculative sources, compute their update-blocks and split-blocks. */
-
-static void
-compute_trg_info (trg)
- int trg;
-{
- register candidate *sp;
- edgelst el;
- int check_block, update_idx;
- int i, j, k, fst_edge, nxt_edge;
-
- /* define some of the fields for the target bb as well */
- sp = candidate_table + trg;
- sp->is_valid = 1;
- sp->is_speculative = 0;
- sp->src_prob = 100;
-
- for (i = trg + 1; i < current_nr_blocks; i++)
- {
- sp = candidate_table + i;
-
- sp->is_valid = IS_DOMINATED (i, trg);
- if (sp->is_valid)
- {
- sp->src_prob = GET_SRC_PROB (i, trg);
- sp->is_valid = (sp->src_prob >= MIN_PROBABILITY);
- }
-
- if (sp->is_valid)
- {
- split_edges (i, trg, &el);
- sp->is_speculative = (el.nr_members) ? 1 : 0;
- if (sp->is_speculative && !flag_schedule_speculative)
- sp->is_valid = 0;
- }
-
- if (sp->is_valid)
- {
- sp->split_bbs.first_member = &bblst_table[bblst_last];
- sp->split_bbs.nr_members = el.nr_members;
- for (j = 0; j < el.nr_members; bblst_last++, j++)
- bblst_table[bblst_last] =
- TO_BLOCK (rgn_edges[el.first_member[j]]);
- sp->update_bbs.first_member = &bblst_table[bblst_last];
- update_idx = 0;
- for (j = 0; j < el.nr_members; j++)
- {
- check_block = FROM_BLOCK (rgn_edges[el.first_member[j]]);
- fst_edge = nxt_edge = OUT_EDGES (check_block);
- do
- {
- for (k = 0; k < el.nr_members; k++)
- if (EDGE_TO_BIT (nxt_edge) == el.first_member[k])
- break;
-
- if (k >= el.nr_members)
- {
- bblst_table[bblst_last++] = TO_BLOCK (nxt_edge);
- update_idx++;
- }
-
- nxt_edge = NEXT_OUT (nxt_edge);
- }
- while (fst_edge != nxt_edge);
- }
- sp->update_bbs.nr_members = update_idx;
-
- }
- else
- {
- sp->split_bbs.nr_members = sp->update_bbs.nr_members = 0;
-
- sp->is_speculative = 0;
- sp->src_prob = 0;
- }
- }
-} /* compute_trg_info */
-
-
-/* Print candidates info, for debugging purposes. Callable from debugger. */
-
-void
-debug_candidate (i)
- int i;
-{
- if (!candidate_table[i].is_valid)
- return;
-
- if (candidate_table[i].is_speculative)
- {
- int j;
- fprintf (dump, "src b %d bb %d speculative \n", BB_TO_BLOCK (i), i);
-
- fprintf (dump, "split path: ");
- for (j = 0; j < candidate_table[i].split_bbs.nr_members; j++)
- {
- int b = candidate_table[i].split_bbs.first_member[j];
-
- fprintf (dump, " %d ", b);
- }
- fprintf (dump, "\n");
-
- fprintf (dump, "update path: ");
- for (j = 0; j < candidate_table[i].update_bbs.nr_members; j++)
- {
- int b = candidate_table[i].update_bbs.first_member[j];
-
- fprintf (dump, " %d ", b);
- }
- fprintf (dump, "\n");
- }
- else
- {
- fprintf (dump, " src %d equivalent\n", BB_TO_BLOCK (i));
- }
-}
-
-
-/* Print candidates info, for debugging purposes. Callable from debugger. */
-
-void
-debug_candidates (trg)
- int trg;
-{
- int i;
-
- fprintf (dump, "----------- candidate table: target: b=%d bb=%d ---\n",
- BB_TO_BLOCK (trg), trg);
- for (i = trg + 1; i < current_nr_blocks; i++)
- debug_candidate (i);
-}
-
-
-/* functions for speculative scheduing */
-
-/* Return 0 if x is a set of a register alive in the beginning of one
- of the split-blocks of src, otherwise return 1. */
-
-static int
-check_live_1 (src, x)
- int src;
- rtx x;
-{
- register int i;
- register int regno;
- register rtx reg = SET_DEST (x);
-
- if (reg == 0)
- return 1;
-
- while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
- || GET_CODE (reg) == SIGN_EXTRACT
- || GET_CODE (reg) == STRICT_LOW_PART)
- reg = XEXP (reg, 0);
-
- if (GET_CODE (reg) == PARALLEL
- && GET_MODE (reg) == BLKmode)
- {
- register int i;
- for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
- if (check_live_1 (src, XVECEXP (reg, 0, i)))
- return 1;
- return 0;
- }
-
- if (GET_CODE (reg) != REG)
- return 1;
-
- regno = REGNO (reg);
-
- if (regno < FIRST_PSEUDO_REGISTER && global_regs[regno])
- {
- /* Global registers are assumed live */
- return 0;
- }
- else
- {
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- /* check for hard registers */
- int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
- while (--j >= 0)
- {
- for (i = 0; i < candidate_table[src].split_bbs.nr_members; i++)
- {
- int b = candidate_table[src].split_bbs.first_member[i];
-
- if (REGNO_REG_SET_P (basic_block_live_at_start[b], regno + j))
- {
- return 0;
- }
- }
- }
- }
- else
- {
- /* check for psuedo registers */
- for (i = 0; i < candidate_table[src].split_bbs.nr_members; i++)
- {
- int b = candidate_table[src].split_bbs.first_member[i];
-
- if (REGNO_REG_SET_P (basic_block_live_at_start[b], regno))
- {
- return 0;
- }
- }
- }
- }
-
- return 1;
-}
-
-
-/* If x is a set of a register R, mark that R is alive in the beginning
- of every update-block of src. */
-
-static void
-update_live_1 (src, x)
- int src;
- rtx x;
-{
- register int i;
- register int regno;
- register rtx reg = SET_DEST (x);
-
- if (reg == 0)
- return;
-
- while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == ZERO_EXTRACT
- || GET_CODE (reg) == SIGN_EXTRACT
- || GET_CODE (reg) == STRICT_LOW_PART)
- reg = XEXP (reg, 0);
-
- if (GET_CODE (reg) == PARALLEL
- && GET_MODE (reg) == BLKmode)
- {
- register int i;
- for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
- update_live_1 (src, XVECEXP (reg, 0, i));
- return;
- }
-
- if (GET_CODE (reg) != REG)
- return;
-
- /* Global registers are always live, so the code below does not apply
- to them. */
-
- regno = REGNO (reg);
-
- if (regno >= FIRST_PSEUDO_REGISTER || !global_regs[regno])
- {
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
- while (--j >= 0)
- {
- for (i = 0; i < candidate_table[src].update_bbs.nr_members; i++)
- {
- int b = candidate_table[src].update_bbs.first_member[i];
-
- SET_REGNO_REG_SET (basic_block_live_at_start[b], regno + j);
- }
- }
- }
- else
- {
- for (i = 0; i < candidate_table[src].update_bbs.nr_members; i++)
- {
- int b = candidate_table[src].update_bbs.first_member[i];
-
- SET_REGNO_REG_SET (basic_block_live_at_start[b], regno);
- }
- }
- }
-}
-
-
-/* Return 1 if insn can be speculatively moved from block src to trg,
- otherwise return 0. Called before first insertion of insn to
- ready-list or before the scheduling. */
-
-static int
-check_live (insn, src)
- rtx insn;
- int src;
-{
- /* find the registers set by instruction */
- if (GET_CODE (PATTERN (insn)) == SET
- || GET_CODE (PATTERN (insn)) == CLOBBER)
- return check_live_1 (src, PATTERN (insn));
- else if (GET_CODE (PATTERN (insn)) == PARALLEL)
- {
- int j;
- for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
- if ((GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
- || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
- && !check_live_1 (src, XVECEXP (PATTERN (insn), 0, j)))
- return 0;
-
- return 1;
- }
-
- return 1;
-}
-
-
-/* Update the live registers info after insn was moved speculatively from
- block src to trg. */
-
-static void
-update_live (insn, src)
- rtx insn;
- int src;
-{
- /* find the registers set by instruction */
- if (GET_CODE (PATTERN (insn)) == SET
- || GET_CODE (PATTERN (insn)) == CLOBBER)
- update_live_1 (src, PATTERN (insn));
- else if (GET_CODE (PATTERN (insn)) == PARALLEL)
- {
- int j;
- for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
- if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
- || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
- update_live_1 (src, XVECEXP (PATTERN (insn), 0, j));
- }
-}
-
-/* Exception Free Loads:
-
- We define five classes of speculative loads: IFREE, IRISKY,
- PFREE, PRISKY, and MFREE.
-
- IFREE loads are loads that are proved to be exception-free, just
- by examining the load insn. Examples for such loads are loads
- from TOC and loads of global data.
-
- IRISKY loads are loads that are proved to be exception-risky,
- just by examining the load insn. Examples for such loads are
- volatile loads and loads from shared memory.
-
- PFREE loads are loads for which we can prove, by examining other
- insns, that they are exception-free. Currently, this class consists
- of loads for which we are able to find a "similar load", either in
- the target block, or, if only one split-block exists, in that split
- block. Load2 is similar to load1 if both have same single base
- register. We identify only part of the similar loads, by finding
- an insn upon which both load1 and load2 have a DEF-USE dependence.
-
- PRISKY loads are loads for which we can prove, by examining other
- insns, that they are exception-risky. Currently we have two proofs for
- such loads. The first proof detects loads that are probably guarded by a
- test on the memory address. This proof is based on the
- backward and forward data dependence information for the region.
- Let load-insn be the examined load.
- Load-insn is PRISKY iff ALL the following hold:
-
- - insn1 is not in the same block as load-insn
- - there is a DEF-USE dependence chain (insn1, ..., load-insn)
- - test-insn is either a compare or a branch, not in the same block as load-insn
- - load-insn is reachable from test-insn
- - there is a DEF-USE dependence chain (insn1, ..., test-insn)
-
- This proof might fail when the compare and the load are fed
- by an insn not in the region. To solve this, we will add to this
- group all loads that have no input DEF-USE dependence.
-
- The second proof detects loads that are directly or indirectly
- fed by a speculative load. This proof is affected by the
- scheduling process. We will use the flag fed_by_spec_load.
- Initially, all insns have this flag reset. After a speculative
- motion of an insn, if insn is either a load, or marked as
- fed_by_spec_load, we will also mark as fed_by_spec_load every
- insn1 for which a DEF-USE dependence (insn, insn1) exists. A
- load which is fed_by_spec_load is also PRISKY.
-
- MFREE (maybe-free) loads are all the remaining loads. They may be
- exception-free, but we cannot prove it.
-
- Now, all loads in IFREE and PFREE classes are considered
- exception-free, while all loads in IRISKY and PRISKY classes are
- considered exception-risky. As for loads in the MFREE class,
- these are considered either exception-free or exception-risky,
- depending on whether we are pessimistic or optimistic. We have
- to take the pessimistic approach to assure the safety of
- speculative scheduling, but we can take the optimistic approach
- by invoking the -fsched_spec_load_dangerous option. */
-
-enum INSN_TRAP_CLASS
-{
- TRAP_FREE = 0, IFREE = 1, PFREE_CANDIDATE = 2,
- PRISKY_CANDIDATE = 3, IRISKY = 4, TRAP_RISKY = 5
-};
-
-#define WORST_CLASS(class1, class2) \
-((class1 > class2) ? class1 : class2)
-
-/* Indexed by INSN_UID, and set if there's DEF-USE dependence between */
-/* some speculatively moved load insn and this one. */
-char *fed_by_spec_load;
-char *is_load_insn;
-
-/* Non-zero if block bb_to is equal to, or reachable from block bb_from. */
-#define IS_REACHABLE(bb_from, bb_to) \
-(bb_from == bb_to \
- || IS_RGN_ENTRY (bb_from) \
- || (bitset_member (ancestor_edges[bb_to], \
- EDGE_TO_BIT (IN_EDGES (BB_TO_BLOCK (bb_from))), \
- edgeset_size)))
-#define FED_BY_SPEC_LOAD(insn) (fed_by_spec_load[INSN_UID (insn)])
-#define IS_LOAD_INSN(insn) (is_load_insn[INSN_UID (insn)])
-
-/* Non-zero iff the address is comprised from at most 1 register */
-#define CONST_BASED_ADDRESS_P(x) \
- (GET_CODE (x) == REG \
- || ((GET_CODE (x) == PLUS || GET_CODE (x) == MINUS \
- || (GET_CODE (x) == LO_SUM)) \
- && (GET_CODE (XEXP (x, 0)) == CONST_INT \
- || GET_CODE (XEXP (x, 1)) == CONST_INT)))
-
-/* Turns on the fed_by_spec_load flag for insns fed by load_insn. */
-
-static void
-set_spec_fed (load_insn)
- rtx load_insn;
-{
- rtx link;
-
- for (link = INSN_DEPEND (load_insn); link; link = XEXP (link, 1))
- if (GET_MODE (link) == VOIDmode)
- FED_BY_SPEC_LOAD (XEXP (link, 0)) = 1;
-} /* set_spec_fed */
-
-/* On the path from the insn to load_insn_bb, find a conditional branch */
-/* depending on insn, that guards the speculative load. */
-
-static int
-find_conditional_protection (insn, load_insn_bb)
- rtx insn;
- int load_insn_bb;
-{
- rtx link;
-
- /* iterate through DEF-USE forward dependences */
- for (link = INSN_DEPEND (insn); link; link = XEXP (link, 1))
- {
- rtx next = XEXP (link, 0);
- if ((CONTAINING_RGN (INSN_BLOCK (next)) ==
- CONTAINING_RGN (BB_TO_BLOCK (load_insn_bb)))
- && IS_REACHABLE (INSN_BB (next), load_insn_bb)
- && load_insn_bb != INSN_BB (next)
- && GET_MODE (link) == VOIDmode
- && (GET_CODE (next) == JUMP_INSN
- || find_conditional_protection (next, load_insn_bb)))
- return 1;
- }
- return 0;
-} /* find_conditional_protection */
-
-/* Returns 1 if the same insn1 that participates in the computation
- of load_insn's address is feeding a conditional branch that is
- guarding on load_insn. This is true if we find a the two DEF-USE
- chains:
- insn1 -> ... -> conditional-branch
- insn1 -> ... -> load_insn,
- and if a flow path exist:
- insn1 -> ... -> conditional-branch -> ... -> load_insn,
- and if insn1 is on the path
- region-entry -> ... -> bb_trg -> ... load_insn.
-
- Locate insn1 by climbing on LOG_LINKS from load_insn.
- Locate the branch by following INSN_DEPEND from insn1. */
-
-static int
-is_conditionally_protected (load_insn, bb_src, bb_trg)
- rtx load_insn;
- int bb_src, bb_trg;
-{
- rtx link;
-
- for (link = LOG_LINKS (load_insn); link; link = XEXP (link, 1))
- {
- rtx insn1 = XEXP (link, 0);
-
- /* must be a DEF-USE dependence upon non-branch */
- if (GET_MODE (link) != VOIDmode
- || GET_CODE (insn1) == JUMP_INSN)
- continue;
-
- /* must exist a path: region-entry -> ... -> bb_trg -> ... load_insn */
- if (INSN_BB (insn1) == bb_src
- || (CONTAINING_RGN (INSN_BLOCK (insn1))
- != CONTAINING_RGN (BB_TO_BLOCK (bb_src)))
- || (!IS_REACHABLE (bb_trg, INSN_BB (insn1))
- && !IS_REACHABLE (INSN_BB (insn1), bb_trg)))
- continue;
-
- /* now search for the conditional-branch */
- if (find_conditional_protection (insn1, bb_src))
- return 1;
-
- /* recursive step: search another insn1, "above" current insn1. */
- return is_conditionally_protected (insn1, bb_src, bb_trg);
- }
-
- /* the chain does not exsist */
- return 0;
-} /* is_conditionally_protected */
-
-/* Returns 1 if a clue for "similar load" 'insn2' is found, and hence
- load_insn can move speculatively from bb_src to bb_trg. All the
- following must hold:
-
- (1) both loads have 1 base register (PFREE_CANDIDATEs).
- (2) load_insn and load1 have a def-use dependence upon
- the same insn 'insn1'.
- (3) either load2 is in bb_trg, or:
- - there's only one split-block, and
- - load1 is on the escape path, and
-
- From all these we can conclude that the two loads access memory
- addresses that differ at most by a constant, and hence if moving
- load_insn would cause an exception, it would have been caused by
- load2 anyhow. */
-
-static int
-is_pfree (load_insn, bb_src, bb_trg)
- rtx load_insn;
- int bb_src, bb_trg;
-{
- rtx back_link;
- register candidate *candp = candidate_table + bb_src;
-
- if (candp->split_bbs.nr_members != 1)
- /* must have exactly one escape block */
- return 0;
-
- for (back_link = LOG_LINKS (load_insn);
- back_link; back_link = XEXP (back_link, 1))
- {
- rtx insn1 = XEXP (back_link, 0);
-
- if (GET_MODE (back_link) == VOIDmode)
- {
- /* found a DEF-USE dependence (insn1, load_insn) */
- rtx fore_link;
-
- for (fore_link = INSN_DEPEND (insn1);
- fore_link; fore_link = XEXP (fore_link, 1))
- {
- rtx insn2 = XEXP (fore_link, 0);
- if (GET_MODE (fore_link) == VOIDmode)
- {
- /* found a DEF-USE dependence (insn1, insn2) */
- if (haifa_classify_insn (insn2) != PFREE_CANDIDATE)
- /* insn2 not guaranteed to be a 1 base reg load */
- continue;
-
- if (INSN_BB (insn2) == bb_trg)
- /* insn2 is the similar load, in the target block */
- return 1;
-
- if (*(candp->split_bbs.first_member) == INSN_BLOCK (insn2))
- /* insn2 is a similar load, in a split-block */
- return 1;
- }
- }
- }
- }
-
- /* couldn't find a similar load */
- return 0;
-} /* is_pfree */
-
-/* Returns a class that insn with GET_DEST(insn)=x may belong to,
- as found by analyzing insn's expression. */
-
-static int
-may_trap_exp (x, is_store)
- rtx x;
- int is_store;
-{
- enum rtx_code code;
-
- if (x == 0)
- return TRAP_FREE;
- code = GET_CODE (x);
- if (is_store)
- {
- if (code == MEM)
- return TRAP_RISKY;
- else
- return TRAP_FREE;
- }
- if (code == MEM)
- {
- /* The insn uses memory */
- /* a volatile load */
- if (MEM_VOLATILE_P (x))
- return IRISKY;
- /* an exception-free load */
- if (!may_trap_p (x))
- return IFREE;
- /* a load with 1 base register, to be further checked */
- if (CONST_BASED_ADDRESS_P (XEXP (x, 0)))
- return PFREE_CANDIDATE;
- /* no info on the load, to be further checked */
- return PRISKY_CANDIDATE;
- }
- else
- {
- char *fmt;
- int i, insn_class = TRAP_FREE;
-
- /* neither store nor load, check if it may cause a trap */
- if (may_trap_p (x))
- return TRAP_RISKY;
- /* recursive step: walk the insn... */
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- {
- int tmp_class = may_trap_exp (XEXP (x, i), is_store);
- insn_class = WORST_CLASS (insn_class, tmp_class);
- }
- else if (fmt[i] == 'E')
- {
- int j;
- for (j = 0; j < XVECLEN (x, i); j++)
- {
- int tmp_class = may_trap_exp (XVECEXP (x, i, j), is_store);
- insn_class = WORST_CLASS (insn_class, tmp_class);
- if (insn_class == TRAP_RISKY || insn_class == IRISKY)
- break;
- }
- }
- if (insn_class == TRAP_RISKY || insn_class == IRISKY)
- break;
- }
- return insn_class;
- }
-} /* may_trap_exp */
-
-
-/* Classifies insn for the purpose of verifying that it can be
- moved speculatively, by examining it's patterns, returning:
- TRAP_RISKY: store, or risky non-load insn (e.g. division by variable).
- TRAP_FREE: non-load insn.
- IFREE: load from a globaly safe location.
- IRISKY: volatile load.
- PFREE_CANDIDATE, PRISKY_CANDIDATE: load that need to be checked for
- being either PFREE or PRISKY. */
-
-static int
-haifa_classify_insn (insn)
- rtx insn;
-{
- rtx pat = PATTERN (insn);
- int tmp_class = TRAP_FREE;
- int insn_class = TRAP_FREE;
- enum rtx_code code;
-
- if (GET_CODE (pat) == PARALLEL)
- {
- int i, len = XVECLEN (pat, 0);
-
- for (i = len - 1; i >= 0; i--)
- {
- code = GET_CODE (XVECEXP (pat, 0, i));
- switch (code)
- {
- case CLOBBER:
- /* test if it is a 'store' */
- tmp_class = may_trap_exp (XEXP (XVECEXP (pat, 0, i), 0), 1);
- break;
- case SET:
- /* test if it is a store */
- tmp_class = may_trap_exp (SET_DEST (XVECEXP (pat, 0, i)), 1);
- if (tmp_class == TRAP_RISKY)
- break;
- /* test if it is a load */
- tmp_class =
- WORST_CLASS (tmp_class,
- may_trap_exp (SET_SRC (XVECEXP (pat, 0, i)), 0));
- break;
- case TRAP_IF:
- tmp_class = TRAP_RISKY;
- break;
- default:;
- }
- insn_class = WORST_CLASS (insn_class, tmp_class);
- if (insn_class == TRAP_RISKY || insn_class == IRISKY)
- break;
- }
- }
- else
- {
- code = GET_CODE (pat);
- switch (code)
- {
- case CLOBBER:
- /* test if it is a 'store' */
- tmp_class = may_trap_exp (XEXP (pat, 0), 1);
- break;
- case SET:
- /* test if it is a store */
- tmp_class = may_trap_exp (SET_DEST (pat), 1);
- if (tmp_class == TRAP_RISKY)
- break;
- /* test if it is a load */
- tmp_class =
- WORST_CLASS (tmp_class,
- may_trap_exp (SET_SRC (pat), 0));
- break;
- case TRAP_IF:
- tmp_class = TRAP_RISKY;
- break;
- default:;
- }
- insn_class = tmp_class;
- }
-
- return insn_class;
-
-} /* haifa_classify_insn */
-
-/* Return 1 if load_insn is prisky (i.e. if load_insn is fed by
- a load moved speculatively, or if load_insn is protected by
- a compare on load_insn's address). */
-
-static int
-is_prisky (load_insn, bb_src, bb_trg)
- rtx load_insn;
- int bb_src, bb_trg;
-{
- if (FED_BY_SPEC_LOAD (load_insn))
- return 1;
-
- if (LOG_LINKS (load_insn) == NULL)
- /* dependence may 'hide' out of the region. */
- return 1;
-
- if (is_conditionally_protected (load_insn, bb_src, bb_trg))
- return 1;
-
- return 0;
-} /* is_prisky */
-
-/* Insn is a candidate to be moved speculatively from bb_src to bb_trg.
- Return 1 if insn is exception-free (and the motion is valid)
- and 0 otherwise. */
-
-static int
-is_exception_free (insn, bb_src, bb_trg)
- rtx insn;
- int bb_src, bb_trg;
-{
- int insn_class = haifa_classify_insn (insn);
-
- /* handle non-load insns */
- switch (insn_class)
- {
- case TRAP_FREE:
- return 1;
- case TRAP_RISKY:
- return 0;
- default:;
- }
-
- /* handle loads */
- if (!flag_schedule_speculative_load)
- return 0;
- IS_LOAD_INSN (insn) = 1;
- switch (insn_class)
- {
- case IFREE:
- return (1);
- case IRISKY:
- return 0;
- case PFREE_CANDIDATE:
- if (is_pfree (insn, bb_src, bb_trg))
- return 1;
- /* don't 'break' here: PFREE-candidate is also PRISKY-candidate */
- case PRISKY_CANDIDATE:
- if (!flag_schedule_speculative_load_dangerous
- || is_prisky (insn, bb_src, bb_trg))
- return 0;
- break;
- default:;
- }
-
- return flag_schedule_speculative_load_dangerous;
-} /* is_exception_free */
-
-
-/* Process an insn's memory dependencies. There are four kinds of
- dependencies:
-
- (0) read dependence: read follows read
- (1) true dependence: read follows write
- (2) anti dependence: write follows read
- (3) output dependence: write follows write
-
- 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. */
-
-HAIFA_INLINE static char
-find_insn_mem_list (insn, x, list, list1)
- rtx insn, x;
- rtx list, list1;
-{
- while (list)
- {
- if (XEXP (list, 0) == insn
- && XEXP (list1, 0) == x)
- return 1;
- list = XEXP (list, 1);
- list1 = XEXP (list1, 1);
- }
- return 0;
-}
-
-
-/* Compute the function units used by INSN. This caches the value
- returned by function_units_used. A function unit is encoded as the
- unit number if the value is non-negative and the compliment of a
- mask if the value is negative. A function unit index is the
- non-negative encoding. */
-
-HAIFA_INLINE static int
-insn_unit (insn)
- rtx insn;
-{
- register int unit = INSN_UNIT (insn);
-
- if (unit == 0)
- {
- recog_memoized (insn);
-
- /* A USE insn, or something else we don't need to understand.
- We can't pass these directly to function_units_used because it will
- trigger a fatal error for unrecognizable insns. */
- if (INSN_CODE (insn) < 0)
- unit = -1;
- else
- {
- unit = function_units_used (insn);
- /* Increment non-negative values so we can cache zero. */
- if (unit >= 0)
- unit++;
- }
- /* We only cache 16 bits of the result, so if the value is out of
- range, don't cache it. */
- if (FUNCTION_UNITS_SIZE < HOST_BITS_PER_SHORT
- || unit >= 0
- || (~unit & ((1 << (HOST_BITS_PER_SHORT - 1)) - 1)) == 0)
- INSN_UNIT (insn) = unit;
- }
- return (unit > 0 ? unit - 1 : unit);
-}
-
-/* Compute the blockage range for executing INSN on UNIT. This caches
- the value returned by the blockage_range_function for the unit.
- These values are encoded in an int where the upper half gives the
- minimum value and the lower half gives the maximum value. */
-
-HAIFA_INLINE static unsigned int
-blockage_range (unit, insn)
- int unit;
- rtx insn;
-{
- unsigned int blockage = INSN_BLOCKAGE (insn);
- unsigned int range;
-
- if ((int) UNIT_BLOCKED (blockage) != unit + 1)
- {
- range = function_units[unit].blockage_range_function (insn);
- /* We only cache the blockage range for one unit and then only if
- the values fit. */
- if (HOST_BITS_PER_INT >= UNIT_BITS + 2 * BLOCKAGE_BITS)
- INSN_BLOCKAGE (insn) = ENCODE_BLOCKAGE (unit + 1, range);
- }
- else
- range = BLOCKAGE_RANGE (blockage);
-
- return range;
-}
-
-/* A vector indexed by function unit instance giving the last insn to use
- the unit. The value of the function unit instance index for unit U
- instance I is (U + I * FUNCTION_UNITS_SIZE). */
-static rtx unit_last_insn[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
-
-/* A vector indexed by function unit instance giving the minimum time when
- the unit will unblock based on the maximum blockage cost. */
-static int unit_tick[FUNCTION_UNITS_SIZE * MAX_MULTIPLICITY];
-
-/* A vector indexed by function unit number giving the number of insns
- that remain to use the unit. */
-static int unit_n_insns[FUNCTION_UNITS_SIZE];
-
-/* Reset the function unit state to the null state. */
-
-static void
-clear_units ()
-{
- bzero ((char *) unit_last_insn, sizeof (unit_last_insn));
- bzero ((char *) unit_tick, sizeof (unit_tick));
- bzero ((char *) unit_n_insns, sizeof (unit_n_insns));
-}
-
-/* Return the issue-delay of an insn */
-
-HAIFA_INLINE static int
-insn_issue_delay (insn)
- rtx insn;
-{
- int i, delay = 0;
- int unit = insn_unit (insn);
-
- /* efficiency note: in fact, we are working 'hard' to compute a
- value that was available in md file, and is not available in
- function_units[] structure. It would be nice to have this
- value there, too. */
- if (unit >= 0)
- {
- if (function_units[unit].blockage_range_function &&
- function_units[unit].blockage_function)
- delay = function_units[unit].blockage_function (insn, insn);
- }
- else
- for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
- if ((unit & 1) != 0 && function_units[i].blockage_range_function
- && function_units[i].blockage_function)
- delay = MAX (delay, function_units[i].blockage_function (insn, insn));
-
- return delay;
-}
-
-/* Return the actual hazard cost of executing INSN on the unit UNIT,
- instance INSTANCE at time CLOCK if the previous actual hazard cost
- was COST. */
-
-HAIFA_INLINE static int
-actual_hazard_this_instance (unit, instance, insn, clock, cost)
- int unit, instance, clock, cost;
- rtx insn;
-{
- int tick = unit_tick[instance]; /* issue time of the last issued insn */
-
- if (tick - clock > cost)
- {
- /* The scheduler is operating forward, so unit's last insn is the
- executing insn and INSN is the candidate insn. We want a
- more exact measure of the blockage if we execute INSN at CLOCK
- given when we committed the execution of the unit's last insn.
-
- The blockage value is given by either the unit's max blockage
- constant, blockage range function, or blockage function. Use
- the most exact form for the given unit. */
-
- if (function_units[unit].blockage_range_function)
- {
- if (function_units[unit].blockage_function)
- tick += (function_units[unit].blockage_function
- (unit_last_insn[instance], insn)
- - function_units[unit].max_blockage);
- else
- tick += ((int) MAX_BLOCKAGE_COST (blockage_range (unit, insn))
- - function_units[unit].max_blockage);
- }
- if (tick - clock > cost)
- cost = tick - clock;
- }
- return cost;
-}
-
-/* Record INSN as having begun execution on the units encoded by UNIT at
- time CLOCK. */
-
-HAIFA_INLINE static void
-schedule_unit (unit, insn, clock)
- int unit, clock;
- rtx insn;
-{
- int i;
-
- if (unit >= 0)
- {
- int instance = unit;
-#if MAX_MULTIPLICITY > 1
- /* Find the first free instance of the function unit and use that
- one. We assume that one is free. */
- for (i = function_units[unit].multiplicity - 1; i > 0; i--)
- {
- if (!actual_hazard_this_instance (unit, instance, insn, clock, 0))
- break;
- instance += FUNCTION_UNITS_SIZE;
- }
-#endif
- unit_last_insn[instance] = insn;
- unit_tick[instance] = (clock + function_units[unit].max_blockage);
- }
- else
- for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
- if ((unit & 1) != 0)
- schedule_unit (i, insn, clock);
-}
-
-/* Return the actual hazard cost of executing INSN on the units encoded by
- UNIT at time CLOCK if the previous actual hazard cost was COST. */
-
-HAIFA_INLINE static int
-actual_hazard (unit, insn, clock, cost)
- int unit, clock, cost;
- rtx insn;
-{
- int i;
-
- if (unit >= 0)
- {
- /* Find the instance of the function unit with the minimum hazard. */
- int instance = unit;
- int best_cost = actual_hazard_this_instance (unit, instance, insn,
- clock, cost);
- int this_cost;
-
-#if MAX_MULTIPLICITY > 1
- if (best_cost > cost)
- {
- for (i = function_units[unit].multiplicity - 1; i > 0; i--)
- {
- instance += FUNCTION_UNITS_SIZE;
- this_cost = actual_hazard_this_instance (unit, instance, insn,
- clock, cost);
- if (this_cost < best_cost)
- {
- best_cost = this_cost;
- if (this_cost <= cost)
- break;
- }
- }
- }
-#endif
- cost = MAX (cost, best_cost);
- }
- else
- for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
- if ((unit & 1) != 0)
- cost = actual_hazard (i, insn, clock, cost);
-
- return cost;
-}
-
-/* Return the potential hazard cost of executing an instruction on the
- units encoded by UNIT if the previous potential hazard cost was COST.
- An insn with a large blockage time is chosen in preference to one
- with a smaller time; an insn that uses a unit that is more likely
- to be used is chosen in preference to one with a unit that is less
- used. We are trying to minimize a subsequent actual hazard. */
-
-HAIFA_INLINE static int
-potential_hazard (unit, insn, cost)
- int unit, cost;
- rtx insn;
-{
- int i, ncost;
- unsigned int minb, maxb;
-
- if (unit >= 0)
- {
- minb = maxb = function_units[unit].max_blockage;
- if (maxb > 1)
- {
- if (function_units[unit].blockage_range_function)
- {
- maxb = minb = blockage_range (unit, insn);
- maxb = MAX_BLOCKAGE_COST (maxb);
- minb = MIN_BLOCKAGE_COST (minb);
- }
-
- if (maxb > 1)
- {
- /* Make the number of instructions left dominate. Make the
- minimum delay dominate the maximum delay. If all these
- are the same, use the unit number to add an arbitrary
- ordering. Other terms can be added. */
- ncost = minb * 0x40 + maxb;
- ncost *= (unit_n_insns[unit] - 1) * 0x1000 + unit;
- if (ncost > cost)
- cost = ncost;
- }
- }
- }
- else
- for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
- if ((unit & 1) != 0)
- cost = potential_hazard (i, insn, cost);
-
- return cost;
-}
-
-/* Compute cost of executing INSN given the dependence LINK on the insn USED.
- This is the number of cycles between instruction issue and
- instruction results. */
-
-HAIFA_INLINE static int
-insn_cost (insn, link, used)
- rtx insn, link, used;
-{
- register int cost = INSN_COST (insn);
-
- if (cost == 0)
- {
- recog_memoized (insn);
-
- /* A USE insn, or something else we don't need to understand.
- We can't pass these directly to result_ready_cost because it will
- trigger a fatal error for unrecognizable insns. */
- if (INSN_CODE (insn) < 0)
- {
- INSN_COST (insn) = 1;
- return 1;
- }
- else
- {
- cost = result_ready_cost (insn);
-
- if (cost < 1)
- cost = 1;
-
- INSN_COST (insn) = cost;
- }
- }
-
- /* in this case estimate cost without caring how insn is used. */
- if (link == 0 && used == 0)
- return cost;
-
- /* A USE insn should never require the value used to be computed. This
- allows the computation of a function's result and parameter values to
- overlap the return and call. */
- recog_memoized (used);
- if (INSN_CODE (used) < 0)
- LINK_COST_FREE (link) = 1;
-
- /* If some dependencies vary the cost, compute the adjustment. Most
- commonly, the adjustment is complete: either the cost is ignored
- (in the case of an output- or anti-dependence), or the cost is
- unchanged. These values are cached in the link as LINK_COST_FREE
- and LINK_COST_ZERO. */
-
- if (LINK_COST_FREE (link))
- cost = 1;
-#ifdef ADJUST_COST
- else if (!LINK_COST_ZERO (link))
- {
- int ncost = cost;
-
- ADJUST_COST (used, link, insn, ncost);
- if (ncost <= 1)
- LINK_COST_FREE (link) = ncost = 1;
- if (cost == ncost)
- LINK_COST_ZERO (link) = 1;
- cost = ncost;
- }
-#endif
- return cost;
-}
-
-/* Compute the priority number for INSN. */
-
-static int
-priority (insn)
- rtx insn;
-{
- int this_priority;
- rtx link;
-
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- return 0;
-
- if ((this_priority = INSN_PRIORITY (insn)) == 0)
- {
- if (INSN_DEPEND (insn) == 0)
- this_priority = insn_cost (insn, 0, 0);
- else
- for (link = INSN_DEPEND (insn); link; link = XEXP (link, 1))
- {
- rtx next;
- int next_priority;
-
- if (RTX_INTEGRATED_P (link))
- continue;
-
- next = XEXP (link, 0);
-
- /* critical path is meaningful in block boundaries only */
- if (INSN_BLOCK (next) != INSN_BLOCK (insn))
- continue;
-
- next_priority = insn_cost (insn, link, next) + priority (next);
- if (next_priority > this_priority)
- this_priority = next_priority;
- }
- INSN_PRIORITY (insn) = this_priority;
- }
- return this_priority;
-}
-
-
-/* Remove all INSN_LISTs and EXPR_LISTs from the pending lists and add
- them to the unused_*_list variables, so that they can be reused. */
-
-static void
-free_pending_lists ()
-{
- if (current_nr_blocks <= 1)
- {
- free_list (&pending_read_insns, &unused_insn_list);
- free_list (&pending_write_insns, &unused_insn_list);
- free_list (&pending_read_mems, &unused_expr_list);
- free_list (&pending_write_mems, &unused_expr_list);
- }
- else
- {
- /* interblock scheduling */
- int bb;
-
- for (bb = 0; bb < current_nr_blocks; bb++)
- {
- free_list (&bb_pending_read_insns[bb], &unused_insn_list);
- free_list (&bb_pending_write_insns[bb], &unused_insn_list);
- free_list (&bb_pending_read_mems[bb], &unused_expr_list);
- free_list (&bb_pending_write_mems[bb], &unused_expr_list);
- }
- }
-}
-
-/* Add an INSN and MEM reference pair to a pending INSN_LIST and MEM_LIST.
- The MEM is a memory reference contained within INSN, which we are saving
- so that we can do memory aliasing on it. */
-
-static void
-add_insn_mem_dependence (insn_list, mem_list, insn, mem)
- rtx *insn_list, *mem_list, insn, mem;
-{
- register rtx link;
-
- link = alloc_INSN_LIST (insn, *insn_list);
- *insn_list = link;
-
- link = alloc_EXPR_LIST (VOIDmode, mem, *mem_list);
- *mem_list = link;
-
- pending_lists_length++;
-}
-
-
-/* Make a dependency between every memory reference on the pending lists
- and INSN, thus flushing the pending lists. If ONLY_WRITE, don't flush
- the read list. */
-
-static void
-flush_pending_lists (insn, only_write)
- rtx insn;
- int only_write;
-{
- rtx u;
- rtx link;
-
- while (pending_read_insns && ! only_write)
- {
- add_dependence (insn, XEXP (pending_read_insns, 0), REG_DEP_ANTI);
-
- link = pending_read_insns;
- pending_read_insns = XEXP (pending_read_insns, 1);
- XEXP (link, 1) = unused_insn_list;
- unused_insn_list = link;
-
- link = pending_read_mems;
- pending_read_mems = XEXP (pending_read_mems, 1);
- XEXP (link, 1) = unused_expr_list;
- unused_expr_list = link;
- }
- while (pending_write_insns)
- {
- add_dependence (insn, XEXP (pending_write_insns, 0), REG_DEP_ANTI);
-
- link = pending_write_insns;
- pending_write_insns = XEXP (pending_write_insns, 1);
- XEXP (link, 1) = unused_insn_list;
- unused_insn_list = link;
-
- link = pending_write_mems;
- pending_write_mems = XEXP (pending_write_mems, 1);
- XEXP (link, 1) = unused_expr_list;
- unused_expr_list = link;
- }
- pending_lists_length = 0;
-
- /* last_pending_memory_flush is now a list of insns */
- for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
-
- free_list (&last_pending_memory_flush, &unused_insn_list);
- last_pending_memory_flush = alloc_INSN_LIST (insn, NULL_RTX);
-}
-
-/* Analyze a single SET or CLOBBER rtx, X, creating all dependencies generated
- by the write to the destination of X, and reads of everything mentioned. */
-
-static void
-sched_analyze_1 (x, insn)
- rtx x;
- rtx insn;
-{
- register int regno;
- register rtx dest = SET_DEST (x);
-
- if (dest == 0)
- return;
-
- if (GET_CODE (dest) == PARALLEL
- && GET_MODE (dest) == BLKmode)
- {
- register int i;
- for (i = XVECLEN (dest, 0) - 1; i >= 0; i--)
- sched_analyze_1 (XVECEXP (dest, 0, i), insn);
- if (GET_CODE (x) == SET)
- sched_analyze_2 (SET_SRC (x), insn);
- return;
- }
-
- while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
- || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
- {
- if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
- {
- /* The second and third arguments are values read by this insn. */
- sched_analyze_2 (XEXP (dest, 1), insn);
- sched_analyze_2 (XEXP (dest, 2), insn);
- }
- dest = SUBREG_REG (dest);
- }
-
- if (GET_CODE (dest) == REG)
- {
- register int i;
-
- regno = REGNO (dest);
-
- /* A hard reg in a wide mode may really be multiple registers.
- If so, mark all of them just like the first. */
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- i = HARD_REGNO_NREGS (regno, GET_MODE (dest));
- while (--i >= 0)
- {
- rtx u;
-
- for (u = reg_last_uses[regno + i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- reg_last_uses[regno + i] = 0;
-
- for (u = reg_last_sets[regno + i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT);
-
- SET_REGNO_REG_SET (reg_pending_sets, regno + i);
-
- if ((call_used_regs[regno + i] || global_regs[regno + i]))
- /* Function calls clobber all call_used regs. */
- for (u = last_function_call; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- }
- }
- else
- {
- rtx u;
-
- for (u = reg_last_uses[regno]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- reg_last_uses[regno] = 0;
-
- for (u = reg_last_sets[regno]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_OUTPUT);
-
- SET_REGNO_REG_SET (reg_pending_sets, regno);
-
- /* Pseudos that are REG_EQUIV to something may be replaced
- by that during reloading. We need only add dependencies for
- the address in the REG_EQUIV note. */
- if (!reload_completed
- && reg_known_equiv_p[regno]
- && GET_CODE (reg_known_value[regno]) == MEM)
- sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
-
- /* Don't let it cross a call after scheduling if it doesn't
- already cross one. */
-
- if (REG_N_CALLS_CROSSED (regno) == 0)
- for (u = last_function_call; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- }
- }
- else if (GET_CODE (dest) == MEM)
- {
- /* Writing memory. */
-
- if (pending_lists_length > 32)
- {
- /* Flush all pending reads and writes to prevent the pending lists
- from getting any larger. Insn scheduling runs too slowly when
- these lists get long. The number 32 was chosen because it
- seems like a reasonable number. When compiling GCC with itself,
- this flush occurs 8 times for sparc, and 10 times for m88k using
- the number 32. */
- flush_pending_lists (insn, 0);
- }
- else
- {
- rtx u;
- rtx pending, pending_mem;
-
- pending = pending_read_insns;
- pending_mem = pending_read_mems;
- while (pending)
- {
- /* If a dependency already exists, don't create a new one. */
- if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
- if (anti_dependence (XEXP (pending_mem, 0), dest))
- add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
-
- pending = XEXP (pending, 1);
- pending_mem = XEXP (pending_mem, 1);
- }
-
- pending = pending_write_insns;
- pending_mem = pending_write_mems;
- while (pending)
- {
- /* If a dependency already exists, don't create a new one. */
- if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
- if (output_dependence (XEXP (pending_mem, 0), dest))
- add_dependence (insn, XEXP (pending, 0), REG_DEP_OUTPUT);
-
- pending = XEXP (pending, 1);
- pending_mem = XEXP (pending_mem, 1);
- }
-
- for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
-
- add_insn_mem_dependence (&pending_write_insns, &pending_write_mems,
- insn, dest);
- }
- sched_analyze_2 (XEXP (dest, 0), insn);
- }
-
- /* Analyze reads. */
- if (GET_CODE (x) == SET)
- sched_analyze_2 (SET_SRC (x), insn);
-}
-
-/* Analyze the uses of memory and registers in rtx X in INSN. */
-
-static void
-sched_analyze_2 (x, insn)
- rtx x;
- rtx insn;
-{
- register int i;
- register int j;
- register enum rtx_code code;
- register char *fmt;
-
- if (x == 0)
- return;
-
- code = GET_CODE (x);
-
- switch (code)
- {
- case CONST_INT:
- case CONST_DOUBLE:
- case SYMBOL_REF:
- case CONST:
- case LABEL_REF:
- /* Ignore constants. Note that we must handle CONST_DOUBLE here
- because it may have a cc0_rtx in its CONST_DOUBLE_CHAIN field, but
- this does not mean that this insn is using cc0. */
- return;
-
-#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:
- {
- rtx u;
- int regno = REGNO (x);
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- int i;
-
- i = HARD_REGNO_NREGS (regno, GET_MODE (x));
- while (--i >= 0)
- {
- reg_last_uses[regno + i]
- = alloc_INSN_LIST (insn, reg_last_uses[regno + i]);
-
- for (u = reg_last_sets[regno + i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), 0);
-
- if ((call_used_regs[regno + i] || global_regs[regno + i]))
- /* Function calls clobber all call_used regs. */
- for (u = last_function_call; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- }
- }
- else
- {
- reg_last_uses[regno] = alloc_INSN_LIST (insn, reg_last_uses[regno]);
-
- for (u = reg_last_sets[regno]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), 0);
-
- /* Pseudos that are REG_EQUIV to something may be replaced
- by that during reloading. We need only add dependencies for
- the address in the REG_EQUIV note. */
- if (!reload_completed
- && reg_known_equiv_p[regno]
- && GET_CODE (reg_known_value[regno]) == MEM)
- sched_analyze_2 (XEXP (reg_known_value[regno], 0), insn);
-
- /* If the register does not already cross any calls, then add this
- insn to the sched_before_next_call list so that it will still
- not cross calls after scheduling. */
- if (REG_N_CALLS_CROSSED (regno) == 0)
- add_dependence (sched_before_next_call, insn, REG_DEP_ANTI);
- }
- return;
- }
-
- case MEM:
- {
- /* Reading memory. */
- rtx u;
- rtx pending, pending_mem;
-
- pending = pending_read_insns;
- pending_mem = pending_read_mems;
- while (pending)
- {
- /* If a dependency already exists, don't create a new one. */
- if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
- if (read_dependence (XEXP (pending_mem, 0), x))
- add_dependence (insn, XEXP (pending, 0), REG_DEP_ANTI);
-
- pending = XEXP (pending, 1);
- pending_mem = XEXP (pending_mem, 1);
- }
-
- pending = pending_write_insns;
- pending_mem = pending_write_mems;
- while (pending)
- {
- /* If a dependency already exists, don't create a new one. */
- if (!find_insn_list (XEXP (pending, 0), LOG_LINKS (insn)))
- if (true_dependence (XEXP (pending_mem, 0), VOIDmode,
- x, rtx_varies_p))
- add_dependence (insn, XEXP (pending, 0), 0);
-
- pending = XEXP (pending, 1);
- pending_mem = XEXP (pending_mem, 1);
- }
-
- for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
-
- /* Always add these dependencies to pending_reads, since
- this insn may be followed by a write. */
- add_insn_mem_dependence (&pending_read_insns, &pending_read_mems,
- insn, x);
-
- /* Take advantage of tail recursion here. */
- sched_analyze_2 (XEXP (x, 0), insn);
- return;
- }
-
- /* Force pending stores to memory in case a trap handler needs them. */
- case TRAP_IF:
- flush_pending_lists (insn, 1);
- break;
-
- case ASM_OPERANDS:
- case ASM_INPUT:
- case UNSPEC_VOLATILE:
- {
- rtx u;
-
- /* Traditional and volatile asm instructions must be considered to use
- and clobber all hard registers, all pseudo-registers and all of
- memory. So must TRAP_IF and UNSPEC_VOLATILE operations.
-
- Consider for instance a volatile asm that changes the fpu rounding
- mode. An insn should not be moved across this even if it only uses
- pseudo-regs because it might give an incorrectly rounded result. */
- if (code != ASM_OPERANDS || MEM_VOLATILE_P (x))
- {
- int max_reg = max_reg_num ();
- for (i = 0; i < max_reg; i++)
- {
- for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- reg_last_uses[i] = 0;
-
- /* reg_last_sets[r] is now a list of insns */
- for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), 0);
- }
- reg_pending_sets_all = 1;
-
- flush_pending_lists (insn, 0);
- }
-
- /* For all ASM_OPERANDS, we must traverse the vector of input operands.
- We can not just fall through here since then we would be confused
- by the ASM_INPUT rtx inside ASM_OPERANDS, which do not indicate
- traditional asms unlike their normal usage. */
-
- if (code == ASM_OPERANDS)
- {
- for (j = 0; j < ASM_OPERANDS_INPUT_LENGTH (x); j++)
- sched_analyze_2 (ASM_OPERANDS_INPUT (x, j), insn);
- return;
- }
- break;
- }
-
- case PRE_DEC:
- case POST_DEC:
- case PRE_INC:
- case POST_INC:
- /* These both read and modify the result. We must handle them as writes
- to get proper dependencies for following instructions. We must handle
- them as reads to get proper dependencies from this to previous
- instructions. Thus we need to pass them to both sched_analyze_1
- and sched_analyze_2. We must call sched_analyze_2 first in order
- to get the proper antecedent for the read. */
- sched_analyze_2 (XEXP (x, 0), insn);
- sched_analyze_1 (x, insn);
- return;
-
- default:
- break;
- }
-
- /* Other cases: walk the insn. */
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- sched_analyze_2 (XEXP (x, i), insn);
- else if (fmt[i] == 'E')
- for (j = 0; j < XVECLEN (x, i); j++)
- sched_analyze_2 (XVECEXP (x, i, j), insn);
- }
-}
-
-/* Analyze an INSN with pattern X to find all dependencies. */
-
-static void
-sched_analyze_insn (x, insn, loop_notes)
- rtx x, insn;
- rtx loop_notes;
-{
- register RTX_CODE code = GET_CODE (x);
- rtx link;
- int maxreg = max_reg_num ();
- int i;
-
- if (code == SET || code == CLOBBER)
- sched_analyze_1 (x, insn);
- else if (code == PARALLEL)
- {
- register int i;
- for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
- {
- code = GET_CODE (XVECEXP (x, 0, i));
- if (code == SET || code == CLOBBER)
- sched_analyze_1 (XVECEXP (x, 0, i), insn);
- else
- sched_analyze_2 (XVECEXP (x, 0, i), insn);
- }
- }
- else
- sched_analyze_2 (x, insn);
-
- /* Mark registers CLOBBERED or used by called function. */
- if (GET_CODE (insn) == CALL_INSN)
- for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
- {
- if (GET_CODE (XEXP (link, 0)) == CLOBBER)
- sched_analyze_1 (XEXP (link, 0), insn);
- else
- sched_analyze_2 (XEXP (link, 0), insn);
- }
-
- /* 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
- become incorrect. */
-
- if (loop_notes)
- {
- int max_reg = max_reg_num ();
- int schedule_barrier_found = 0;
- rtx link;
-
- /* Update loop_notes with any notes from this insn. Also determine
- if any of the notes on the list correspond to instruction scheduling
- barriers (loop, eh & setjmp notes, but not range notes. */
- link = loop_notes;
- while (XEXP (link, 1))
- {
- if (INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_BEG
- || INTVAL (XEXP (link, 0)) == NOTE_INSN_LOOP_END
- || INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_BEG
- || INTVAL (XEXP (link, 0)) == NOTE_INSN_EH_REGION_END
- || INTVAL (XEXP (link, 0)) == NOTE_INSN_SETJMP)
- schedule_barrier_found = 1;
-
- link = XEXP (link, 1);
- }
- XEXP (link, 1) = REG_NOTES (insn);
- REG_NOTES (insn) = loop_notes;
-
- /* Add dependencies if a scheduling barrier was found. */
- if (schedule_barrier_found)
- {
- for (i = 0; i < max_reg; i++)
- {
- rtx u;
- for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- reg_last_uses[i] = 0;
-
- /* reg_last_sets[r] is now a list of insns */
- for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), 0);
- }
- reg_pending_sets_all = 1;
-
- flush_pending_lists (insn, 0);
- }
-
- }
-
- EXECUTE_IF_SET_IN_REG_SET (reg_pending_sets, 0, i,
- {
- /* reg_last_sets[r] is now a list of insns */
- free_list (&reg_last_sets[i], &unused_insn_list);
- reg_last_sets[i]
- = alloc_INSN_LIST (insn, NULL_RTX);
- });
- CLEAR_REG_SET (reg_pending_sets);
-
- if (reg_pending_sets_all)
- {
- for (i = 0; i < maxreg; i++)
- {
- /* reg_last_sets[r] is now a list of insns */
- free_list (&reg_last_sets[i], &unused_insn_list);
- reg_last_sets[i] = alloc_INSN_LIST (insn, NULL_RTX);
- }
-
- 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);
- }
- }
-}
-
-/* Analyze every insn between HEAD and TAIL inclusive, creating LOG_LINKS
- for every dependency. */
-
-static void
-sched_analyze (head, tail)
- rtx head, tail;
-{
- register rtx insn;
- register rtx u;
- rtx loop_notes = 0;
-
- for (insn = head;; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
- {
- /* Make each JUMP_INSN a scheduling barrier for memory references. */
- if (GET_CODE (insn) == JUMP_INSN)
- last_pending_memory_flush
- = alloc_INSN_LIST (insn, last_pending_memory_flush);
- sched_analyze_insn (PATTERN (insn), insn, loop_notes);
- loop_notes = 0;
- }
- else if (GET_CODE (insn) == CALL_INSN)
- {
- rtx x;
- register int i;
-
- CANT_MOVE (insn) = 1;
-
- /* Any instruction using a hard register which may get clobbered
- by a call needs to be marked as dependent on this call.
- This prevents a use of a hard return reg from being moved
- past a void call (i.e. it does not explicitly set the hard
- return reg). */
-
- /* If this call is followed by a NOTE_INSN_SETJMP, then assume that
- all registers, not just hard registers, may be clobbered by this
- call. */
-
- /* Insn, being a CALL_INSN, magically depends on
- `last_function_call' already. */
-
- if (NEXT_INSN (insn) && GET_CODE (NEXT_INSN (insn)) == NOTE
- && NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP)
- {
- int max_reg = max_reg_num ();
- for (i = 0; i < max_reg; i++)
- {
- for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
-
- reg_last_uses[i] = 0;
-
- /* reg_last_sets[r] is now a list of insns */
- for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), 0);
- }
- reg_pending_sets_all = 1;
-
- /* Add a pair of fake REG_NOTE which we will later
- convert back into a NOTE_INSN_SETJMP note. See
- reemit_notes for why we use a pair of NOTEs. */
- REG_NOTES (insn) = alloc_EXPR_LIST (REG_DEAD,
- GEN_INT (0),
- REG_NOTES (insn));
- REG_NOTES (insn) = alloc_EXPR_LIST (REG_DEAD,
- GEN_INT (NOTE_INSN_SETJMP),
- REG_NOTES (insn));
- }
- else
- {
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (call_used_regs[i] || global_regs[i])
- {
- for (u = reg_last_uses[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
- reg_last_uses[i] = 0;
-
- /* reg_last_sets[r] is now a list of insns */
- for (u = reg_last_sets[i]; u; u = XEXP (u, 1))
- add_dependence (insn, XEXP (u, 0), REG_DEP_ANTI);
-
- SET_REGNO_REG_SET (reg_pending_sets, i);
- }
- }
-
- /* For each insn which shouldn't cross a call, add a dependence
- between that insn and this call insn. */
- x = LOG_LINKS (sched_before_next_call);
- while (x)
- {
- add_dependence (insn, XEXP (x, 0), REG_DEP_ANTI);
- x = XEXP (x, 1);
- }
- LOG_LINKS (sched_before_next_call) = 0;
-
- sched_analyze_insn (PATTERN (insn), insn, loop_notes);
- loop_notes = 0;
-
- /* In the absence of interprocedural alias analysis, we must flush
- all pending reads and writes, and start new dependencies starting
- from here. But only flush writes for constant calls (which may
- be passed a pointer to something we haven't written yet). */
- flush_pending_lists (insn, CONST_CALL_P (insn));
-
- /* Depend this function call (actually, the user of this
- function call) on all hard register clobberage. */
-
- /* last_function_call is now a list of insns */
- free_list(&last_function_call, &unused_insn_list);
- last_function_call = alloc_INSN_LIST (insn, NULL_RTX);
- }
-
- /* See comments on reemit_notes as to why we do this. */
- /* ??? Actually, the reemit_notes just say what is done, not why. */
-
- else if (GET_CODE (insn) == NOTE
- && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_START
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_RANGE_END))
- {
- loop_notes = alloc_EXPR_LIST (REG_DEAD, NOTE_RANGE_INFO (insn),
- loop_notes);
- loop_notes = alloc_EXPR_LIST (REG_DEAD,
- GEN_INT (NOTE_LINE_NUMBER (insn)),
- loop_notes);
- }
- else if (GET_CODE (insn) == NOTE
- && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
- || NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END
- || (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP
- && GET_CODE (PREV_INSN (insn)) != CALL_INSN)))
- {
- loop_notes = alloc_EXPR_LIST (REG_DEAD,
- GEN_INT (NOTE_BLOCK_NUMBER (insn)),
- loop_notes);
- loop_notes = alloc_EXPR_LIST (REG_DEAD,
- GEN_INT (NOTE_LINE_NUMBER (insn)),
- loop_notes);
- CONST_CALL_P (loop_notes) = CONST_CALL_P (insn);
- }
-
- if (insn == tail)
- return;
- }
- abort ();
-}
-
-/* Called when we see a set of a register. If death is true, then we are
- scanning backwards. Mark that register as unborn. If nobody says
- otherwise, that is how things will remain. If death is false, then we
- are scanning forwards. Mark that register as being born. */
-
-static void
-sched_note_set (x, death)
- rtx x;
- int death;
-{
- register int regno;
- register rtx reg = SET_DEST (x);
- int subreg_p = 0;
-
- if (reg == 0)
- return;
-
- if (GET_CODE (reg) == PARALLEL
- && GET_MODE (reg) == BLKmode)
- {
- register int i;
- for (i = XVECLEN (reg, 0) - 1; i >= 0; i--)
- sched_note_set (XVECEXP (reg, 0, i), death);
- return;
- }
-
- while (GET_CODE (reg) == SUBREG || GET_CODE (reg) == STRICT_LOW_PART
- || GET_CODE (reg) == SIGN_EXTRACT || GET_CODE (reg) == ZERO_EXTRACT)
- {
- /* Must treat modification of just one hardware register of a multi-reg
- value or just a byte field of a register exactly the same way that
- mark_set_1 in flow.c does, i.e. anything except a paradoxical subreg
- does not kill the entire register. */
- if (GET_CODE (reg) != SUBREG
- || REG_SIZE (SUBREG_REG (reg)) > REG_SIZE (reg))
- subreg_p = 1;
-
- reg = SUBREG_REG (reg);
- }
-
- if (GET_CODE (reg) != REG)
- return;
-
- /* Global registers are always live, so the code below does not apply
- to them. */
-
- regno = REGNO (reg);
- if (regno >= FIRST_PSEUDO_REGISTER || !global_regs[regno])
- {
- if (death)
- {
- /* If we only set part of the register, then this set does not
- kill it. */
- if (subreg_p)
- return;
-
- /* Try killing this register. */
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
- while (--j >= 0)
- {
- CLEAR_REGNO_REG_SET (bb_live_regs, regno + j);
- }
- }
- else
- {
- /* Recompute REG_BASIC_BLOCK as we update all the other
- dataflow information. */
- if (sched_reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
- sched_reg_basic_block[regno] = current_block_num;
- else if (sched_reg_basic_block[regno] != current_block_num)
- sched_reg_basic_block[regno] = REG_BLOCK_GLOBAL;
-
- CLEAR_REGNO_REG_SET (bb_live_regs, regno);
- }
- }
- else
- {
- /* Make the register live again. */
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- int j = HARD_REGNO_NREGS (regno, GET_MODE (reg));
- while (--j >= 0)
- {
- SET_REGNO_REG_SET (bb_live_regs, regno + j);
- }
- }
- else
- {
- SET_REGNO_REG_SET (bb_live_regs, regno);
- }
- }
- }
-}
-
-/* Macros and functions for keeping the priority queue sorted, and
- dealing with queueing and dequeueing of instructions. */
-
-#define SCHED_SORT(READY, N_READY) \
-do { if ((N_READY) == 2) \
- swap_sort (READY, N_READY); \
- else if ((N_READY) > 2) \
- qsort (READY, N_READY, sizeof (rtx), rank_for_schedule); } \
-while (0)
-
-/* Returns a positive value if x is preferred; returns a negative value if
- y is preferred. Should never return 0, since that will make the sort
- unstable. */
-
-static int
-rank_for_schedule (x, y)
- const GENERIC_PTR x;
- const GENERIC_PTR y;
-{
- rtx tmp = *(rtx *)y;
- rtx tmp2 = *(rtx *)x;
- rtx link;
- int tmp_class, tmp2_class, depend_count1, depend_count2;
- int val, priority_val, spec_val, prob_val, weight_val;
-
-
- /* prefer insn with higher priority */
- priority_val = INSN_PRIORITY (tmp2) - INSN_PRIORITY (tmp);
- if (priority_val)
- return priority_val;
-
- /* prefer an insn with smaller contribution to registers-pressure */
- if (!reload_completed &&
- (weight_val = INSN_REG_WEIGHT (tmp) - INSN_REG_WEIGHT (tmp2)))
- return (weight_val);
-
- /* some comparison make sense in interblock scheduling only */
- if (INSN_BB (tmp) != INSN_BB (tmp2))
- {
- /* prefer an inblock motion on an interblock motion */
- if ((INSN_BB (tmp2) == target_bb) && (INSN_BB (tmp) != target_bb))
- return 1;
- if ((INSN_BB (tmp) == target_bb) && (INSN_BB (tmp2) != target_bb))
- return -1;
-
- /* prefer a useful motion on a speculative one */
- if ((spec_val = IS_SPECULATIVE_INSN (tmp) - IS_SPECULATIVE_INSN (tmp2)))
- return (spec_val);
-
- /* prefer a more probable (speculative) insn */
- prob_val = INSN_PROBABILITY (tmp2) - INSN_PROBABILITY (tmp);
- if (prob_val)
- return (prob_val);
- }
-
- /* compare insns based on their relation to the last-scheduled-insn */
- if (last_scheduled_insn)
- {
- /* Classify the instructions into three classes:
- 1) Data dependent on last schedule insn.
- 2) Anti/Output dependent on last scheduled insn.
- 3) Independent of last scheduled insn, or has latency of one.
- Choose the insn from the highest numbered class if different. */
- link = find_insn_list (tmp, INSN_DEPEND (last_scheduled_insn));
- if (link == 0 || insn_cost (last_scheduled_insn, link, tmp) == 1)
- tmp_class = 3;
- else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
- tmp_class = 1;
- else
- tmp_class = 2;
-
- link = find_insn_list (tmp2, INSN_DEPEND (last_scheduled_insn));
- if (link == 0 || insn_cost (last_scheduled_insn, link, tmp2) == 1)
- tmp2_class = 3;
- else if (REG_NOTE_KIND (link) == 0) /* Data dependence. */
- tmp2_class = 1;
- else
- tmp2_class = 2;
-
- if ((val = tmp2_class - tmp_class))
- return val;
- }
-
- /* Prefer the insn which has more later insns that depend on it.
- This gives the scheduler more freedom when scheduling later
- instructions at the expense of added register pressure. */
- depend_count1 = 0;
- for (link = INSN_DEPEND (tmp); link; link = XEXP (link, 1))
- depend_count1++;
-
- depend_count2 = 0;
- for (link = INSN_DEPEND (tmp2); link; link = XEXP (link, 1))
- depend_count2++;
-
- val = depend_count2 - depend_count1;
- if (val)
- return val;
-
- /* If insns are equally good, sort by INSN_LUID (original insn order),
- so that we make the sort stable. This minimizes instruction movement,
- thus minimizing sched's effect on debugging and cross-jumping. */
- return INSN_LUID (tmp) - INSN_LUID (tmp2);
-}
-
-/* Resort the array A in which only element at index N may be out of order. */
-
-HAIFA_INLINE static void
-swap_sort (a, n)
- rtx *a;
- int n;
-{
- rtx insn = a[n - 1];
- int i = n - 2;
-
- while (i >= 0 && rank_for_schedule (a + i, &insn) >= 0)
- {
- a[i + 1] = a[i];
- i -= 1;
- }
- a[i + 1] = insn;
-}
-
-static int max_priority;
-
-/* Add INSN to the insn queue so that it can be executed at least
- N_CYCLES after the currently executing insn. Preserve insns
- chain for debugging purposes. */
-
-HAIFA_INLINE static void
-queue_insn (insn, n_cycles)
- rtx insn;
- int n_cycles;
-{
- int next_q = NEXT_Q_AFTER (q_ptr, n_cycles);
- rtx link = alloc_INSN_LIST (insn, insn_queue[next_q]);
- insn_queue[next_q] = link;
- q_size += 1;
-
- if (sched_verbose >= 2)
- {
- fprintf (dump, ";;\t\tReady-->Q: insn %d: ", INSN_UID (insn));
-
- if (INSN_BB (insn) != target_bb)
- fprintf (dump, "(b%d) ", INSN_BLOCK (insn));
-
- fprintf (dump, "queued for %d cycles.\n", n_cycles);
- }
-
-}
-
-/* Return nonzero if PAT is the pattern of an insn which makes a
- register live. */
-
-HAIFA_INLINE static int
-birthing_insn_p (pat)
- rtx pat;
-{
- int j;
-
- if (reload_completed == 1)
- return 0;
-
- if (GET_CODE (pat) == SET
- && (GET_CODE (SET_DEST (pat)) == REG
- || (GET_CODE (SET_DEST (pat)) == PARALLEL
- && GET_MODE (SET_DEST (pat)) == BLKmode)))
- {
- rtx dest = SET_DEST (pat);
- int i;
-
- /* It would be more accurate to use refers_to_regno_p or
- reg_mentioned_p to determine when the dest is not live before this
- insn. */
- if (GET_CODE (dest) == REG)
- {
- i = REGNO (dest);
- if (REGNO_REG_SET_P (bb_live_regs, i))
- return (REG_N_SETS (i) == 1);
- }
- else
- {
- for (i = XVECLEN (dest, 0) - 1; i >= 0; i--)
- {
- int regno = REGNO (SET_DEST (XVECEXP (dest, 0, i)));
- if (REGNO_REG_SET_P (bb_live_regs, regno))
- return (REG_N_SETS (regno) == 1);
- }
- }
- return 0;
- }
- if (GET_CODE (pat) == PARALLEL)
- {
- for (j = 0; j < XVECLEN (pat, 0); j++)
- if (birthing_insn_p (XVECEXP (pat, 0, j)))
- return 1;
- }
- return 0;
-}
-
-/* PREV is an insn that is ready to execute. Adjust its priority if that
- will help shorten register lifetimes. */
-
-HAIFA_INLINE static void
-adjust_priority (prev)
- rtx prev;
-{
- /* Trying to shorten register lives after reload has completed
- is useless and wrong. It gives inaccurate schedules. */
- if (reload_completed == 0)
- {
- rtx note;
- int n_deaths = 0;
-
- /* ??? This code has no effect, because REG_DEAD notes are removed
- before we ever get here. */
- for (note = REG_NOTES (prev); note; note = XEXP (note, 1))
- if (REG_NOTE_KIND (note) == REG_DEAD)
- n_deaths += 1;
-
- /* Defer scheduling insns which kill registers, since that
- shortens register lives. Prefer scheduling insns which
- make registers live for the same reason. */
- switch (n_deaths)
- {
- default:
- INSN_PRIORITY (prev) >>= 3;
- break;
- case 3:
- INSN_PRIORITY (prev) >>= 2;
- break;
- case 2:
- case 1:
- INSN_PRIORITY (prev) >>= 1;
- break;
- case 0:
- if (birthing_insn_p (PATTERN (prev)))
- {
- int max = max_priority;
-
- if (max > INSN_PRIORITY (prev))
- INSN_PRIORITY (prev) = max;
- }
- break;
- }
-#ifdef ADJUST_PRIORITY
- ADJUST_PRIORITY (prev);
-#endif
- }
-}
-
-/* Clock at which the previous instruction was issued. */
-static int last_clock_var;
-
-/* INSN is the "currently executing insn". Launch each insn which was
- waiting on INSN. READY is a vector of insns which are ready to fire.
- N_READY is the number of elements in READY. CLOCK is the current
- cycle. */
-
-static int
-schedule_insn (insn, ready, n_ready, clock)
- rtx insn;
- rtx *ready;
- int n_ready;
- int clock;
-{
- rtx link;
- int unit;
-
- unit = insn_unit (insn);
-
- if (sched_verbose >= 2)
- {
- fprintf (dump, ";;\t\t--> scheduling insn <<<%d>>> on unit ", INSN_UID (insn));
- insn_print_units (insn);
- fprintf (dump, "\n");
- }
-
- if (sched_verbose && unit == -1)
- visualize_no_unit (insn);
-
- if (MAX_BLOCKAGE > 1 || issue_rate > 1 || sched_verbose)
- schedule_unit (unit, insn, clock);
-
- if (INSN_DEPEND (insn) == 0)
- return n_ready;
-
- /* This is used by the function adjust_priority above. */
- if (n_ready > 0)
- max_priority = MAX (INSN_PRIORITY (ready[0]), INSN_PRIORITY (insn));
- else
- max_priority = INSN_PRIORITY (insn);
-
- for (link = INSN_DEPEND (insn); link != 0; link = XEXP (link, 1))
- {
- rtx next = XEXP (link, 0);
- int cost = insn_cost (insn, link, next);
-
- INSN_TICK (next) = MAX (INSN_TICK (next), clock + cost);
-
- if ((INSN_DEP_COUNT (next) -= 1) == 0)
- {
- int effective_cost = INSN_TICK (next) - clock;
-
- /* For speculative insns, before inserting to ready/queue,
- check live, exception-free, and issue-delay */
- if (INSN_BB (next) != target_bb
- && (!IS_VALID (INSN_BB (next))
- || CANT_MOVE (next)
- || (IS_SPECULATIVE_INSN (next)
- && (insn_issue_delay (next) > 3
- || !check_live (next, INSN_BB (next))
- || !is_exception_free (next, INSN_BB (next), target_bb)))))
- continue;
-
- if (sched_verbose >= 2)
- {
- fprintf (dump, ";;\t\tdependences resolved: insn %d ", INSN_UID (next));
-
- if (current_nr_blocks > 1 && INSN_BB (next) != target_bb)
- fprintf (dump, "/b%d ", INSN_BLOCK (next));
-
- if (effective_cost <= 1)
- fprintf (dump, "into ready\n");
- else
- fprintf (dump, "into queue with cost=%d\n", effective_cost);
- }
-
- /* Adjust the priority of NEXT and either put it on the ready
- list or queue it. */
- adjust_priority (next);
- if (effective_cost <= 1)
- ready[n_ready++] = next;
- else
- queue_insn (next, effective_cost);
- }
- }
-
- /* Annotate the instruction with issue information -- TImode
- indicates that the instruction is expected not to be able
- to issue on the same cycle as the previous insn. A machine
- may use this information to decide how the instruction should
- be aligned. */
- if (reload_completed && issue_rate > 1)
- {
- PUT_MODE (insn, clock > last_clock_var ? TImode : VOIDmode);
- last_clock_var = clock;
- }
-
- return n_ready;
-}
-
-
-/* Add a REG_DEAD note for REG to INSN, reusing a REG_DEAD note from the
- dead_notes list. */
-
-static void
-create_reg_dead_note (reg, insn)
- rtx reg, insn;
-{
- rtx link;
-
- /* The number of registers killed after scheduling must be the same as the
- number of registers killed before scheduling. The number of REG_DEAD
- notes may not be conserved, i.e. two SImode hard register REG_DEAD notes
- might become one DImode hard register REG_DEAD note, but the number of
- registers killed will be conserved.
-
- We carefully remove REG_DEAD notes from the dead_notes list, so that
- there will be none left at the end. If we run out early, then there
- is a bug somewhere in flow, combine and/or sched. */
-
- if (dead_notes == 0)
- {
- if (current_nr_blocks <= 1)
- abort ();
- else
- link = alloc_EXPR_LIST (REG_DEAD, NULL_RTX, NULL_RTX);
- }
- else
- {
- /* Number of regs killed by REG. */
- int regs_killed = (REGNO (reg) >= FIRST_PSEUDO_REGISTER ? 1
- : HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)));
- /* Number of regs killed by REG_DEAD notes taken off the list. */
- int reg_note_regs;
-
- link = dead_notes;
- reg_note_regs = (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
- : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
- GET_MODE (XEXP (link, 0))));
- while (reg_note_regs < regs_killed)
- {
- link = XEXP (link, 1);
-
- /* LINK might be zero if we killed more registers after scheduling
- than before, and the last hard register we kill is actually
- multiple hard regs.
-
- This is normal for interblock scheduling, so deal with it in
- that case, else abort. */
- if (link == NULL_RTX && current_nr_blocks <= 1)
- abort ();
- else if (link == NULL_RTX)
- link = alloc_EXPR_LIST (REG_DEAD, gen_rtx_REG (word_mode, 0),
- NULL_RTX);
-
- reg_note_regs += (REGNO (XEXP (link, 0)) >= FIRST_PSEUDO_REGISTER ? 1
- : HARD_REGNO_NREGS (REGNO (XEXP (link, 0)),
- GET_MODE (XEXP (link, 0))));
- }
- dead_notes = XEXP (link, 1);
-
- /* If we took too many regs kills off, put the extra ones back. */
- while (reg_note_regs > regs_killed)
- {
- rtx temp_reg, temp_link;
-
- temp_reg = gen_rtx_REG (word_mode, 0);
- temp_link = alloc_EXPR_LIST (REG_DEAD, temp_reg, dead_notes);
- dead_notes = temp_link;
- reg_note_regs--;
- }
- }
-
- XEXP (link, 0) = reg;
- XEXP (link, 1) = REG_NOTES (insn);
- REG_NOTES (insn) = link;
-}
-
-/* Subroutine on attach_deaths_insn--handles the recursive search
- through INSN. If SET_P is true, then x is being modified by the insn. */
-
-static void
-attach_deaths (x, insn, set_p)
- rtx x;
- rtx insn;
- int set_p;
-{
- register int i;
- register int j;
- register enum rtx_code code;
- register char *fmt;
-
- if (x == 0)
- return;
-
- code = GET_CODE (x);
-
- switch (code)
- {
- case CONST_INT:
- case CONST_DOUBLE:
- case LABEL_REF:
- case SYMBOL_REF:
- case CONST:
- case CODE_LABEL:
- case PC:
- case CC0:
- /* Get rid of the easy cases first. */
- return;
-
- case REG:
- {
- /* If the register dies in this insn, queue that note, and mark
- this register as needing to die. */
- /* This code is very similar to mark_used_1 (if set_p is false)
- and mark_set_1 (if set_p is true) in flow.c. */
-
- register int regno;
- int some_needed;
- int all_needed;
-
- if (set_p)
- return;
-
- regno = REGNO (x);
- all_needed = some_needed = REGNO_REG_SET_P (old_live_regs, regno);
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- int n;
-
- n = HARD_REGNO_NREGS (regno, GET_MODE (x));
- while (--n > 0)
- {
- int needed = (REGNO_REG_SET_P (old_live_regs, regno + n));
- some_needed |= needed;
- all_needed &= needed;
- }
- }
-
- /* If it wasn't live before we started, then add a REG_DEAD note.
- We must check the previous lifetime info not the current info,
- because we may have to execute this code several times, e.g.
- once for a clobber (which doesn't add a note) and later
- for a use (which does add a note).
-
- Always make the register live. We must do this even if it was
- live before, because this may be an insn which sets and uses
- the same register, in which case the register has already been
- killed, so we must make it live again.
-
- Global registers are always live, and should never have a REG_DEAD
- note added for them, so none of the code below applies to them. */
-
- if (regno >= FIRST_PSEUDO_REGISTER || ! global_regs[regno])
- {
- /* Never add REG_DEAD notes for the FRAME_POINTER_REGNUM or the
- STACK_POINTER_REGNUM, since these are always considered to be
- live. Similarly for ARG_POINTER_REGNUM if it is fixed. */
- if (regno != FRAME_POINTER_REGNUM
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && ! (regno == HARD_FRAME_POINTER_REGNUM)
-#endif
-#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
- && ! (regno == ARG_POINTER_REGNUM && fixed_regs[regno])
-#endif
- && regno != STACK_POINTER_REGNUM)
- {
- if (! all_needed && ! dead_or_set_p (insn, x))
- {
- /* Check for the case where the register dying partially
- overlaps the register set by this insn. */
- if (regno < FIRST_PSEUDO_REGISTER
- && HARD_REGNO_NREGS (regno, GET_MODE (x)) > 1)
- {
- int n = HARD_REGNO_NREGS (regno, GET_MODE (x));
- while (--n >= 0)
- some_needed |= dead_or_set_regno_p (insn, regno + n);
- }
-
- /* If none of the words in X is needed, make a REG_DEAD
- note. Otherwise, we must make partial REG_DEAD
- notes. */
- if (! some_needed)
- create_reg_dead_note (x, insn);
- else
- {
- int i;
-
- /* Don't make a REG_DEAD note for a part of a
- register that is set in the insn. */
- for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1;
- i >= 0; i--)
- if (! REGNO_REG_SET_P (old_live_regs, regno+i)
- && ! dead_or_set_regno_p (insn, regno + i))
- create_reg_dead_note (gen_rtx_REG (reg_raw_mode[regno + i],
- regno + i),
- insn);
- }
- }
- }
-
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- int j = HARD_REGNO_NREGS (regno, GET_MODE (x));
- while (--j >= 0)
- {
- SET_REGNO_REG_SET (bb_live_regs, regno + j);
- }
- }
- else
- {
- /* Recompute REG_BASIC_BLOCK as we update all the other
- dataflow information. */
- if (sched_reg_basic_block[regno] == REG_BLOCK_UNKNOWN)
- sched_reg_basic_block[regno] = current_block_num;
- else if (sched_reg_basic_block[regno] != current_block_num)
- sched_reg_basic_block[regno] = REG_BLOCK_GLOBAL;
-
- SET_REGNO_REG_SET (bb_live_regs, regno);
- }
- }
- return;
- }
-
- case MEM:
- /* Handle tail-recursive case. */
- attach_deaths (XEXP (x, 0), insn, 0);
- return;
-
- case SUBREG:
- attach_deaths (SUBREG_REG (x), insn,
- set_p && ((GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
- <= UNITS_PER_WORD)
- || (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
- == GET_MODE_SIZE (GET_MODE ((x))))));
- return;
-
- case STRICT_LOW_PART:
- attach_deaths (XEXP (x, 0), insn, 0);
- return;
-
- case ZERO_EXTRACT:
- case SIGN_EXTRACT:
- attach_deaths (XEXP (x, 0), insn, 0);
- attach_deaths (XEXP (x, 1), insn, 0);
- attach_deaths (XEXP (x, 2), insn, 0);
- return;
-
- case PARALLEL:
- if (set_p
- && GET_MODE (x) == BLKmode)
- {
- for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
- attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
- return;
- }
-
- /* fallthrough */
- default:
- /* Other cases: walk the insn. */
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- attach_deaths (XEXP (x, i), insn, 0);
- else if (fmt[i] == 'E')
- for (j = 0; j < XVECLEN (x, i); j++)
- attach_deaths (XVECEXP (x, i, j), insn, 0);
- }
- }
-}
-
-/* After INSN has executed, add register death notes for each register
- that is dead after INSN. */
-
-static void
-attach_deaths_insn (insn)
- rtx insn;
-{
- rtx x = PATTERN (insn);
- register RTX_CODE code = GET_CODE (x);
- rtx link;
-
- if (code == SET)
- {
- attach_deaths (SET_SRC (x), insn, 0);
-
- /* A register might die here even if it is the destination, e.g.
- it is the target of a volatile read and is otherwise unused.
- Hence we must always call attach_deaths for the SET_DEST. */
- attach_deaths (SET_DEST (x), insn, 1);
- }
- else if (code == PARALLEL)
- {
- register int i;
- for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
- {
- code = GET_CODE (XVECEXP (x, 0, i));
- if (code == SET)
- {
- attach_deaths (SET_SRC (XVECEXP (x, 0, i)), insn, 0);
-
- attach_deaths (SET_DEST (XVECEXP (x, 0, i)), insn, 1);
- }
- /* Flow does not add REG_DEAD notes to registers that die in
- clobbers, so we can't either. */
- else if (code != CLOBBER)
- attach_deaths (XVECEXP (x, 0, i), insn, 0);
- }
- }
- /* If this is a CLOBBER, only add REG_DEAD notes to registers inside a
- MEM being clobbered, just like flow. */
- else if (code == CLOBBER && GET_CODE (XEXP (x, 0)) == MEM)
- attach_deaths (XEXP (XEXP (x, 0), 0), insn, 0);
- /* Otherwise don't add a death note to things being clobbered. */
- else if (code != CLOBBER)
- attach_deaths (x, insn, 0);
-
- /* Make death notes for things used in the called function. */
- if (GET_CODE (insn) == CALL_INSN)
- for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
- attach_deaths (XEXP (XEXP (link, 0), 0), insn,
- GET_CODE (XEXP (link, 0)) == CLOBBER);
-}
-
-/* functions for handlnig of notes */
-
-/* Delete notes beginning with INSN and put them in the chain
- of notes ended by NOTE_LIST.
- Returns the insn following the notes. */
-
-static rtx
-unlink_other_notes (insn, tail)
- rtx insn, tail;
-{
- rtx prev = PREV_INSN (insn);
-
- while (insn != tail && GET_CODE (insn) == NOTE)
- {
- rtx next = NEXT_INSN (insn);
- /* Delete the note from its current position. */
- if (prev)
- NEXT_INSN (prev) = next;
- if (next)
- PREV_INSN (next) = prev;
-
- /* Don't save away NOTE_INSN_SETJMPs, because they must remain
- immediately after the call they follow. We use a fake
- (REG_DEAD (const_int -1)) note to remember them.
- Likewise with NOTE_INSN_{LOOP,EHREGION}_{BEG, END}. */
- if (NOTE_LINE_NUMBER (insn) != NOTE_INSN_SETJMP
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_END
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_START
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_RANGE_END
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG
- && NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_END)
- {
- /* Insert the note at the end of the notes list. */
- PREV_INSN (insn) = note_list;
- if (note_list)
- NEXT_INSN (note_list) = insn;
- note_list = insn;
- }
-
- insn = next;
- }
- return insn;
-}
-
-/* Delete line notes beginning with INSN. Record line-number notes so
- they can be reused. Returns the insn following the notes. */
-
-static rtx
-unlink_line_notes (insn, tail)
- rtx insn, tail;
-{
- rtx prev = PREV_INSN (insn);
-
- while (insn != tail && GET_CODE (insn) == NOTE)
- {
- rtx next = NEXT_INSN (insn);
-
- if (write_symbols != NO_DEBUG && NOTE_LINE_NUMBER (insn) > 0)
- {
- /* Delete the note from its current position. */
- if (prev)
- NEXT_INSN (prev) = next;
- if (next)
- PREV_INSN (next) = prev;
-
- /* Record line-number notes so they can be reused. */
- LINE_NOTE (insn) = insn;
- }
- else
- prev = insn;
-
- insn = next;
- }
- return insn;
-}
-
-/* Return the head and tail pointers of BB. */
-
-HAIFA_INLINE static void
-get_block_head_tail (bb, headp, tailp)
- int bb;
- rtx *headp;
- rtx *tailp;
-{
-
- rtx head;
- rtx tail;
- int b;
-
- b = BB_TO_BLOCK (bb);
-
- /* HEAD and TAIL delimit the basic block being scheduled. */
- head = BLOCK_HEAD (b);
- tail = BLOCK_END (b);
-
- /* Don't include any notes or labels at the beginning of the
- basic block, or notes at the ends of basic blocks. */
- 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;
- }
-
- *headp = head;
- *tailp = tail;
-}
-
-/* Delete line notes from bb. Save them so they can be later restored
- (in restore_line_notes ()). */
-
-static void
-rm_line_notes (bb)
- int bb;
-{
- rtx next_tail;
- rtx tail;
- rtx head;
- rtx insn;
-
- get_block_head_tail (bb, &head, &tail);
-
- if (head == tail
- && (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
- return;
-
- next_tail = NEXT_INSN (tail);
- for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
- {
- rtx prev;
-
- /* Farm out notes, and maybe save them in NOTE_LIST.
- This is needed to keep the debugger from
- getting completely deranged. */
- if (GET_CODE (insn) == NOTE)
- {
- prev = insn;
- insn = unlink_line_notes (insn, next_tail);
-
- if (prev == tail)
- abort ();
- if (prev == head)
- abort ();
- if (insn == next_tail)
- abort ();
- }
- }
-}
-
-/* Save line number notes for each insn in bb. */
-
-static void
-save_line_notes (bb)
- int bb;
-{
- rtx head, tail;
- rtx next_tail;
-
- /* We must use the true line number for the first insn in the block
- that was computed and saved at the start of this pass. We can't
- use the current line number, because scheduling of the previous
- block may have changed the current line number. */
-
- rtx line = line_note_head[BB_TO_BLOCK (bb)];
- rtx insn;
-
- get_block_head_tail (bb, &head, &tail);
- next_tail = NEXT_INSN (tail);
-
- for (insn = BLOCK_HEAD (BB_TO_BLOCK (bb));
- insn != next_tail;
- insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
- line = insn;
- else
- LINE_NOTE (insn) = line;
-}
-
-
-/* After bb was scheduled, insert line notes into the insns list. */
-
-static void
-restore_line_notes (bb)
- int bb;
-{
- rtx line, note, prev, new;
- int added_notes = 0;
- int b;
- rtx head, next_tail, insn;
-
- b = BB_TO_BLOCK (bb);
-
- head = BLOCK_HEAD (b);
- next_tail = NEXT_INSN (BLOCK_END (b));
-
- /* Determine the current line-number. We want to know the current
- line number of the first insn of the block here, in case it is
- different from the true line number that was saved earlier. If
- different, then we need a line number note before the first insn
- of this block. If it happens to be the same, then we don't want to
- emit another line number note here. */
- for (line = head; line; line = PREV_INSN (line))
- if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
- break;
-
- /* Walk the insns keeping track of the current line-number and inserting
- the line-number notes as needed. */
- for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
- line = insn;
- /* This used to emit line number notes before every non-deleted note.
- However, this confuses a debugger, because line notes not separated
- by real instructions all end up at the same address. I can find no
- use for line number notes before other notes, so none are emitted. */
- else if (GET_CODE (insn) != NOTE
- && (note = LINE_NOTE (insn)) != 0
- && note != line
- && (line == 0
- || NOTE_LINE_NUMBER (note) != NOTE_LINE_NUMBER (line)
- || NOTE_SOURCE_FILE (note) != NOTE_SOURCE_FILE (line)))
- {
- line = note;
- prev = PREV_INSN (insn);
- if (LINE_NOTE (note))
- {
- /* Re-use the original line-number note. */
- LINE_NOTE (note) = 0;
- PREV_INSN (note) = prev;
- NEXT_INSN (prev) = note;
- PREV_INSN (insn) = note;
- NEXT_INSN (note) = insn;
- }
- else
- {
- added_notes++;
- new = emit_note_after (NOTE_LINE_NUMBER (note), prev);
- NOTE_SOURCE_FILE (new) = NOTE_SOURCE_FILE (note);
- RTX_INTEGRATED_P (new) = RTX_INTEGRATED_P (note);
- }
- }
- if (sched_verbose && added_notes)
- fprintf (dump, ";; added %d line-number notes\n", added_notes);
-}
-
-/* After scheduling the function, delete redundant line notes from the
- insns list. */
-
-static void
-rm_redundant_line_notes ()
-{
- rtx line = 0;
- rtx insn = get_insns ();
- int active_insn = 0;
- int notes = 0;
-
- /* Walk the insns deleting redundant line-number notes. Many of these
- are already present. The remainder tend to occur at basic
- block boundaries. */
- for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
- if (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) > 0)
- {
- /* If there are no active insns following, INSN is redundant. */
- if (active_insn == 0)
- {
- notes++;
- NOTE_SOURCE_FILE (insn) = 0;
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- }
- /* If the line number is unchanged, LINE is redundant. */
- else if (line
- && NOTE_LINE_NUMBER (line) == NOTE_LINE_NUMBER (insn)
- && NOTE_SOURCE_FILE (line) == NOTE_SOURCE_FILE (insn))
- {
- notes++;
- NOTE_SOURCE_FILE (line) = 0;
- NOTE_LINE_NUMBER (line) = NOTE_INSN_DELETED;
- line = insn;
- }
- else
- line = insn;
- active_insn = 0;
- }
- else if (!((GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_DELETED)
- || (GET_CODE (insn) == INSN
- && (GET_CODE (PATTERN (insn)) == USE
- || GET_CODE (PATTERN (insn)) == CLOBBER))))
- active_insn++;
-
- if (sched_verbose && notes)
- fprintf (dump, ";; deleted %d line-number notes\n", notes);
-}
-
-/* Delete notes between head and tail and put them in the chain
- of notes ended by NOTE_LIST. */
-
-static void
-rm_other_notes (head, tail)
- rtx head;
- rtx tail;
-{
- rtx next_tail;
- rtx insn;
-
- if (head == tail
- && (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
- return;
-
- next_tail = NEXT_INSN (tail);
- for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
- {
- rtx prev;
-
- /* Farm out notes, and maybe save them in NOTE_LIST.
- This is needed to keep the debugger from
- getting completely deranged. */
- if (GET_CODE (insn) == NOTE)
- {
- prev = insn;
-
- insn = unlink_other_notes (insn, next_tail);
-
- if (prev == tail)
- abort ();
- if (prev == head)
- abort ();
- if (insn == next_tail)
- abort ();
- }
- }
-}
-
-/* Constructor for `sometimes' data structure. */
-
-static int
-new_sometimes_live (regs_sometimes_live, regno, sometimes_max)
- struct sometimes *regs_sometimes_live;
- int regno;
- int sometimes_max;
-{
- register struct sometimes *p;
-
- /* There should never be a register greater than max_regno here. If there
- is, it means that a define_split has created a new pseudo reg. This
- is not allowed, since there will not be flow info available for any
- new register, so catch the error here. */
- if (regno >= max_regno)
- abort ();
-
- p = &regs_sometimes_live[sometimes_max];
- p->regno = regno;
- p->live_length = 0;
- p->calls_crossed = 0;
- sometimes_max++;
- return sometimes_max;
-}
-
-/* Count lengths of all regs we are currently tracking,
- and find new registers no longer live. */
-
-static void
-finish_sometimes_live (regs_sometimes_live, sometimes_max)
- struct sometimes *regs_sometimes_live;
- int sometimes_max;
-{
- int i;
-
- for (i = 0; i < sometimes_max; i++)
- {
- register struct sometimes *p = &regs_sometimes_live[i];
- int regno = p->regno;
-
- sched_reg_live_length[regno] += p->live_length;
- sched_reg_n_calls_crossed[regno] += p->calls_crossed;
- }
-}
-
-/* functions for computation of registers live/usage info */
-
-/* It is assumed that prior to scheduling basic_block_live_at_start (b)
- contains the registers that are alive at the entry to b.
-
- Two passes follow: The first pass is performed before the scheduling
- of a region. It scans each block of the region forward, computing
- the set of registers alive at the end of the basic block and
- discard REG_DEAD notes (done by find_pre_sched_live ()).
-
- The second path is invoked after scheduling all region blocks.
- It scans each block of the region backward, a block being traversed
- only after its succesors in the region. When the set of registers
- live at the end of a basic block may be changed by the scheduling
- (this may happen for multiple blocks region), it is computed as
- the union of the registers live at the start of its succesors.
- The last-use information is updated by inserting REG_DEAD notes.
- (done by find_post_sched_live ()) */
-
-/* Scan all the insns to be scheduled, removing register death notes.
- Register death notes end up in DEAD_NOTES.
- Recreate the register life information for the end of this basic
- block. */
-
-static void
-find_pre_sched_live (bb)
- int bb;
-{
- rtx insn, next_tail, head, tail;
- int b = BB_TO_BLOCK (bb);
-
- get_block_head_tail (bb, &head, &tail);
- COPY_REG_SET (bb_live_regs, basic_block_live_at_start[b]);
- next_tail = NEXT_INSN (tail);
-
- for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
- {
- rtx prev, next, link;
- int reg_weight = 0;
-
- /* Handle register life information. */
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
- {
- /* See if the register gets born here. */
- /* We must check for registers being born before we check for
- registers dying. It is possible for a register to be born and
- die in the same insn, e.g. reading from a volatile memory
- location into an otherwise unused register. Such a register
- must be marked as dead after this insn. */
- if (GET_CODE (PATTERN (insn)) == SET
- || GET_CODE (PATTERN (insn)) == CLOBBER)
- {
- sched_note_set (PATTERN (insn), 0);
- reg_weight++;
- }
-
- else if (GET_CODE (PATTERN (insn)) == PARALLEL)
- {
- int j;
- for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
- if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
- || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
- {
- sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
- reg_weight++;
- }
-
- /* ??? This code is obsolete and should be deleted. It
- is harmless though, so we will leave it in for now. */
- for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
- if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == USE)
- sched_note_set (XVECEXP (PATTERN (insn), 0, j), 0);
- }
-
- /* Each call cobbers (makes live) all call-clobbered regs
- that are not global or fixed. Note that the function-value
- reg is a call_clobbered reg. */
- if (GET_CODE (insn) == CALL_INSN)
- {
- int j;
- for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
- if (call_used_regs[j] && !global_regs[j]
- && ! fixed_regs[j])
- {
- SET_REGNO_REG_SET (bb_live_regs, j);
- }
- }
-
- /* Need to know what registers this insn kills. */
- for (prev = 0, link = REG_NOTES (insn); link; link = next)
- {
- next = XEXP (link, 1);
- if ((REG_NOTE_KIND (link) == REG_DEAD
- || REG_NOTE_KIND (link) == REG_UNUSED)
- /* Verify that the REG_NOTE has a valid value. */
- && GET_CODE (XEXP (link, 0)) == REG)
- {
- register int regno = REGNO (XEXP (link, 0));
-
- reg_weight--;
-
- /* Only unlink REG_DEAD notes; leave REG_UNUSED notes
- alone. */
- if (REG_NOTE_KIND (link) == REG_DEAD)
- {
- if (prev)
- XEXP (prev, 1) = next;
- else
- REG_NOTES (insn) = next;
- XEXP (link, 1) = dead_notes;
- dead_notes = link;
- }
- else
- prev = link;
-
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- int j = HARD_REGNO_NREGS (regno,
- GET_MODE (XEXP (link, 0)));
- while (--j >= 0)
- {
- CLEAR_REGNO_REG_SET (bb_live_regs, regno+j);
- }
- }
- else
- {
- CLEAR_REGNO_REG_SET (bb_live_regs, regno);
- }
- }
- else
- prev = link;
- }
- }
-
- INSN_REG_WEIGHT (insn) = reg_weight;
- }
-}
-
-/* Update register life and usage information for block bb
- after scheduling. Put register dead notes back in the code. */
-
-static void
-find_post_sched_live (bb)
- int bb;
-{
- int sometimes_max;
- int j, i;
- int b;
- rtx insn;
- rtx head, tail, prev_head, next_tail;
-
- register struct sometimes *regs_sometimes_live;
-
- b = BB_TO_BLOCK (bb);
-
- /* compute live regs at the end of bb as a function of its successors. */
- if (current_nr_blocks > 1)
- {
- int e;
- int first_edge;
-
- first_edge = e = OUT_EDGES (b);
- CLEAR_REG_SET (bb_live_regs);
-
- if (e)
- do
- {
- int b_succ;
-
- b_succ = TO_BLOCK (e);
- IOR_REG_SET (bb_live_regs, basic_block_live_at_start[b_succ]);
- e = NEXT_OUT (e);
- }
- while (e != first_edge);
- }
-
- get_block_head_tail (bb, &head, &tail);
- next_tail = NEXT_INSN (tail);
- prev_head = PREV_INSN (head);
-
- EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, FIRST_PSEUDO_REGISTER, i,
- {
- sched_reg_basic_block[i] = REG_BLOCK_GLOBAL;
- });
-
- /* if the block is empty, same regs are alive at its end and its start.
- since this is not guaranteed after interblock scheduling, make sure they
- are truly identical. */
- if (NEXT_INSN (prev_head) == tail
- && (GET_RTX_CLASS (GET_CODE (tail)) != 'i'))
- {
- if (current_nr_blocks > 1)
- COPY_REG_SET (basic_block_live_at_start[b], bb_live_regs);
-
- return;
- }
-
- b = BB_TO_BLOCK (bb);
- current_block_num = b;
-
- /* Keep track of register lives. */
- old_live_regs = ALLOCA_REG_SET ();
- regs_sometimes_live
- = (struct sometimes *) alloca (max_regno * sizeof (struct sometimes));
- sometimes_max = 0;
-
- /* initiate "sometimes" data, starting with registers live at end */
- sometimes_max = 0;
- COPY_REG_SET (old_live_regs, bb_live_regs);
- EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, 0, j,
- {
- sometimes_max
- = new_sometimes_live (regs_sometimes_live,
- j, sometimes_max);
- });
-
- /* scan insns back, computing regs live info */
- for (insn = tail; insn != prev_head; insn = PREV_INSN (insn))
- {
- /* First we kill registers set by this insn, and then we
- make registers used by this insn live. This is the opposite
- order used above because we are traversing the instructions
- backwards. */
-
- /* Strictly speaking, we should scan REG_UNUSED notes and make
- every register mentioned there live, however, we will just
- kill them again immediately below, so there doesn't seem to
- be any reason why we bother to do this. */
-
- /* See if this is the last notice we must take of a register. */
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
-
- if (GET_CODE (PATTERN (insn)) == SET
- || GET_CODE (PATTERN (insn)) == CLOBBER)
- sched_note_set (PATTERN (insn), 1);
- else if (GET_CODE (PATTERN (insn)) == PARALLEL)
- {
- for (j = XVECLEN (PATTERN (insn), 0) - 1; j >= 0; j--)
- if (GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == SET
- || GET_CODE (XVECEXP (PATTERN (insn), 0, j)) == CLOBBER)
- sched_note_set (XVECEXP (PATTERN (insn), 0, j), 1);
- }
-
- /* This code keeps life analysis information up to date. */
- if (GET_CODE (insn) == CALL_INSN)
- {
- register struct sometimes *p;
-
- /* A call kills all call used registers that are not
- global or fixed, except for those mentioned in the call
- pattern which will be made live again later. */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (call_used_regs[i] && ! global_regs[i]
- && ! fixed_regs[i])
- {
- CLEAR_REGNO_REG_SET (bb_live_regs, i);
- }
-
- /* Regs live at the time of a call instruction must not
- go in a register clobbered by calls. Record this for
- all regs now live. Note that insns which are born or
- die in a call do not cross a call, so this must be done
- after the killings (above) and before the births
- (below). */
- p = regs_sometimes_live;
- for (i = 0; i < sometimes_max; i++, p++)
- if (REGNO_REG_SET_P (bb_live_regs, p->regno))
- p->calls_crossed += 1;
- }
-
- /* Make every register used live, and add REG_DEAD notes for
- registers which were not live before we started. */
- attach_deaths_insn (insn);
-
- /* Find registers now made live by that instruction. */
- EXECUTE_IF_AND_COMPL_IN_REG_SET (bb_live_regs, old_live_regs, 0, j,
- {
- sometimes_max
- = new_sometimes_live (regs_sometimes_live,
- j, sometimes_max);
- });
- IOR_REG_SET (old_live_regs, bb_live_regs);
-
- /* Count lengths of all regs we are worrying about now,
- and handle registers no longer live. */
-
- for (i = 0; i < sometimes_max; i++)
- {
- register struct sometimes *p = &regs_sometimes_live[i];
- int regno = p->regno;
-
- p->live_length += 1;
-
- if (!REGNO_REG_SET_P (bb_live_regs, regno))
- {
- /* This is the end of one of this register's lifetime
- segments. Save the lifetime info collected so far,
- and clear its bit in the old_live_regs entry. */
- sched_reg_live_length[regno] += p->live_length;
- sched_reg_n_calls_crossed[regno] += p->calls_crossed;
- CLEAR_REGNO_REG_SET (old_live_regs, p->regno);
-
- /* Delete the reg_sometimes_live entry for this reg by
- copying the last entry over top of it. */
- *p = regs_sometimes_live[--sometimes_max];
- /* ...and decrement i so that this newly copied entry
- will be processed. */
- i--;
- }
- }
- }
-
- finish_sometimes_live (regs_sometimes_live, sometimes_max);
-
- /* In interblock scheduling, basic_block_live_at_start may have changed. */
- if (current_nr_blocks > 1)
- COPY_REG_SET (basic_block_live_at_start[b], bb_live_regs);
-
-
- FREE_REG_SET (old_live_regs);
-} /* find_post_sched_live */
-
-/* After scheduling the subroutine, restore information about uses of
- registers. */
-
-static void
-update_reg_usage ()
-{
- int regno;
-
- if (n_basic_blocks > 0)
- EXECUTE_IF_SET_IN_REG_SET (bb_live_regs, FIRST_PSEUDO_REGISTER, regno,
- {
- sched_reg_basic_block[regno]
- = REG_BLOCK_GLOBAL;
- });
-
- for (regno = 0; regno < max_regno; regno++)
- if (sched_reg_live_length[regno])
- {
- if (sched_verbose)
- {
- if (REG_LIVE_LENGTH (regno) > sched_reg_live_length[regno])
- fprintf (dump,
- ";; register %d life shortened from %d to %d\n",
- regno, REG_LIVE_LENGTH (regno),
- sched_reg_live_length[regno]);
- /* Negative values are special; don't overwrite the current
- reg_live_length value if it is negative. */
- else if (REG_LIVE_LENGTH (regno) < sched_reg_live_length[regno]
- && REG_LIVE_LENGTH (regno) >= 0)
- fprintf (dump,
- ";; register %d life extended from %d to %d\n",
- regno, REG_LIVE_LENGTH (regno),
- sched_reg_live_length[regno]);
-
- if (!REG_N_CALLS_CROSSED (regno)
- && sched_reg_n_calls_crossed[regno])
- fprintf (dump,
- ";; register %d now crosses calls\n", regno);
- else if (REG_N_CALLS_CROSSED (regno)
- && !sched_reg_n_calls_crossed[regno]
- && REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
- fprintf (dump,
- ";; register %d no longer crosses calls\n", regno);
-
- if (REG_BASIC_BLOCK (regno) != sched_reg_basic_block[regno]
- && sched_reg_basic_block[regno] != REG_BLOCK_UNKNOWN
- && REG_BASIC_BLOCK(regno) != REG_BLOCK_UNKNOWN)
- fprintf (dump,
- ";; register %d changed basic block from %d to %d\n",
- regno, REG_BASIC_BLOCK(regno),
- sched_reg_basic_block[regno]);
-
- }
- /* Negative values are special; don't overwrite the current
- reg_live_length value if it is negative. */
- if (REG_LIVE_LENGTH (regno) >= 0)
- REG_LIVE_LENGTH (regno) = sched_reg_live_length[regno];
-
- if (sched_reg_basic_block[regno] != REG_BLOCK_UNKNOWN
- && REG_BASIC_BLOCK(regno) != REG_BLOCK_UNKNOWN)
- REG_BASIC_BLOCK(regno) = sched_reg_basic_block[regno];
-
- /* We can't change the value of reg_n_calls_crossed to zero for
- pseudos which are live in more than one block.
-
- This is because combine might have made an optimization which
- invalidated basic_block_live_at_start and reg_n_calls_crossed,
- but it does not update them. If we update reg_n_calls_crossed
- here, the two variables are now inconsistent, and this might
- confuse the caller-save code into saving a register that doesn't
- need to be saved. This is only a problem when we zero calls
- crossed for a pseudo live in multiple basic blocks.
-
- Alternatively, we could try to correctly update basic block live
- at start here in sched, but that seems complicated.
-
- Note: it is possible that a global register became local, as result
- of interblock motion, but will remain marked as a global register. */
- if (sched_reg_n_calls_crossed[regno]
- || REG_BASIC_BLOCK (regno) != REG_BLOCK_GLOBAL)
- REG_N_CALLS_CROSSED (regno) = sched_reg_n_calls_crossed[regno];
-
- }
-}
-
-/* Scheduling clock, modified in schedule_block() and queue_to_ready () */
-static int clock_var;
-
-/* Move insns that became ready to fire from queue to ready list. */
-
-static int
-queue_to_ready (ready, n_ready)
- rtx ready[];
- int n_ready;
-{
- rtx insn;
- rtx link;
-
- q_ptr = NEXT_Q (q_ptr);
-
- /* Add all pending insns that can be scheduled without stalls to the
- ready list. */
- for (link = insn_queue[q_ptr]; link; link = XEXP (link, 1))
- {
-
- insn = XEXP (link, 0);
- q_size -= 1;
-
- if (sched_verbose >= 2)
- fprintf (dump, ";;\t\tQ-->Ready: insn %d: ", INSN_UID (insn));
-
- if (sched_verbose >= 2 && INSN_BB (insn) != target_bb)
- fprintf (dump, "(b%d) ", INSN_BLOCK (insn));
-
- ready[n_ready++] = insn;
- if (sched_verbose >= 2)
- fprintf (dump, "moving to ready without stalls\n");
- }
- insn_queue[q_ptr] = 0;
-
- /* If there are no ready insns, stall until one is ready and add all
- of the pending insns at that point to the ready list. */
- if (n_ready == 0)
- {
- register int stalls;
-
- for (stalls = 1; stalls < INSN_QUEUE_SIZE; stalls++)
- {
- if ((link = insn_queue[NEXT_Q_AFTER (q_ptr, stalls)]))
- {
- for (; link; link = XEXP (link, 1))
- {
- insn = XEXP (link, 0);
- q_size -= 1;
-
- if (sched_verbose >= 2)
- fprintf (dump, ";;\t\tQ-->Ready: insn %d: ", INSN_UID (insn));
-
- if (sched_verbose >= 2 && INSN_BB (insn) != target_bb)
- fprintf (dump, "(b%d) ", INSN_BLOCK (insn));
-
- ready[n_ready++] = insn;
- if (sched_verbose >= 2)
- fprintf (dump, "moving to ready with %d stalls\n", stalls);
- }
- insn_queue[NEXT_Q_AFTER (q_ptr, stalls)] = 0;
-
- if (n_ready)
- break;
- }
- }
-
- if (sched_verbose && stalls)
- visualize_stall_cycles (BB_TO_BLOCK (target_bb), stalls);
- q_ptr = NEXT_Q_AFTER (q_ptr, stalls);
- clock_var += stalls;
- }
- return n_ready;
-}
-
-/* Print the ready list for debugging purposes. Callable from debugger
- and machine back ends. */
-
-/* CYGNUS LOCAL - static qualifier removed from debug_ready_list */
-void
-debug_ready_list (ready, n_ready)
- rtx ready[];
- int n_ready;
-{
- int i;
-
- for (i = 0; i < n_ready; i++)
- {
- fprintf (dump, " %d", INSN_UID (ready[i]));
- if (current_nr_blocks > 1 && INSN_BB (ready[i]) != target_bb)
- fprintf (dump, "/b%d", INSN_BLOCK (ready[i]));
- }
- fprintf (dump, "\n");
-}
-
-/* Print names of units on which insn can/should execute, for debugging. */
-
-static void
-insn_print_units (insn)
- rtx insn;
-{
- int i;
- int unit = insn_unit (insn);
-
- if (unit == -1)
- fprintf (dump, "none");
- else if (unit >= 0)
- fprintf (dump, "%s", function_units[unit].name);
- else
- {
- fprintf (dump, "[");
- for (i = 0, unit = ~unit; unit; i++, unit >>= 1)
- if (unit & 1)
- {
- fprintf (dump, "%s", function_units[i].name);
- if (unit != 1)
- fprintf (dump, " ");
- }
- fprintf (dump, "]");
- }
-}
-
-/* MAX_VISUAL_LINES is the maximum number of lines in visualization table
- of a basic block. If more lines are needed, table is splitted to two.
- n_visual_lines is the number of lines printed so far for a block.
- visual_tbl contains the block visualization info.
- vis_no_unit holds insns in a cycle that are not mapped to any unit. */
-#define MAX_VISUAL_LINES 100
-#define INSN_LEN 30
-int n_visual_lines;
-char *visual_tbl;
-int n_vis_no_unit;
-rtx vis_no_unit[10];
-
-/* Finds units that are in use in this fuction. Required only
- for visualization. */
-
-static void
-init_target_units ()
-{
- rtx insn;
- int unit;
-
- for (insn = get_last_insn (); insn; insn = PREV_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
-
- unit = insn_unit (insn);
-
- if (unit < 0)
- target_units |= ~unit;
- else
- target_units |= (1 << unit);
- }
-}
-
-/* Return the length of the visualization table */
-
-static int
-get_visual_tbl_length ()
-{
- int unit, i;
- int n, n1;
- char *s;
-
- /* compute length of one field in line */
- s = (char *) alloca (INSN_LEN + 5);
- sprintf (s, " %33s", "uname");
- n1 = strlen (s);
-
- /* compute length of one line */
- n = strlen (";; ");
- n += n1;
- for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
- if (function_units[unit].bitmask & target_units)
- for (i = 0; i < function_units[unit].multiplicity; i++)
- n += n1;
- n += n1;
- n += strlen ("\n") + 2;
-
- /* compute length of visualization string */
- return (MAX_VISUAL_LINES * n);
-}
-
-/* Init block visualization debugging info */
-
-static void
-init_block_visualization ()
-{
- strcpy (visual_tbl, "");
- n_visual_lines = 0;
- n_vis_no_unit = 0;
-}
-
-#define BUF_LEN 256
-
-static char *
-safe_concat (buf, cur, str)
- char *buf;
- char *cur;
- char *str;
-{
- char *end = buf + BUF_LEN - 2; /* leave room for null */
- int c;
-
- if (cur > end)
- {
- *end = '\0';
- return end;
- }
-
- while (cur < end && (c = *str++) != '\0')
- *cur++ = c;
-
- *cur = '\0';
- return cur;
-}
-
-/* This recognizes rtx, I classified as expressions. These are always */
-/* represent some action on values or results of other expression, */
-/* that may be stored in objects representing values. */
-
-static void
-print_exp (buf, x, verbose)
- char *buf;
- rtx x;
- int verbose;
-{
- char tmp[BUF_LEN];
- char *st[4];
- char *cur = buf;
- char *fun = (char *)0;
- char *sep;
- rtx op[4];
- int i;
-
- for (i = 0; i < 4; i++)
- {
- st[i] = (char *)0;
- op[i] = NULL_RTX;
- }
-
- switch (GET_CODE (x))
- {
- case PLUS:
- op[0] = XEXP (x, 0);
- st[1] = "+";
- op[1] = XEXP (x, 1);
- break;
- case LO_SUM:
- op[0] = XEXP (x, 0);
- st[1] = "+low(";
- op[1] = XEXP (x, 1);
- st[2] = ")";
- break;
- case MINUS:
- op[0] = XEXP (x, 0);
- st[1] = "-";
- op[1] = XEXP (x, 1);
- break;
- case COMPARE:
- fun = "cmp";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case NEG:
- st[0] = "-";
- op[0] = XEXP (x, 0);
- break;
- case MULT:
- op[0] = XEXP (x, 0);
- st[1] = "*";
- op[1] = XEXP (x, 1);
- break;
- case DIV:
- op[0] = XEXP (x, 0);
- st[1] = "/";
- op[1] = XEXP (x, 1);
- break;
- case UDIV:
- fun = "udiv";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case MOD:
- op[0] = XEXP (x, 0);
- st[1] = "%";
- op[1] = XEXP (x, 1);
- break;
- case UMOD:
- fun = "umod";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case SMIN:
- fun = "smin";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case SMAX:
- fun = "smax";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case UMIN:
- fun = "umin";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case UMAX:
- fun = "umax";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case NOT:
- st[0] = "!";
- op[0] = XEXP (x, 0);
- break;
- case AND:
- op[0] = XEXP (x, 0);
- st[1] = "&";
- op[1] = XEXP (x, 1);
- break;
- case IOR:
- op[0] = XEXP (x, 0);
- st[1] = "|";
- op[1] = XEXP (x, 1);
- break;
- case XOR:
- op[0] = XEXP (x, 0);
- st[1] = "^";
- op[1] = XEXP (x, 1);
- break;
- case ASHIFT:
- op[0] = XEXP (x, 0);
- st[1] = "<<";
- op[1] = XEXP (x, 1);
- break;
- case LSHIFTRT:
- op[0] = XEXP (x, 0);
- st[1] = " 0>>";
- op[1] = XEXP (x, 1);
- break;
- case ASHIFTRT:
- op[0] = XEXP (x, 0);
- st[1] = ">>";
- op[1] = XEXP (x, 1);
- break;
- case ROTATE:
- op[0] = XEXP (x, 0);
- st[1] = "<-<";
- op[1] = XEXP (x, 1);
- break;
- case ROTATERT:
- op[0] = XEXP (x, 0);
- st[1] = ">->";
- op[1] = XEXP (x, 1);
- break;
- case ABS:
- fun = "abs";
- op[0] = XEXP (x, 0);
- break;
- case SQRT:
- fun = "sqrt";
- op[0] = XEXP (x, 0);
- break;
- case FFS:
- fun = "ffs";
- op[0] = XEXP (x, 0);
- break;
- case EQ:
- op[0] = XEXP (x, 0);
- st[1] = "==";
- op[1] = XEXP (x, 1);
- break;
- case NE:
- op[0] = XEXP (x, 0);
- st[1] = "!=";
- op[1] = XEXP (x, 1);
- break;
- case GT:
- op[0] = XEXP (x, 0);
- st[1] = ">";
- op[1] = XEXP (x, 1);
- break;
- case GTU:
- fun = "gtu";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case LT:
- op[0] = XEXP (x, 0);
- st[1] = "<";
- op[1] = XEXP (x, 1);
- break;
- case LTU:
- fun = "ltu";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case GE:
- op[0] = XEXP (x, 0);
- st[1] = ">=";
- op[1] = XEXP (x, 1);
- break;
- case GEU:
- fun = "geu";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case LE:
- op[0] = XEXP (x, 0);
- st[1] = "<=";
- op[1] = XEXP (x, 1);
- break;
- case LEU:
- fun = "leu";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- break;
- case SIGN_EXTRACT:
- fun = (verbose) ? "sign_extract" : "sxt";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- op[2] = XEXP (x, 2);
- break;
- case ZERO_EXTRACT:
- fun = (verbose) ? "zero_extract" : "zxt";
- op[0] = XEXP (x, 0);
- op[1] = XEXP (x, 1);
- op[2] = XEXP (x, 2);
- break;
- case SIGN_EXTEND:
- fun = (verbose) ? "sign_extend" : "sxn";
- op[0] = XEXP (x, 0);
- break;
- case ZERO_EXTEND:
- fun = (verbose) ? "zero_extend" : "zxn";
- op[0] = XEXP (x, 0);
- break;
- case FLOAT_EXTEND:
- fun = (verbose) ? "float_extend" : "fxn";
- op[0] = XEXP (x, 0);
- break;
- case TRUNCATE:
- fun = (verbose) ? "trunc" : "trn";
- op[0] = XEXP (x, 0);
- break;
- case FLOAT_TRUNCATE:
- fun = (verbose) ? "float_trunc" : "ftr";
- op[0] = XEXP (x, 0);
- break;
- case FLOAT:
- fun = (verbose) ? "float" : "flt";
- op[0] = XEXP (x, 0);
- break;
- case UNSIGNED_FLOAT:
- fun = (verbose) ? "uns_float" : "ufl";
- op[0] = XEXP (x, 0);
- break;
- case FIX:
- fun = "fix";
- op[0] = XEXP (x, 0);
- break;
- case UNSIGNED_FIX:
- fun = (verbose) ? "uns_fix" : "ufx";
- op[0] = XEXP (x, 0);
- break;
- case PRE_DEC:
- st[0] = "--";
- op[0] = XEXP (x, 0);
- break;
- case PRE_INC:
- st[0] = "++";
- op[0] = XEXP (x, 0);
- break;
- case POST_DEC:
- op[0] = XEXP (x, 0);
- st[1] = "--";
- break;
- case POST_INC:
- op[0] = XEXP (x, 0);
- st[1] = "++";
- break;
- case CALL:
- st[0] = "call ";
- op[0] = XEXP (x, 0);
- if (verbose)
- {
- st[1] = " argc:";
- op[1] = XEXP (x, 1);
- }
- break;
- case IF_THEN_ELSE:
- st[0] = "{(";
- op[0] = XEXP (x, 0);
- st[1] = ")?";
- op[1] = XEXP (x, 1);
- st[2] = ":";
- op[2] = XEXP (x, 2);
- st[3] = "}";
- break;
- case TRAP_IF:
- fun = "trap_if";
- op[0] = TRAP_CONDITION (x);
- break;
- case UNSPEC:
- case UNSPEC_VOLATILE:
- {
- cur = safe_concat (buf, cur, "unspec");
- if (GET_CODE (x) == UNSPEC_VOLATILE)
- cur = safe_concat (buf, cur, "/v");
- cur = safe_concat (buf, cur, "[");
- sep = "";
- for (i = 0; i < XVECLEN (x, 0); i++)
- {
- print_pattern (tmp, XVECEXP (x, 0, i), verbose);
- cur = safe_concat (buf, cur, sep);
- cur = safe_concat (buf, cur, tmp);
- sep = ",";
- }
- cur = safe_concat (buf, cur, "] ");
- sprintf (tmp, "%d", XINT (x, 1));
- cur = safe_concat (buf, cur, tmp);
- }
- break;
- default:
- /* if (verbose) debug_rtx (x); */
- st[0] = GET_RTX_NAME (GET_CODE (x));
- break;
- }
-
- /* Print this as a function? */
- if (fun)
- {
- cur = safe_concat (buf, cur, fun);
- cur = safe_concat (buf, cur, "(");
- }
-
- for (i = 0; i < 4; i++)
- {
- if (st[i])
- cur = safe_concat (buf, cur, st[i]);
-
- if (op[i])
- {
- if (fun && i != 0)
- cur = safe_concat (buf, cur, ",");
-
- print_value (tmp, op[i], verbose);
- cur = safe_concat (buf, cur, tmp);
- }
- }
-
- if (fun)
- cur = safe_concat (buf, cur, ")");
-} /* print_exp */
-
-/* Prints rtxes, i customly classified as values. They're constants, */
-/* registers, labels, symbols and memory accesses. */
-
-static void
-print_value (buf, x, verbose)
- char *buf;
- rtx x;
- int verbose;
-{
- char t[BUF_LEN];
- char *cur = buf;
-
- switch (GET_CODE (x))
- {
- case CONST_INT:
- sprintf (t, "0x%lx", (long)INTVAL (x));
- cur = safe_concat (buf, cur, t);
- break;
- case CONST_DOUBLE:
- sprintf (t, "<0x%lx,0x%lx>", (long)XWINT (x, 2), (long)XWINT (x, 3));
- cur = safe_concat (buf, cur, t);
- break;
- case CONST_STRING:
- cur = safe_concat (buf, cur, "\"");
- cur = safe_concat (buf, cur, XSTR (x, 0));
- cur = safe_concat (buf, cur, "\"");
- break;
- case SYMBOL_REF:
- cur = safe_concat (buf, cur, "`");
- cur = safe_concat (buf, cur, XSTR (x, 0));
- cur = safe_concat (buf, cur, "'");
- break;
- case LABEL_REF:
- sprintf (t, "L%d", INSN_UID (XEXP (x, 0)));
- cur = safe_concat (buf, cur, t);
- break;
- case CONST:
- print_value (t, XEXP (x, 0), verbose);
- cur = safe_concat (buf, cur, "const(");
- cur = safe_concat (buf, cur, t);
- cur = safe_concat (buf, cur, ")");
- break;
- case HIGH:
- print_value (t, XEXP (x, 0), verbose);
- cur = safe_concat (buf, cur, "high(");
- cur = safe_concat (buf, cur, t);
- cur = safe_concat (buf, cur, ")");
- break;
- case REG:
- if (REGNO (x) < FIRST_PSEUDO_REGISTER)
- {
- int c = reg_names[ REGNO (x) ][0];
- if (c >= '0' && c <= '9')
- cur = safe_concat (buf, cur, "%");
-
- cur = safe_concat (buf, cur, reg_names[ REGNO (x) ]);
- }
- else
- {
- sprintf (t, "r%d", REGNO (x));
- cur = safe_concat (buf, cur, t);
- }
- break;
- case SUBREG:
- print_value (t, SUBREG_REG (x), verbose);
- cur = safe_concat (buf, cur, t);
- sprintf (t, "#%d", SUBREG_WORD (x));
- cur = safe_concat (buf, cur, t);
- break;
- case SCRATCH:
- cur = safe_concat (buf, cur, "scratch");
- break;
- case CC0:
- cur = safe_concat (buf, cur, "cc0");
- break;
- case PC:
- cur = safe_concat (buf, cur, "pc");
- break;
- case MEM:
- print_value (t, XEXP (x, 0), verbose);
- cur = safe_concat (buf, cur, "[");
- cur = safe_concat (buf, cur, t);
- cur = safe_concat (buf, cur, "]");
- break;
- default:
- print_exp (t, x, verbose);
- cur = safe_concat (buf, cur, t);
- break;
- }
-} /* print_value */
-
-/* The next step in insn detalization, its pattern recognition */
-
-static void
-print_pattern (buf, x, verbose)
- char *buf;
- rtx x;
- int verbose;
-{
- char t1[BUF_LEN], t2[BUF_LEN], t3[BUF_LEN];
-
- switch (GET_CODE (x))
- {
- case SET:
- print_value (t1, SET_DEST (x), verbose);
- print_value (t2, SET_SRC (x), verbose);
- sprintf (buf, "%s=%s", t1, t2);
- break;
- case RETURN:
- sprintf (buf, "return");
- break;
- case CALL:
- print_exp (buf, x, verbose);
- break;
- case CLOBBER:
- print_value (t1, XEXP (x, 0), verbose);
- sprintf (buf, "clobber %s", t1);
- break;
- case USE:
- print_value (t1, XEXP (x, 0), verbose);
- sprintf (buf, "use %s", t1);
- break;
- case PARALLEL:
- {
- int i;
-
- sprintf (t1, "{");
- for (i = 0; i < XVECLEN (x, 0); i++)
- {
- print_pattern (t2, XVECEXP (x, 0, i), verbose);
- sprintf (t3, "%s%s;", t1, t2);
- strcpy (t1, t3);
- }
- sprintf (buf, "%s}", t1);
- }
- break;
- case SEQUENCE:
- {
- int i;
-
- sprintf (t1, "%%{");
- for (i = 0; i < XVECLEN (x, 0); i++)
- {
- print_insn (t2, XVECEXP (x, 0, i), verbose);
- sprintf (t3, "%s%s;", t1, t2);
- strcpy (t1, t3);
- }
- sprintf (buf, "%s%%}", t1);
- }
- break;
- case ASM_INPUT:
- sprintf (buf, "asm {%s}", XSTR (x, 0));
- break;
- case ADDR_VEC:
- break;
- case ADDR_DIFF_VEC:
- print_value (buf, XEXP (x, 0), verbose);
- break;
- case TRAP_IF:
- print_value (t1, TRAP_CONDITION (x), verbose);
- sprintf (buf, "trap_if %s", t1);
- break;
- case UNSPEC:
- {
- int i;
-
- sprintf (t1, "unspec{");
- for (i = 0; i < XVECLEN (x, 0); i++)
- {
- print_pattern (t2, XVECEXP (x, 0, i), verbose);
- sprintf (t3, "%s%s;", t1, t2);
- strcpy (t1, t3);
- }
- sprintf (buf, "%s}", t1);
- }
- break;
- case UNSPEC_VOLATILE:
- {
- int i;
-
- sprintf (t1, "unspec/v{");
- for (i = 0; i < XVECLEN (x, 0); i++)
- {
- print_pattern (t2, XVECEXP (x, 0, i), verbose);
- sprintf (t3, "%s%s;", t1, t2);
- strcpy (t1, t3);
- }
- sprintf (buf, "%s}", t1);
- }
- break;
- default:
- print_value (buf, x, verbose);
- }
-} /* print_pattern */
-
-/* This is the main function in rtl visualization mechanism. It
- accepts an rtx and tries to recognize it as an insn, then prints it
- properly in human readable form, resembling assembler mnemonics. */
-/* For every insn it prints its UID and BB the insn belongs */
-/* too. (probably the last "option" should be extended somehow, since */
-/* it depends now on sched.c inner variables ...) */
-
-static void
-print_insn (buf, x, verbose)
- char *buf;
- rtx x;
- int verbose;
-{
- char t[BUF_LEN];
- rtx insn = x;
-
- switch (GET_CODE (x))
- {
- case INSN:
- print_pattern (t, PATTERN (x), verbose);
- if (verbose)
- sprintf (buf, "b%d: i% 4d: %s", INSN_BB (x),
- INSN_UID (x), t);
- else
- sprintf (buf, "%-4d %s", INSN_UID (x), t);
- break;
- case JUMP_INSN:
- print_pattern (t, PATTERN (x), verbose);
- if (verbose)
- sprintf (buf, "b%d: i% 4d: jump %s", INSN_BB (x),
- INSN_UID (x), t);
- else
- sprintf (buf, "%-4d %s", INSN_UID (x), t);
- break;
- case CALL_INSN:
- x = PATTERN (insn);
- if (GET_CODE (x) == PARALLEL)
- {
- x = XVECEXP (x, 0, 0);
- print_pattern (t, x, verbose);
- }
- else
- strcpy (t, "call <...>");
- if (verbose)
- sprintf (buf, "b%d: i% 4d: %s", INSN_BB (insn),
- INSN_UID (insn), t);
- else
- sprintf (buf, "%-4d %s", INSN_UID (insn), t);
- break;
- case CODE_LABEL:
- sprintf (buf, "L%d:", INSN_UID (x));
- break;
- case BARRIER:
- sprintf (buf, "i% 4d: barrier", INSN_UID (x));
- break;
- case NOTE:
- if (NOTE_LINE_NUMBER (x) > 0)
- sprintf (buf, "%4d note \"%s\" %d", INSN_UID (x),
- NOTE_SOURCE_FILE (x), NOTE_LINE_NUMBER (x));
- else
- sprintf (buf, "%4d %s", INSN_UID (x),
- GET_NOTE_INSN_NAME (NOTE_LINE_NUMBER (x)));
- break;
- default:
- if (verbose)
- {
- sprintf (buf, "Not an INSN at all\n");
- debug_rtx (x);
- }
- else
- sprintf (buf, "i%-4d <What?>", INSN_UID (x));
- }
-} /* print_insn */
-
-/* Print visualization debugging info */
-
-static void
-print_block_visualization (b, s)
- int b;
- char *s;
-{
- int unit, i;
-
- /* print header */
- fprintf (dump, "\n;; ==================== scheduling visualization for block %d %s \n", b, s);
-
- /* Print names of units */
- fprintf (dump, ";; %-8s", "clock");
- for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
- if (function_units[unit].bitmask & target_units)
- for (i = 0; i < function_units[unit].multiplicity; i++)
- fprintf (dump, " %-33s", function_units[unit].name);
- fprintf (dump, " %-8s\n", "no-unit");
-
- fprintf (dump, ";; %-8s", "=====");
- for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
- if (function_units[unit].bitmask & target_units)
- for (i = 0; i < function_units[unit].multiplicity; i++)
- fprintf (dump, " %-33s", "==============================");
- fprintf (dump, " %-8s\n", "=======");
-
- /* Print insns in each cycle */
- fprintf (dump, "%s\n", visual_tbl);
-}
-
-/* Print insns in the 'no_unit' column of visualization */
-
-static void
-visualize_no_unit (insn)
- rtx insn;
-{
- vis_no_unit[n_vis_no_unit] = insn;
- n_vis_no_unit++;
-}
-
-/* Print insns scheduled in clock, for visualization. */
-
-static void
-visualize_scheduled_insns (b, clock)
- int b, clock;
-{
- int i, unit;
-
- /* if no more room, split table into two */
- if (n_visual_lines >= MAX_VISUAL_LINES)
- {
- print_block_visualization (b, "(incomplete)");
- init_block_visualization ();
- }
-
- n_visual_lines++;
-
- sprintf (visual_tbl + strlen (visual_tbl), ";; %-8d", clock);
- for (unit = 0; unit < FUNCTION_UNITS_SIZE; unit++)
- if (function_units[unit].bitmask & target_units)
- for (i = 0; i < function_units[unit].multiplicity; i++)
- {
- int instance = unit + i * FUNCTION_UNITS_SIZE;
- rtx insn = unit_last_insn[instance];
-
- /* print insns that still keep the unit busy */
- if (insn &&
- actual_hazard_this_instance (unit, instance, insn, clock, 0))
- {
- char str[BUF_LEN];
- print_insn (str, insn, 0);
- str[INSN_LEN] = '\0';
- sprintf (visual_tbl + strlen (visual_tbl), " %-33s", str);
- }
- else
- sprintf (visual_tbl + strlen (visual_tbl), " %-33s", "------------------------------");
- }
-
- /* print insns that are not assigned to any unit */
- for (i = 0; i < n_vis_no_unit; i++)
- sprintf (visual_tbl + strlen (visual_tbl), " %-8d",
- INSN_UID (vis_no_unit[i]));
- n_vis_no_unit = 0;
-
- sprintf (visual_tbl + strlen (visual_tbl), "\n");
-}
-
-/* Print stalled cycles */
-
-static void
-visualize_stall_cycles (b, stalls)
- int b, stalls;
-{
- int i;
-
- /* if no more room, split table into two */
- if (n_visual_lines >= MAX_VISUAL_LINES)
- {
- print_block_visualization (b, "(incomplete)");
- init_block_visualization ();
- }
-
- n_visual_lines++;
-
- sprintf (visual_tbl + strlen (visual_tbl), ";; ");
- for (i = 0; i < stalls; i++)
- sprintf (visual_tbl + strlen (visual_tbl), ".");
- sprintf (visual_tbl + strlen (visual_tbl), "\n");
-}
-
-/* move_insn1: Remove INSN from insn chain, and link it after LAST insn */
-
-static rtx
-move_insn1 (insn, last)
- rtx insn, last;
-{
- NEXT_INSN (PREV_INSN (insn)) = NEXT_INSN (insn);
- PREV_INSN (NEXT_INSN (insn)) = PREV_INSN (insn);
-
- NEXT_INSN (insn) = NEXT_INSN (last);
- PREV_INSN (NEXT_INSN (last)) = insn;
-
- NEXT_INSN (last) = insn;
- PREV_INSN (insn) = last;
-
- return insn;
-}
-
-/* Search INSN for fake REG_DEAD note pairs for NOTE_INSN_SETJMP,
- NOTE_INSN_{LOOP,EHREGION}_{BEG,END}; and convert them back into
- NOTEs. The REG_DEAD note following first one is contains the saved
- value for NOTE_BLOCK_NUMBER which is useful for
- NOTE_INSN_EH_REGION_{BEG,END} NOTEs. LAST is the last instruction
- output by the instruction scheduler. Return the new value of LAST. */
-
-static rtx
-reemit_notes (insn, last)
- rtx insn;
- rtx last;
-{
- rtx note, retval;
-
- retval = last;
- for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
- {
- if (REG_NOTE_KIND (note) == REG_DEAD
- && GET_CODE (XEXP (note, 0)) == CONST_INT)
- {
- int note_type = INTVAL (XEXP (note, 0));
- if (note_type == NOTE_INSN_SETJMP)
- {
- retval = emit_note_after (NOTE_INSN_SETJMP, insn);
- CONST_CALL_P (retval) = CONST_CALL_P (note);
- remove_note (insn, note);
- note = XEXP (note, 1);
- }
- else if (note_type == NOTE_INSN_RANGE_START
- || note_type == NOTE_INSN_RANGE_END)
- {
- last = emit_note_before (note_type, last);
- remove_note (insn, note);
- note = XEXP (note, 1);
- NOTE_RANGE_INFO (last) = XEXP (note, 0);
- }
- else
- {
- last = emit_note_before (INTVAL (XEXP (note, 0)), last);
- remove_note (insn, note);
- note = XEXP (note, 1);
- NOTE_BLOCK_NUMBER (last) = INTVAL (XEXP (note, 0));
- }
- remove_note (insn, note);
- }
- }
- return retval;
-}
-
-/* Move INSN, and all insns which should be issued before it,
- due to SCHED_GROUP_P flag. Reemit notes if needed.
-
- Return the last insn emitted by the scheduler, which is the
- return value from the first call to reemit_notes. */
-
-static rtx
-move_insn (insn, last)
- rtx insn, last;
-{
- rtx retval = NULL;
-
- /* If INSN has SCHED_GROUP_P set, then issue it and any other
- insns with SCHED_GROUP_P set first. */
- while (SCHED_GROUP_P (insn))
- {
- rtx prev = PREV_INSN (insn);
-
- /* Move a SCHED_GROUP_P insn. */
- move_insn1 (insn, last);
- /* If this is the first call to reemit_notes, then record
- its return value. */
- if (retval == NULL_RTX)
- retval = reemit_notes (insn, insn);
- else
- reemit_notes (insn, insn);
- insn = prev;
- }
-
- /* Now move the first non SCHED_GROUP_P insn. */
- move_insn1 (insn, last);
-
- /* If this is the first call to reemit_notes, then record
- its return value. */
- if (retval == NULL_RTX)
- retval = reemit_notes (insn, insn);
- else
- reemit_notes (insn, insn);
-
- return retval;
-}
-
-/* Return an insn which represents a SCHED_GROUP, which is
- the last insn in the group. */
-
-static rtx
-group_leader (insn)
- rtx insn;
-{
- rtx prev;
-
- do
- {
- prev = insn;
- insn = next_nonnote_insn (insn);
- }
- while (insn && SCHED_GROUP_P (insn) && (GET_CODE (insn) != CODE_LABEL));
-
- return prev;
-}
-
-/* Use forward list scheduling to rearrange insns of block BB in region RGN,
- possibly bringing insns from subsequent blocks in the same region.
- Return number of insns scheduled. */
-
-static int
-schedule_block (bb, rgn_n_insns)
- int bb;
- int rgn_n_insns;
-{
- /* Local variables. */
- rtx insn, last;
- rtx *ready;
- int i;
- int n_ready = 0;
- int can_issue_more;
-
- /* flow block of this bb */
- int b = BB_TO_BLOCK (bb);
-
- /* target_n_insns == number of insns in b before scheduling starts.
- sched_target_n_insns == how many of b's insns were scheduled.
- sched_n_insns == how many insns were scheduled in b */
- int target_n_insns = 0;
- int sched_target_n_insns = 0;
- int sched_n_insns = 0;
-
-#define NEED_NOTHING 0
-#define NEED_HEAD 1
-#define NEED_TAIL 2
- int new_needs;
-
- /* head/tail info for this block */
- rtx prev_head;
- rtx next_tail;
- rtx head;
- rtx tail;
- int bb_src;
-
- /* We used to have code to avoid getting parameters moved from hard
- argument registers into pseudos.
-
- However, it was removed when it proved to be of marginal benefit
- and caused problems because schedule_block and compute_forward_dependences
- had different notions of what the "head" insn was. */
- get_block_head_tail (bb, &head, &tail);
-
- /* Interblock scheduling could have moved the original head insn from this
- block into a proceeding block. This may also cause schedule_block and
- compute_forward_dependences to have different notions of what the
- "head" insn was.
-
- If the interblock movement happened to make this block start with
- some notes (LOOP, EH or SETJMP) before the first real insn, then
- HEAD will have various special notes attached to it which must be
- removed so that we don't end up with extra copies of the notes. */
- if (GET_RTX_CLASS (GET_CODE (head)) == 'i')
- {
- rtx note;
-
- for (note = REG_NOTES (head); note; note = XEXP (note, 1))
- if (REG_NOTE_KIND (note) == REG_DEAD
- && GET_CODE (XEXP (note, 0)) == CONST_INT)
- remove_note (head, note);
- }
-
- next_tail = NEXT_INSN (tail);
- prev_head = PREV_INSN (head);
-
- /* If the only insn left is a NOTE or a CODE_LABEL, then there is no need
- to schedule this block. */
- if (head == tail
- && (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
- return (sched_n_insns);
-
- /* debug info */
- if (sched_verbose)
- {
- fprintf (dump, ";; ======================================================\n");
- fprintf (dump,
- ";; -- basic block %d from %d to %d -- %s reload\n",
- b, INSN_UID (BLOCK_HEAD (b)), INSN_UID (BLOCK_END (b)),
- (reload_completed ? "after" : "before"));
- fprintf (dump, ";; ======================================================\n");
- fprintf (dump, "\n");
-
- visual_tbl = (char *) alloca (get_visual_tbl_length ());
- init_block_visualization ();
- }
-
- /* remove remaining note insns from the block, save them in
- note_list. These notes are restored at the end of
- schedule_block (). */
- note_list = 0;
- rm_other_notes (head, tail);
-
- target_bb = bb;
-
- /* prepare current target block info */
- if (current_nr_blocks > 1)
- {
- candidate_table = (candidate *) alloca (current_nr_blocks * sizeof (candidate));
-
- bblst_last = 0;
- /* ??? It is not clear why bblst_size is computed this way. The original
- number was clearly too small as it resulted in compiler failures.
- Multiplying by the original number by 2 (to account for update_bbs
- members) seems to be a reasonable solution. */
- /* ??? Or perhaps there is a bug somewhere else in this file? */
- bblst_size = (current_nr_blocks - bb) * rgn_nr_edges * 2;
- bblst_table = (int *) alloca (bblst_size * sizeof (int));
-
- bitlst_table_last = 0;
- bitlst_table_size = rgn_nr_edges;
- bitlst_table = (int *) alloca (rgn_nr_edges * sizeof (int));
-
- compute_trg_info (bb);
- }
-
- clear_units ();
-
- /* Allocate the ready list */
- ready = (rtx *) alloca ((rgn_n_insns + 1) * sizeof (rtx));
-
- /* Print debugging information. */
- if (sched_verbose >= 5)
- debug_dependencies ();
-
-
- /* Initialize ready list with all 'ready' insns in target block.
- Count number of insns in the target block being scheduled. */
- n_ready = 0;
- for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
- {
- rtx next;
-
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
- next = NEXT_INSN (insn);
-
- if (INSN_DEP_COUNT (insn) == 0
- && (SCHED_GROUP_P (next) == 0 || GET_RTX_CLASS (GET_CODE (next)) != 'i'))
- ready[n_ready++] = insn;
- if (!(SCHED_GROUP_P (insn)))
- target_n_insns++;
- }
-
- /* Add to ready list all 'ready' insns in valid source blocks.
- For speculative insns, check-live, exception-free, and
- issue-delay. */
- for (bb_src = bb + 1; bb_src < current_nr_blocks; bb_src++)
- if (IS_VALID (bb_src))
- {
- rtx src_head;
- rtx src_next_tail;
- rtx tail, head;
-
- get_block_head_tail (bb_src, &head, &tail);
- src_next_tail = NEXT_INSN (tail);
- src_head = head;
-
- if (head == tail
- && (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
- continue;
-
- for (insn = src_head; insn != src_next_tail; insn = NEXT_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
-
- if (!CANT_MOVE (insn)
- && (!IS_SPECULATIVE_INSN (insn)
- || (insn_issue_delay (insn) <= 3
- && check_live (insn, bb_src)
- && is_exception_free (insn, bb_src, target_bb))))
-
- {
- rtx next;
-
- next = NEXT_INSN (insn);
- if (INSN_DEP_COUNT (insn) == 0
- && (SCHED_GROUP_P (next) == 0
- || GET_RTX_CLASS (GET_CODE (next)) != 'i'))
- ready[n_ready++] = insn;
- }
- }
- }
-
-#ifdef MD_SCHED_INIT
- MD_SCHED_INIT (dump, sched_verbose);
-#endif
-
- /* no insns scheduled in this block yet */
- last_scheduled_insn = 0;
-
- /* Sort the ready list */
- SCHED_SORT (ready, n_ready);
-#ifdef MD_SCHED_REORDER
- MD_SCHED_REORDER (dump, sched_verbose, ready, n_ready);
-#endif
-
- if (sched_verbose >= 2)
- {
- fprintf (dump, ";;\t\tReady list initially: ");
- debug_ready_list (ready, n_ready);
- }
-
- /* Q_SIZE is the total number of insns in the queue. */
- q_ptr = 0;
- q_size = 0;
- clock_var = 0;
- last_clock_var = 0;
- bzero ((char *) insn_queue, sizeof (insn_queue));
-
- /* We start inserting insns after PREV_HEAD. */
- last = prev_head;
-
- /* Initialize INSN_QUEUE, LIST and NEW_NEEDS. */
- new_needs = (NEXT_INSN (prev_head) == BLOCK_HEAD (b)
- ? NEED_HEAD : NEED_NOTHING);
- if (PREV_INSN (next_tail) == BLOCK_END (b))
- new_needs |= NEED_TAIL;
-
- /* loop until all the insns in BB are scheduled. */
- while (sched_target_n_insns < target_n_insns)
- {
- int b1;
-
- clock_var++;
-
- /* Add to the ready list all pending insns that can be issued now.
- If there are no ready insns, increment clock until one
- is ready and add all pending insns at that point to the ready
- list. */
- n_ready = queue_to_ready (ready, n_ready);
-
- if (n_ready == 0)
- abort ();
-
- if (sched_verbose >= 2)
- {
- fprintf (dump, ";;\t\tReady list after queue_to_ready: ");
- debug_ready_list (ready, n_ready);
- }
-
- /* Sort the ready list. */
- SCHED_SORT (ready, n_ready);
-#ifdef MD_SCHED_REORDER
- MD_SCHED_REORDER (dump, sched_verbose, ready, n_ready);
-#endif
-
- if (sched_verbose)
- {
- fprintf (dump, "\n;;\tReady list (t =%3d): ", clock_var);
- debug_ready_list (ready, n_ready);
- }
-
- /* Issue insns from ready list.
- It is important to count down from n_ready, because n_ready may change
- as insns are issued. */
- can_issue_more = issue_rate;
- for (i = n_ready - 1; i >= 0 && can_issue_more; i--)
- {
- rtx insn = ready[i];
- int cost = actual_hazard (insn_unit (insn), insn, clock_var, 0);
-
- if (cost > 1)
- {
- queue_insn (insn, cost);
- ready[i] = ready[--n_ready]; /* remove insn from ready list */
- }
- else if (cost == 0)
- {
- /* an interblock motion? */
- if (INSN_BB (insn) != target_bb)
- {
- rtx temp;
-
- if (IS_SPECULATIVE_INSN (insn))
- {
-
- if (!check_live (insn, INSN_BB (insn)))
- {
- /* speculative motion, live check failed, remove
- insn from ready list */
- ready[i] = ready[--n_ready];
- continue;
- }
- update_live (insn, INSN_BB (insn));
-
- /* for speculative load, mark insns fed by it. */
- if (IS_LOAD_INSN (insn) || FED_BY_SPEC_LOAD (insn))
- set_spec_fed (insn);
-
- nr_spec++;
- }
- nr_inter++;
-
- temp = insn;
- while (SCHED_GROUP_P (temp))
- temp = PREV_INSN (temp);
-
- /* Update source block boundaries. */
- b1 = INSN_BLOCK (temp);
- if (temp == BLOCK_HEAD (b1)
- && insn == BLOCK_END (b1))
- {
- /* We moved all the insns in the basic block.
- Emit a note after the last insn and update the
- begin/end boundaries to point to the note. */
- emit_note_after (NOTE_INSN_DELETED, insn);
- BLOCK_END (b1) = NEXT_INSN (insn);
- BLOCK_HEAD (b1) = NEXT_INSN (insn);
- }
- else if (insn == BLOCK_END (b1))
- {
- /* We took insns from the end of the basic block,
- so update the end of block boundary so that it
- points to the first insn we did not move. */
- BLOCK_END (b1) = PREV_INSN (temp);
- }
- else if (temp == BLOCK_HEAD (b1))
- {
- /* We took insns from the start of the basic block,
- so update the start of block boundary so that
- it points to the first insn we did not move. */
- BLOCK_HEAD (b1) = NEXT_INSN (insn);
- }
- }
- else
- {
- /* in block motion */
- sched_target_n_insns++;
- }
-
- last_scheduled_insn = insn;
- last = move_insn (insn, last);
- sched_n_insns++;
-
-#ifdef MD_SCHED_VARIABLE_ISSUE
- MD_SCHED_VARIABLE_ISSUE (dump, sched_verbose, insn, can_issue_more);
-#else
- can_issue_more--;
-#endif
-
- n_ready = schedule_insn (insn, ready, n_ready, clock_var);
-
- /* remove insn from ready list */
- ready[i] = ready[--n_ready];
-
- /* close this block after scheduling its jump */
- if (GET_CODE (last_scheduled_insn) == JUMP_INSN)
- break;
- }
- }
-
- /* debug info */
- if (sched_verbose)
- {
- visualize_scheduled_insns (b, clock_var);
- }
- }
-
- /* debug info */
- if (sched_verbose)
- {
- fprintf (dump, ";;\tReady list (final): ");
- debug_ready_list (ready, n_ready);
- print_block_visualization (b, "");
- }
-
- /* Sanity check -- queue must be empty now. Meaningless if region has
- multiple bbs. */
- if (current_nr_blocks > 1)
- if (!flag_schedule_interblock && q_size != 0)
- abort ();
-
- /* update head/tail boundaries. */
- head = NEXT_INSN (prev_head);
- tail = last;
-
- /* Restore-other-notes: NOTE_LIST is the end of a chain of notes
- previously found among the insns. Insert them at the beginning
- of the insns. */
- if (note_list != 0)
- {
- rtx note_head = note_list;
-
- while (PREV_INSN (note_head))
- {
- note_head = PREV_INSN (note_head);
- }
-
- PREV_INSN (note_head) = PREV_INSN (head);
- NEXT_INSN (PREV_INSN (head)) = note_head;
- PREV_INSN (head) = note_list;
- NEXT_INSN (note_list) = head;
- head = note_head;
- }
-
- /* update target block boundaries. */
- if (new_needs & NEED_HEAD)
- BLOCK_HEAD (b) = head;
-
- if (new_needs & NEED_TAIL)
- BLOCK_END (b) = tail;
-
- /* debugging */
- if (sched_verbose)
- {
- fprintf (dump, ";; total time = %d\n;; new basic block head = %d\n",
- clock_var, INSN_UID (BLOCK_HEAD (b)));
- fprintf (dump, ";; new basic block end = %d\n\n",
- INSN_UID (BLOCK_END (b)));
- }
-
- return (sched_n_insns);
-} /* schedule_block () */
-
-
-/* print the bit-set of registers, S. callable from debugger */
-
-extern void
-debug_reg_vector (s)
- regset s;
-{
- int regno;
-
- EXECUTE_IF_SET_IN_REG_SET (s, 0, regno,
- {
- fprintf (dump, " %d", regno);
- });
-
- fprintf (dump, "\n");
-}
-
-/* Use the backward dependences from LOG_LINKS to build
- forward dependences in INSN_DEPEND. */
-
-static void
-compute_block_forward_dependences (bb)
- int bb;
-{
- rtx insn, link;
- rtx tail, head;
- rtx next_tail;
- enum reg_note dep_type;
-
- get_block_head_tail (bb, &head, &tail);
- next_tail = NEXT_INSN (tail);
- for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
-
- insn = group_leader (insn);
-
- for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
- {
- rtx x = group_leader (XEXP (link, 0));
- rtx new_link;
-
- if (x != XEXP (link, 0))
- continue;
-
- /* Ignore dependences upon deleted insn */
- if (GET_CODE (x) == NOTE || INSN_DELETED_P (x))
- continue;
- if (find_insn_list (insn, INSN_DEPEND (x)))
- continue;
-
- new_link = alloc_INSN_LIST (insn, INSN_DEPEND (x));
-
- dep_type = REG_NOTE_KIND (link);
- PUT_REG_NOTE_KIND (new_link, dep_type);
-
- INSN_DEPEND (x) = new_link;
- INSN_DEP_COUNT (insn) += 1;
- }
- }
-}
-
-/* Initialize variables for region data dependence analysis.
- n_bbs is the number of region blocks */
-
-__inline static void
-init_rgn_data_dependences (n_bbs)
- int n_bbs;
-{
- int bb;
-
- /* variables for which one copy exists for each block */
- bzero ((char *) bb_pending_read_insns, n_bbs * sizeof (rtx));
- bzero ((char *) bb_pending_read_mems, n_bbs * sizeof (rtx));
- bzero ((char *) bb_pending_write_insns, n_bbs * sizeof (rtx));
- bzero ((char *) bb_pending_write_mems, n_bbs * sizeof (rtx));
- bzero ((char *) bb_pending_lists_length, n_bbs * sizeof (rtx));
- bzero ((char *) bb_last_pending_memory_flush, n_bbs * sizeof (rtx));
- bzero ((char *) bb_last_function_call, n_bbs * sizeof (rtx));
- bzero ((char *) bb_sched_before_next_call, n_bbs * sizeof (rtx));
-
- /* Create an insn here so that we can hang dependencies off of it later. */
- for (bb = 0; bb < n_bbs; bb++)
- {
- bb_sched_before_next_call[bb] =
- gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX,
- NULL_RTX, 0, NULL_RTX, NULL_RTX);
- LOG_LINKS (bb_sched_before_next_call[bb]) = 0;
- }
-}
-
-/* Add dependences so that branches are scheduled to run last in their block */
-
-static void
-add_branch_dependences (head, tail)
- rtx head, tail;
-{
-
- rtx insn, last;
-
- /* For all branches, calls, uses, and cc0 setters, force them to remain
- in order at the end of the block by adding dependencies and giving
- the last a high priority. There may be notes present, and prev_head
- may also be a note.
-
- Branches must obviously remain at the end. Calls should remain at the
- end since moving them results in worse register allocation. Uses remain
- at the end to ensure proper register allocation. cc0 setters remaim
- at the end because they can't be moved away from their cc0 user. */
- insn = tail;
- last = 0;
- while (GET_CODE (insn) == CALL_INSN || GET_CODE (insn) == JUMP_INSN
- || (GET_CODE (insn) == INSN
- && (GET_CODE (PATTERN (insn)) == USE
-#ifdef HAVE_cc0
- || sets_cc0_p (PATTERN (insn))
-#endif
- ))
- || GET_CODE (insn) == NOTE)
- {
- if (GET_CODE (insn) != NOTE)
- {
- if (last != 0
- && !find_insn_list (insn, LOG_LINKS (last)))
- {
- add_dependence (last, insn, REG_DEP_ANTI);
- INSN_REF_COUNT (insn)++;
- }
-
- CANT_MOVE (insn) = 1;
-
- last = insn;
- /* Skip over insns that are part of a group.
- Make each insn explicitly depend on the previous insn.
- This ensures that only the group header will ever enter
- the ready queue (and, when scheduled, will automatically
- schedule the SCHED_GROUP_P block). */
- while (SCHED_GROUP_P (insn))
- {
- rtx temp = prev_nonnote_insn (insn);
- add_dependence (insn, temp, REG_DEP_ANTI);
- insn = temp;
- }
- }
-
- /* Don't overrun the bounds of the basic block. */
- if (insn == head)
- break;
-
- insn = PREV_INSN (insn);
- }
-
- /* make sure these insns are scheduled last in their block */
- insn = last;
- if (insn != 0)
- while (insn != head)
- {
- insn = prev_nonnote_insn (insn);
-
- if (INSN_REF_COUNT (insn) != 0)
- continue;
-
- if (!find_insn_list (last, LOG_LINKS (insn)))
- add_dependence (last, insn, REG_DEP_ANTI);
- INSN_REF_COUNT (insn) = 1;
-
- /* Skip over insns that are part of a group. */
- while (SCHED_GROUP_P (insn))
- insn = prev_nonnote_insn (insn);
- }
-}
-
-/* Compute bacward dependences inside BB. In a multiple blocks region:
- (1) a bb is analyzed after its predecessors, and (2) the lists in
- effect at the end of bb (after analyzing for bb) are inherited by
- bb's successrs.
-
- Specifically for reg-reg data dependences, the block insns are
- scanned by sched_analyze () top-to-bottom. Two lists are
- naintained by sched_analyze (): reg_last_defs[] for register DEFs,
- and reg_last_uses[] for register USEs.
-
- When analysis is completed for bb, we update for its successors:
- ; - DEFS[succ] = Union (DEFS [succ], DEFS [bb])
- ; - USES[succ] = Union (USES [succ], DEFS [bb])
-
- The mechanism for computing mem-mem data dependence is very
- similar, and the result is interblock dependences in the region. */
-
-static void
-compute_block_backward_dependences (bb)
- int bb;
-{
- int b;
- rtx x;
- rtx head, tail;
- int max_reg = max_reg_num ();
-
- b = BB_TO_BLOCK (bb);
-
- if (current_nr_blocks == 1)
- {
- reg_last_uses = (rtx *) alloca (max_reg * sizeof (rtx));
- reg_last_sets = (rtx *) alloca (max_reg * sizeof (rtx));
-
- bzero ((char *) reg_last_uses, max_reg * sizeof (rtx));
- bzero ((char *) reg_last_sets, max_reg * sizeof (rtx));
-
- pending_read_insns = 0;
- pending_read_mems = 0;
- pending_write_insns = 0;
- pending_write_mems = 0;
- pending_lists_length = 0;
- last_function_call = 0;
- last_pending_memory_flush = 0;
- sched_before_next_call
- = gen_rtx_INSN (VOIDmode, 0, NULL_RTX, NULL_RTX,
- NULL_RTX, 0, NULL_RTX, NULL_RTX);
- LOG_LINKS (sched_before_next_call) = 0;
- }
- else
- {
- reg_last_uses = bb_reg_last_uses[bb];
- reg_last_sets = bb_reg_last_sets[bb];
-
- pending_read_insns = bb_pending_read_insns[bb];
- pending_read_mems = bb_pending_read_mems[bb];
- pending_write_insns = bb_pending_write_insns[bb];
- pending_write_mems = bb_pending_write_mems[bb];
- pending_lists_length = bb_pending_lists_length[bb];
- last_function_call = bb_last_function_call[bb];
- last_pending_memory_flush = bb_last_pending_memory_flush[bb];
-
- sched_before_next_call = bb_sched_before_next_call[bb];
- }
-
- /* do the analysis for this block */
- get_block_head_tail (bb, &head, &tail);
- sched_analyze (head, tail);
- add_branch_dependences (head, tail);
-
- if (current_nr_blocks > 1)
- {
- int e, first_edge;
- int b_succ, bb_succ;
- int reg;
- rtx link_insn, link_mem;
- rtx u;
-
- /* these lists should point to the right place, for correct freeing later. */
- bb_pending_read_insns[bb] = pending_read_insns;
- bb_pending_read_mems[bb] = pending_read_mems;
- bb_pending_write_insns[bb] = pending_write_insns;
- bb_pending_write_mems[bb] = pending_write_mems;
-
- /* bb's structures are inherited by it's successors */
- first_edge = e = OUT_EDGES (b);
- if (e > 0)
- do
- {
- b_succ = TO_BLOCK (e);
- bb_succ = BLOCK_TO_BB (b_succ);
-
- /* only bbs "below" bb, in the same region, are interesting */
- if (CONTAINING_RGN (b) != CONTAINING_RGN (b_succ)
- || bb_succ <= bb)
- {
- e = NEXT_OUT (e);
- continue;
- }
-
- for (reg = 0; reg < max_reg; reg++)
- {
-
- /* reg-last-uses lists are inherited by bb_succ */
- for (u = reg_last_uses[reg]; u; u = XEXP (u, 1))
- {
- if (find_insn_list (XEXP (u, 0), (bb_reg_last_uses[bb_succ])[reg]))
- continue;
-
- (bb_reg_last_uses[bb_succ])[reg]
- = alloc_INSN_LIST (XEXP (u, 0),
- (bb_reg_last_uses[bb_succ])[reg]);
- }
-
- /* reg-last-defs lists are inherited by bb_succ */
- for (u = reg_last_sets[reg]; u; u = XEXP (u, 1))
- {
- if (find_insn_list (XEXP (u, 0), (bb_reg_last_sets[bb_succ])[reg]))
- continue;
-
- (bb_reg_last_sets[bb_succ])[reg]
- = alloc_INSN_LIST (XEXP (u, 0),
- (bb_reg_last_sets[bb_succ])[reg]);
- }
- }
-
- /* mem read/write lists are inherited by bb_succ */
- link_insn = pending_read_insns;
- link_mem = pending_read_mems;
- while (link_insn)
- {
- if (!(find_insn_mem_list (XEXP (link_insn, 0), XEXP (link_mem, 0),
- bb_pending_read_insns[bb_succ],
- bb_pending_read_mems[bb_succ])))
- add_insn_mem_dependence (&bb_pending_read_insns[bb_succ],
- &bb_pending_read_mems[bb_succ],
- XEXP (link_insn, 0), XEXP (link_mem, 0));
- link_insn = XEXP (link_insn, 1);
- link_mem = XEXP (link_mem, 1);
- }
-
- link_insn = pending_write_insns;
- link_mem = pending_write_mems;
- while (link_insn)
- {
- if (!(find_insn_mem_list (XEXP (link_insn, 0), XEXP (link_mem, 0),
- bb_pending_write_insns[bb_succ],
- bb_pending_write_mems[bb_succ])))
- add_insn_mem_dependence (&bb_pending_write_insns[bb_succ],
- &bb_pending_write_mems[bb_succ],
- XEXP (link_insn, 0), XEXP (link_mem, 0));
-
- link_insn = XEXP (link_insn, 1);
- link_mem = XEXP (link_mem, 1);
- }
-
- /* last_function_call is inherited by bb_succ */
- for (u = last_function_call; u; u = XEXP (u, 1))
- {
- if (find_insn_list (XEXP (u, 0), bb_last_function_call[bb_succ]))
- continue;
-
- bb_last_function_call[bb_succ]
- = alloc_INSN_LIST (XEXP (u, 0),
- bb_last_function_call[bb_succ]);
- }
-
- /* last_pending_memory_flush is inherited by bb_succ */
- for (u = last_pending_memory_flush; u; u = XEXP (u, 1))
- {
- if (find_insn_list (XEXP (u, 0), bb_last_pending_memory_flush[bb_succ]))
- continue;
-
- bb_last_pending_memory_flush[bb_succ]
- = alloc_INSN_LIST (XEXP (u, 0),
- bb_last_pending_memory_flush[bb_succ]);
- }
-
- /* sched_before_next_call is inherited by bb_succ */
- x = LOG_LINKS (sched_before_next_call);
- for (; x; x = XEXP (x, 1))
- add_dependence (bb_sched_before_next_call[bb_succ],
- XEXP (x, 0), REG_DEP_ANTI);
-
- e = NEXT_OUT (e);
- }
- while (e != first_edge);
- }
-
- /* Free up the INSN_LISTs
-
- Note this loop is executed max_reg * nr_regions times. It's first
- implementation accounted for over 90% of the calls to free_list.
- The list was empty for the vast majority of those calls. On the PA,
- not calling free_list in those cases improves -O2 compile times by
- 3-5% on average. */
- for (b = 0; b < max_reg; ++b)
- {
- if (reg_last_sets[b])
- free_list (&reg_last_sets[b], &unused_insn_list);
- if (reg_last_uses[b])
- free_list (&reg_last_uses[b], &unused_insn_list);
- }
-
- /* Assert that we won't need bb_reg_last_* for this block anymore. */
- if (current_nr_blocks > 1)
- {
- bb_reg_last_uses[bb] = (rtx *) NULL_RTX;
- bb_reg_last_sets[bb] = (rtx *) NULL_RTX;
- }
-}
-
-/* Print dependences for debugging, callable from debugger */
-
-void
-debug_dependencies ()
-{
- int bb;
-
- fprintf (dump, ";; --------------- forward dependences: ------------ \n");
- for (bb = 0; bb < current_nr_blocks; bb++)
- {
- if (1)
- {
- rtx head, tail;
- rtx next_tail;
- rtx insn;
-
- get_block_head_tail (bb, &head, &tail);
- next_tail = NEXT_INSN (tail);
- fprintf (dump, "\n;; --- Region Dependences --- b %d bb %d \n",
- BB_TO_BLOCK (bb), bb);
-
- fprintf (dump, ";; %7s%6s%6s%6s%6s%6s%11s%6s\n",
- "insn", "code", "bb", "dep", "prio", "cost", "blockage", "units");
- fprintf (dump, ";; %7s%6s%6s%6s%6s%6s%11s%6s\n",
- "----", "----", "--", "---", "----", "----", "--------", "-----");
- for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
- {
- rtx link;
- int unit, range;
-
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- {
- int n;
- fprintf (dump, ";; %6d ", INSN_UID (insn));
- if (GET_CODE (insn) == NOTE)
- {
- n = NOTE_LINE_NUMBER (insn);
- if (n < 0)
- fprintf (dump, "%s\n", GET_NOTE_INSN_NAME (n));
- else
- fprintf (dump, "line %d, file %s\n", n,
- NOTE_SOURCE_FILE (insn));
- }
- else
- fprintf (dump, " {%s}\n", GET_RTX_NAME (GET_CODE (insn)));
- continue;
- }
-
- unit = insn_unit (insn);
- range = (unit < 0
- || function_units[unit].blockage_range_function == 0) ? 0 :
- function_units[unit].blockage_range_function (insn);
- fprintf (dump,
- ";; %s%5d%6d%6d%6d%6d%6d %3d -%3d ",
- (SCHED_GROUP_P (insn) ? "+" : " "),
- INSN_UID (insn),
- INSN_CODE (insn),
- INSN_BB (insn),
- INSN_DEP_COUNT (insn),
- INSN_PRIORITY (insn),
- insn_cost (insn, 0, 0),
- (int) MIN_BLOCKAGE_COST (range),
- (int) MAX_BLOCKAGE_COST (range));
- insn_print_units (insn);
- fprintf (dump, "\t: ");
- for (link = INSN_DEPEND (insn); link; link = XEXP (link, 1))
- fprintf (dump, "%d ", INSN_UID (XEXP (link, 0)));
- fprintf (dump, "\n");
- }
- }
- }
- fprintf (dump, "\n");
-}
-
-/* Set_priorities: compute priority of each insn in the block */
-
-static int
-set_priorities (bb)
- int bb;
-{
- rtx insn;
- int n_insn;
-
- rtx tail;
- rtx prev_head;
- rtx head;
-
- get_block_head_tail (bb, &head, &tail);
- prev_head = PREV_INSN (head);
-
- if (head == tail
- && (GET_RTX_CLASS (GET_CODE (head)) != 'i'))
- return 0;
-
- n_insn = 0;
- for (insn = tail; insn != prev_head; insn = PREV_INSN (insn))
- {
-
- if (GET_CODE (insn) == NOTE)
- continue;
-
- if (!(SCHED_GROUP_P (insn)))
- n_insn++;
- (void) priority (insn);
- }
-
- return n_insn;
-}
-
-/* Make each element of VECTOR point at an rtx-vector,
- taking the space for all those rtx-vectors from SPACE.
- SPACE is of type (rtx *), but it is really as long as NELTS rtx-vectors.
- BYTES_PER_ELT is the number of bytes in one rtx-vector.
- (this is the same as init_regset_vector () in flow.c) */
-
-static void
-init_rtx_vector (vector, space, nelts, bytes_per_elt)
- rtx **vector;
- rtx *space;
- int nelts;
- int bytes_per_elt;
-{
- register int i;
- register rtx *p = space;
-
- for (i = 0; i < nelts; i++)
- {
- vector[i] = p;
- p += bytes_per_elt / sizeof (*p);
- }
-}
-
-/* Schedule a region. A region is either an inner loop, a loop-free
- subroutine, or a single basic block. Each bb in the region is
- scheduled after its flow predecessors. */
-
-static void
-schedule_region (rgn)
- int rgn;
-{
- int bb;
- int rgn_n_insns = 0;
- int sched_rgn_n_insns = 0;
-
- /* set variables for the current region */
- current_nr_blocks = RGN_NR_BLOCKS (rgn);
- current_blocks = RGN_BLOCKS (rgn);
-
- reg_pending_sets = ALLOCA_REG_SET ();
- reg_pending_sets_all = 0;
-
- /* initializations for region data dependence analyisis */
- if (current_nr_blocks > 1)
- {
- rtx *space;
- int maxreg = max_reg_num ();
-
- bb_reg_last_uses = (rtx **) alloca (current_nr_blocks * sizeof (rtx *));
- space = (rtx *) alloca (current_nr_blocks * maxreg * sizeof (rtx));
- bzero ((char *) space, current_nr_blocks * maxreg * sizeof (rtx));
- init_rtx_vector (bb_reg_last_uses, space, current_nr_blocks, maxreg * sizeof (rtx *));
-
- bb_reg_last_sets = (rtx **) alloca (current_nr_blocks * sizeof (rtx *));
- space = (rtx *) alloca (current_nr_blocks * maxreg * sizeof (rtx));
- bzero ((char *) space, current_nr_blocks * maxreg * sizeof (rtx));
- init_rtx_vector (bb_reg_last_sets, space, current_nr_blocks, maxreg * sizeof (rtx *));
-
- bb_pending_read_insns = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
- bb_pending_read_mems = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
- bb_pending_write_insns = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
- bb_pending_write_mems = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
- bb_pending_lists_length = (int *) alloca (current_nr_blocks * sizeof (int));
- bb_last_pending_memory_flush = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
- bb_last_function_call = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
- bb_sched_before_next_call = (rtx *) alloca (current_nr_blocks * sizeof (rtx));
-
- init_rgn_data_dependences (current_nr_blocks);
- }
-
- /* compute LOG_LINKS */
- for (bb = 0; bb < current_nr_blocks; bb++)
- compute_block_backward_dependences (bb);
-
- /* compute INSN_DEPEND */
- for (bb = current_nr_blocks - 1; bb >= 0; bb--)
- compute_block_forward_dependences (bb);
-
- /* Delete line notes, compute live-regs at block end, and set priorities. */
- dead_notes = 0;
- for (bb = 0; bb < current_nr_blocks; bb++)
- {
- if (reload_completed == 0)
- find_pre_sched_live (bb);
-
- if (write_symbols != NO_DEBUG)
- {
- save_line_notes (bb);
- rm_line_notes (bb);
- }
-
- rgn_n_insns += set_priorities (bb);
- }
-
- /* compute interblock info: probabilities, split-edges, dominators, etc. */
- if (current_nr_blocks > 1)
- {
- int i;
-
- prob = (float *) alloca ((current_nr_blocks) * sizeof (float));
-
- bbset_size = current_nr_blocks / HOST_BITS_PER_WIDE_INT + 1;
- dom = (bbset *) alloca (current_nr_blocks * sizeof (bbset));
- for (i = 0; i < current_nr_blocks; i++)
- {
- dom[i] = (bbset) alloca (bbset_size * sizeof (HOST_WIDE_INT));
- bzero ((char *) dom[i], bbset_size * sizeof (HOST_WIDE_INT));
- }
-
- /* edge to bit */
- rgn_nr_edges = 0;
- edge_to_bit = (int *) alloca (nr_edges * sizeof (int));
- for (i = 1; i < nr_edges; i++)
- if (CONTAINING_RGN (FROM_BLOCK (i)) == rgn)
- EDGE_TO_BIT (i) = rgn_nr_edges++;
- rgn_edges = (int *) alloca (rgn_nr_edges * sizeof (int));
-
- rgn_nr_edges = 0;
- for (i = 1; i < nr_edges; i++)
- if (CONTAINING_RGN (FROM_BLOCK (i)) == (rgn))
- rgn_edges[rgn_nr_edges++] = i;
-
- /* split edges */
- edgeset_size = rgn_nr_edges / HOST_BITS_PER_WIDE_INT + 1;
- pot_split = (edgeset *) alloca (current_nr_blocks * sizeof (edgeset));
- ancestor_edges = (edgeset *) alloca (current_nr_blocks * sizeof (edgeset));
- for (i = 0; i < current_nr_blocks; i++)
- {
- pot_split[i] =
- (edgeset) alloca (edgeset_size * sizeof (HOST_WIDE_INT));
- bzero ((char *) pot_split[i],
- edgeset_size * sizeof (HOST_WIDE_INT));
- ancestor_edges[i] =
- (edgeset) alloca (edgeset_size * sizeof (HOST_WIDE_INT));
- bzero ((char *) ancestor_edges[i],
- edgeset_size * sizeof (HOST_WIDE_INT));
- }
-
- /* compute probabilities, dominators, split_edges */
- for (bb = 0; bb < current_nr_blocks; bb++)
- compute_dom_prob_ps (bb);
- }
-
- /* now we can schedule all blocks */
- for (bb = 0; bb < current_nr_blocks; bb++)
- {
- sched_rgn_n_insns += schedule_block (bb, rgn_n_insns);
-
-#ifdef USE_C_ALLOCA
- alloca (0);
-#endif
- }
-
- /* sanity check: verify that all region insns were scheduled */
- if (sched_rgn_n_insns != rgn_n_insns)
- abort ();
-
- /* update register life and usage information */
- if (reload_completed == 0)
- {
- for (bb = current_nr_blocks - 1; bb >= 0; bb--)
- find_post_sched_live (bb);
-
- if (current_nr_blocks <= 1)
- /* Sanity check. There should be no REG_DEAD notes leftover at the end.
- In practice, this can occur as the result of bugs in flow, combine.c,
- and/or sched.c. The values of the REG_DEAD notes remaining are
- meaningless, because dead_notes is just used as a free list. */
- if (dead_notes != 0)
- abort ();
- }
-
- /* restore line notes. */
- if (write_symbols != NO_DEBUG)
- {
- for (bb = 0; bb < current_nr_blocks; bb++)
- restore_line_notes (bb);
- }
-
- /* Done with this region */
- free_pending_lists ();
-
- FREE_REG_SET (reg_pending_sets);
-}
-
-/* Subroutine of update_flow_info. Determines whether any new REG_NOTEs are
- needed for the hard register mentioned in the note. This can happen
- if the reference to the hard register in the original insn was split into
- several smaller hard register references in the split insns. */
-
-static void
-split_hard_reg_notes (note, first, last)
- rtx note, first, last;
-{
- rtx reg, temp, link;
- int n_regs, i, new_reg;
- rtx insn;
-
- /* Assume that this is a REG_DEAD note. */
- if (REG_NOTE_KIND (note) != REG_DEAD)
- abort ();
-
- reg = XEXP (note, 0);
-
- n_regs = HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg));
-
- for (i = 0; i < n_regs; i++)
- {
- new_reg = REGNO (reg) + i;
-
- /* Check for references to new_reg in the split insns. */
- for (insn = last;; insn = PREV_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && (temp = regno_use_in (new_reg, PATTERN (insn))))
- {
- /* Create a new reg dead note ere. */
- link = alloc_EXPR_LIST (REG_DEAD, temp, REG_NOTES (insn));
- REG_NOTES (insn) = link;
-
- /* If killed multiple registers here, then add in the excess. */
- i += HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) - 1;
-
- break;
- }
- /* It isn't mentioned anywhere, so no new reg note is needed for
- this register. */
- if (insn == first)
- break;
- }
- }
-}
-
-/* Subroutine of update_flow_info. Determines whether a SET or CLOBBER in an
- insn created by splitting needs a REG_DEAD or REG_UNUSED note added. */
-
-static void
-new_insn_dead_notes (pat, insn, last, orig_insn)
- rtx pat, insn, last, orig_insn;
-{
- rtx dest, tem, set;
-
- /* PAT is either a CLOBBER or a SET here. */
- dest = XEXP (pat, 0);
-
- while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
- || GET_CODE (dest) == STRICT_LOW_PART
- || GET_CODE (dest) == SIGN_EXTRACT)
- dest = XEXP (dest, 0);
-
- if (GET_CODE (dest) == REG)
- {
- /* If the original insn already used this register, we may not add new
- notes for it. One example for a split that needs this test is
- when a multi-word memory access with register-indirect addressing
- is split into multiple memory accesses with auto-increment and
- one adjusting add instruction for the address register. */
- if (reg_referenced_p (dest, PATTERN (orig_insn)))
- return;
- for (tem = last; tem != insn; tem = PREV_INSN (tem))
- {
- if (GET_RTX_CLASS (GET_CODE (tem)) == 'i'
- && reg_overlap_mentioned_p (dest, PATTERN (tem))
- && (set = single_set (tem)))
- {
- rtx tem_dest = SET_DEST (set);
-
- while (GET_CODE (tem_dest) == ZERO_EXTRACT
- || GET_CODE (tem_dest) == SUBREG
- || GET_CODE (tem_dest) == STRICT_LOW_PART
- || GET_CODE (tem_dest) == SIGN_EXTRACT)
- tem_dest = XEXP (tem_dest, 0);
-
- if (!rtx_equal_p (tem_dest, dest))
- {
- /* Use the same scheme as combine.c, don't put both REG_DEAD
- and REG_UNUSED notes on the same insn. */
- if (!find_regno_note (tem, REG_UNUSED, REGNO (dest))
- && !find_regno_note (tem, REG_DEAD, REGNO (dest)))
- {
- rtx note = alloc_EXPR_LIST (REG_DEAD, dest,
- REG_NOTES (tem));
- REG_NOTES (tem) = note;
- }
- /* The reg only dies in one insn, the last one that uses
- it. */
- break;
- }
- else if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
- /* We found an instruction that both uses the register,
- and sets it, so no new REG_NOTE is needed for this set. */
- break;
- }
- }
- /* If this is a set, it must die somewhere, unless it is the dest of
- the original insn, and hence is live after the original insn. Abort
- if it isn't supposed to be live after the original insn.
-
- If this is a clobber, then just add a REG_UNUSED note. */
- if (tem == insn)
- {
- int live_after_orig_insn = 0;
- rtx pattern = PATTERN (orig_insn);
- int i;
-
- if (GET_CODE (pat) == CLOBBER)
- {
- rtx note = alloc_EXPR_LIST (REG_UNUSED, dest, REG_NOTES (insn));
- REG_NOTES (insn) = note;
- return;
- }
-
- /* The original insn could have multiple sets, so search the
- insn for all sets. */
- if (GET_CODE (pattern) == SET)
- {
- if (reg_overlap_mentioned_p (dest, SET_DEST (pattern)))
- live_after_orig_insn = 1;
- }
- else if (GET_CODE (pattern) == PARALLEL)
- {
- for (i = 0; i < XVECLEN (pattern, 0); i++)
- if (GET_CODE (XVECEXP (pattern, 0, i)) == SET
- && reg_overlap_mentioned_p (dest,
- SET_DEST (XVECEXP (pattern,
- 0, i))))
- live_after_orig_insn = 1;
- }
-
- if (!live_after_orig_insn)
- abort ();
- }
- }
-}
-
-/* Subroutine of update_flow_info. Update the value of reg_n_sets for all
- registers modified by X. INC is -1 if the containing insn is being deleted,
- and is 1 if the containing insn is a newly generated insn. */
-
-static void
-update_n_sets (x, inc)
- rtx x;
- int inc;
-{
- rtx dest = SET_DEST (x);
-
- while (GET_CODE (dest) == STRICT_LOW_PART || GET_CODE (dest) == SUBREG
- || GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
- dest = SUBREG_REG (dest);
-
- if (GET_CODE (dest) == REG)
- {
- int regno = REGNO (dest);
-
- if (regno < FIRST_PSEUDO_REGISTER)
- {
- register int i;
- int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest));
-
- for (i = regno; i < endregno; i++)
- REG_N_SETS (i) += inc;
- }
- else
- REG_N_SETS (regno) += inc;
- }
-}
-
-/* Updates all flow-analysis related quantities (including REG_NOTES) for
- the insns from FIRST to LAST inclusive that were created by splitting
- ORIG_INSN. NOTES are the original REG_NOTES. */
-
-void
-update_flow_info (notes, first, last, orig_insn)
- rtx notes;
- rtx first, last;
- rtx orig_insn;
-{
- rtx insn, note;
- rtx next;
- rtx orig_dest, temp;
- rtx set;
-
- /* Get and save the destination set by the original insn. */
-
- orig_dest = single_set (orig_insn);
- if (orig_dest)
- orig_dest = SET_DEST (orig_dest);
-
- /* Move REG_NOTES from the original insn to where they now belong. */
-
- for (note = notes; note; note = next)
- {
- next = XEXP (note, 1);
- switch (REG_NOTE_KIND (note))
- {
- case REG_DEAD:
- case REG_UNUSED:
- /* Move these notes from the original insn to the last new insn where
- the register is now set. */
-
- for (insn = last;; insn = PREV_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
- {
- /* If this note refers to a multiple word hard register, it
- may have been split into several smaller hard register
- references, so handle it specially. */
- temp = XEXP (note, 0);
- if (REG_NOTE_KIND (note) == REG_DEAD
- && GET_CODE (temp) == REG
- && REGNO (temp) < FIRST_PSEUDO_REGISTER
- && HARD_REGNO_NREGS (REGNO (temp), GET_MODE (temp)) > 1)
- split_hard_reg_notes (note, first, last);
- else
- {
- XEXP (note, 1) = REG_NOTES (insn);
- REG_NOTES (insn) = note;
- }
-
- /* Sometimes need to convert REG_UNUSED notes to REG_DEAD
- notes. */
- /* ??? This won't handle multiple word registers correctly,
- but should be good enough for now. */
- if (REG_NOTE_KIND (note) == REG_UNUSED
- && GET_CODE (XEXP (note, 0)) != SCRATCH
- && !dead_or_set_p (insn, XEXP (note, 0)))
- PUT_REG_NOTE_KIND (note, REG_DEAD);
-
- /* The reg only dies in one insn, the last one that uses
- it. */
- break;
- }
- /* It must die somewhere, fail it we couldn't find where it died.
-
- If this is a REG_UNUSED note, then it must be a temporary
- register that was not needed by this instantiation of the
- pattern, so we can safely ignore it. */
- if (insn == first)
- {
- if (REG_NOTE_KIND (note) != REG_UNUSED)
- abort ();
-
- break;
- }
- }
- break;
-
- case REG_WAS_0:
- /* If the insn that set the register to 0 was deleted, this
- note cannot be relied on any longer. The destination might
- even have been moved to memory.
- This was observed for SH4 with execute/920501-6.c compilation,
- -O2 -fomit-frame-pointer -finline-functions . */
- if (GET_CODE (XEXP (note, 0)) == NOTE
- || INSN_DELETED_P (XEXP (note, 0)))
- break;
- /* This note applies to the dest of the original insn. Find the
- first new insn that now has the same dest, and move the note
- there. */
-
- if (!orig_dest)
- abort ();
-
- for (insn = first;; insn = NEXT_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && (temp = single_set (insn))
- && rtx_equal_p (SET_DEST (temp), orig_dest))
- {
- XEXP (note, 1) = REG_NOTES (insn);
- REG_NOTES (insn) = note;
- /* The reg is only zero before one insn, the first that
- uses it. */
- break;
- }
- /* If this note refers to a multiple word hard
- register, it may have been split into several smaller
- hard register references. We could split the notes,
- but simply dropping them is good enough. */
- if (GET_CODE (orig_dest) == REG
- && REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
- && HARD_REGNO_NREGS (REGNO (orig_dest),
- GET_MODE (orig_dest)) > 1)
- break;
- /* It must be set somewhere, fail if we couldn't find where it
- was set. */
- if (insn == last)
- abort ();
- }
- break;
-
- case REG_EQUAL:
- case REG_EQUIV:
- /* A REG_EQUIV or REG_EQUAL note on an insn with more than one
- set is meaningless. Just drop the note. */
- if (!orig_dest)
- break;
-
- case REG_NO_CONFLICT:
- /* These notes apply to the dest of the original insn. Find the last
- new insn that now has the same dest, and move the note there. */
-
- if (!orig_dest)
- abort ();
-
- for (insn = last;; insn = PREV_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && (temp = single_set (insn))
- && rtx_equal_p (SET_DEST (temp), orig_dest))
- {
- XEXP (note, 1) = REG_NOTES (insn);
- REG_NOTES (insn) = note;
- /* Only put this note on one of the new insns. */
- break;
- }
-
- /* The original dest must still be set someplace. Abort if we
- couldn't find it. */
- if (insn == first)
- {
- /* However, if this note refers to a multiple word hard
- register, it may have been split into several smaller
- hard register references. We could split the notes,
- but simply dropping them is good enough. */
- if (GET_CODE (orig_dest) == REG
- && REGNO (orig_dest) < FIRST_PSEUDO_REGISTER
- && HARD_REGNO_NREGS (REGNO (orig_dest),
- GET_MODE (orig_dest)) > 1)
- break;
- /* Likewise for multi-word memory references. */
- if (GET_CODE (orig_dest) == MEM
- && SIZE_FOR_MODE (orig_dest) > UNITS_PER_WORD)
- break;
- abort ();
- }
- }
- break;
-
- case REG_LIBCALL:
- /* Move a REG_LIBCALL note to the first insn created, and update
- the corresponding REG_RETVAL note. */
- XEXP (note, 1) = REG_NOTES (first);
- REG_NOTES (first) = note;
-
- insn = XEXP (note, 0);
- note = find_reg_note (insn, REG_RETVAL, NULL_RTX);
- if (note)
- XEXP (note, 0) = first;
- break;
-
- case REG_EXEC_COUNT:
- /* Move a REG_EXEC_COUNT note to the first insn created. */
- XEXP (note, 1) = REG_NOTES (first);
- REG_NOTES (first) = note;
- break;
-
- case REG_RETVAL:
- /* Move a REG_RETVAL note to the last insn created, and update
- the corresponding REG_LIBCALL note. */
- XEXP (note, 1) = REG_NOTES (last);
- REG_NOTES (last) = note;
-
- insn = XEXP (note, 0);
- note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
- if (note)
- XEXP (note, 0) = last;
- break;
-
- case REG_NONNEG:
- case REG_BR_PROB:
- /* This should be moved to whichever instruction is a JUMP_INSN. */
-
- for (insn = last;; insn = PREV_INSN (insn))
- {
- if (GET_CODE (insn) == JUMP_INSN)
- {
- XEXP (note, 1) = REG_NOTES (insn);
- REG_NOTES (insn) = note;
- /* Only put this note on one of the new insns. */
- break;
- }
- /* Fail if we couldn't find a JUMP_INSN. */
- if (insn == first)
- abort ();
- }
- break;
-
- case REG_INC:
- /* reload sometimes leaves obsolete REG_INC notes around. */
- if (reload_completed)
- break;
- /* This should be moved to whichever instruction now has the
- increment operation. */
- abort ();
-
- case REG_LABEL:
- /* Should be moved to the new insn(s) which use the label. */
- for (insn = first; insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && reg_mentioned_p (XEXP (note, 0), PATTERN (insn)))
- {
- REG_NOTES (insn) = alloc_EXPR_LIST (REG_LABEL,
- XEXP (note, 0),
- REG_NOTES (insn));
- }
- break;
-
- case REG_CC_SETTER:
- case REG_CC_USER:
- /* These two notes will never appear until after reorg, so we don't
- have to handle them here. */
- default:
- abort ();
- }
- }
-
- /* Each new insn created, except the last, has a new set. If the destination
- is a register, then this reg is now live across several insns, whereas
- previously the dest reg was born and died within the same insn. To
- reflect this, we now need a REG_DEAD note on the insn where this
- dest reg dies.
-
- Similarly, the new insns may have clobbers that need REG_UNUSED notes. */
-
- for (insn = first; insn != last; insn = NEXT_INSN (insn))
- {
- rtx pat;
- int i;
-
- pat = PATTERN (insn);
- if (GET_CODE (pat) == SET || GET_CODE (pat) == CLOBBER)
- new_insn_dead_notes (pat, insn, last, orig_insn);
- else if (GET_CODE (pat) == PARALLEL)
- {
- for (i = 0; i < XVECLEN (pat, 0); i++)
- if (GET_CODE (XVECEXP (pat, 0, i)) == SET
- || GET_CODE (XVECEXP (pat, 0, i)) == CLOBBER)
- new_insn_dead_notes (XVECEXP (pat, 0, i), insn, last, orig_insn);
- }
- }
-
- /* If any insn, except the last, uses the register set by the last insn,
- then we need a new REG_DEAD note on that insn. In this case, there
- would not have been a REG_DEAD note for this register in the original
- insn because it was used and set within one insn. */
-
- set = single_set (last);
- if (set)
- {
- rtx dest = SET_DEST (set);
-
- while (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SUBREG
- || GET_CODE (dest) == STRICT_LOW_PART
- || GET_CODE (dest) == SIGN_EXTRACT)
- dest = XEXP (dest, 0);
-
- if (GET_CODE (dest) == REG
- /* Global registers are always live, so the code below does not
- apply to them. */
- && (REGNO (dest) >= FIRST_PSEUDO_REGISTER
- || ! global_regs[REGNO (dest)]))
- {
- rtx stop_insn = PREV_INSN (first);
-
- /* If the last insn uses the register that it is setting, then
- we don't want to put a REG_DEAD note there. Search backwards
- to find the first insn that sets but does not use DEST. */
-
- insn = last;
- if (reg_overlap_mentioned_p (dest, SET_SRC (set)))
- {
- for (insn = PREV_INSN (insn); insn != first;
- insn = PREV_INSN (insn))
- {
- if ((set = single_set (insn))
- && reg_mentioned_p (dest, SET_DEST (set))
- && ! reg_overlap_mentioned_p (dest, SET_SRC (set)))
- break;
- }
- }
-
- /* Now find the first insn that uses but does not set DEST. */
-
- for (insn = PREV_INSN (insn); insn != stop_insn;
- insn = PREV_INSN (insn))
- {
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && reg_mentioned_p (dest, PATTERN (insn))
- && (set = single_set (insn)))
- {
- rtx insn_dest = SET_DEST (set);
-
- while (GET_CODE (insn_dest) == ZERO_EXTRACT
- || GET_CODE (insn_dest) == SUBREG
- || GET_CODE (insn_dest) == STRICT_LOW_PART
- || GET_CODE (insn_dest) == SIGN_EXTRACT)
- insn_dest = XEXP (insn_dest, 0);
-
- if (insn_dest != dest)
- {
- note = alloc_EXPR_LIST (REG_DEAD, dest, REG_NOTES (insn));
- REG_NOTES (insn) = note;
- /* The reg only dies in one insn, the last one
- that uses it. */
- break;
- }
- }
- }
- }
- }
-
- /* If the original dest is modifying a multiple register target, and the
- original instruction was split such that the original dest is now set
- by two or more SUBREG sets, then the split insns no longer kill the
- destination of the original insn.
-
- In this case, if there exists an instruction in the same basic block,
- before the split insn, which uses the original dest, and this use is
- killed by the original insn, then we must remove the REG_DEAD note on
- this insn, because it is now superfluous.
-
- This does not apply when a hard register gets split, because the code
- knows how to handle overlapping hard registers properly. */
- if (orig_dest && GET_CODE (orig_dest) == REG)
- {
- int found_orig_dest = 0;
- int found_split_dest = 0;
-
- for (insn = first;; insn = NEXT_INSN (insn))
- {
- rtx pat;
- int i;
-
- /* I'm not sure if this can happen, but let's be safe. */
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
-
- pat = PATTERN (insn);
- i = GET_CODE (pat) == PARALLEL ? XVECLEN (pat, 0) : 0;
- set = pat;
-
- for (;;)
- {
- if (GET_CODE (set) == SET)
- {
- if (GET_CODE (SET_DEST (set)) == REG
- && REGNO (SET_DEST (set)) == REGNO (orig_dest))
- {
- found_orig_dest = 1;
- break;
- }
- else if (GET_CODE (SET_DEST (set)) == SUBREG
- && SUBREG_REG (SET_DEST (set)) == orig_dest)
- {
- found_split_dest = 1;
- break;
- }
- }
- if (--i < 0)
- break;
- set = XVECEXP (pat, 0, i);
- }
-
- if (insn == last)
- break;
- }
-
- if (found_split_dest)
- {
- /* Search backwards from FIRST, looking for the first insn that uses
- the original dest. Stop if we pass a CODE_LABEL or a JUMP_INSN.
- If we find an insn, and it has a REG_DEAD note, then delete the
- note. */
-
- for (insn = first; insn; insn = PREV_INSN (insn))
- {
- if (GET_CODE (insn) == CODE_LABEL
- || GET_CODE (insn) == JUMP_INSN)
- break;
- else if (GET_RTX_CLASS (GET_CODE (insn)) == 'i'
- && reg_mentioned_p (orig_dest, insn))
- {
- note = find_regno_note (insn, REG_DEAD, REGNO (orig_dest));
- if (note)
- remove_note (insn, note);
- }
- }
- }
- else if (!found_orig_dest)
- {
- int i, regno;
-
- /* Should never reach here for a pseudo reg. */
- if (REGNO (orig_dest) >= FIRST_PSEUDO_REGISTER)
- abort ();
-
- /* This can happen for a hard register, if the splitter
- does not bother to emit instructions which would be no-ops.
- We try to verify that this is the case by checking to see if
- the original instruction uses all of the registers that it
- set. This case is OK, because deleting a no-op can not affect
- REG_DEAD notes on other insns. If this is not the case, then
- abort. */
-
- regno = REGNO (orig_dest);
- for (i = HARD_REGNO_NREGS (regno, GET_MODE (orig_dest)) - 1;
- i >= 0; i--)
- if (! refers_to_regno_p (regno + i, regno + i + 1, orig_insn,
- NULL_PTR))
- break;
- if (i >= 0)
- abort ();
- }
- }
-
- /* Update reg_n_sets. This is necessary to prevent local alloc from
- converting REG_EQUAL notes to REG_EQUIV when splitting has modified
- a reg from set once to set multiple times. */
-
- {
- rtx x = PATTERN (orig_insn);
- RTX_CODE code = GET_CODE (x);
-
- if (code == SET || code == CLOBBER)
- update_n_sets (x, -1);
- else if (code == PARALLEL)
- {
- int i;
- for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
- {
- code = GET_CODE (XVECEXP (x, 0, i));
- if (code == SET || code == CLOBBER)
- update_n_sets (XVECEXP (x, 0, i), -1);
- }
- }
-
- for (insn = first;; insn = NEXT_INSN (insn))
- {
- x = PATTERN (insn);
- code = GET_CODE (x);
-
- if (code == SET || code == CLOBBER)
- update_n_sets (x, 1);
- else if (code == PARALLEL)
- {
- int i;
- for (i = XVECLEN (x, 0) - 1; i >= 0; i--)
- {
- code = GET_CODE (XVECEXP (x, 0, i));
- if (code == SET || code == CLOBBER)
- update_n_sets (XVECEXP (x, 0, i), 1);
- }
- }
-
- if (insn == last)
- break;
- }
- }
-}
-
-/* The one entry point in this file. DUMP_FILE is the dump file for
- this pass. */
-
-void
-schedule_insns (dump_file)
- FILE *dump_file;
-{
-
- int max_uid;
- int b;
- rtx insn;
- int rgn;
-
- int luid;
-
- /* disable speculative loads in their presence if cc0 defined */
-#ifdef HAVE_cc0
- flag_schedule_speculative_load = 0;
-#endif
-
- /* Taking care of this degenerate case makes the rest of
- this code simpler. */
- if (n_basic_blocks == 0)
- return;
-
- /* set dump and sched_verbose for the desired debugging output. If no
- dump-file was specified, but -fsched-verbose-N (any N), print to stderr.
- For -fsched-verbose-N, N>=10, print everything to stderr. */
- sched_verbose = sched_verbose_param;
- if (sched_verbose_param == 0 && dump_file)
- sched_verbose = 1;
- dump = ((sched_verbose_param >= 10 || !dump_file) ? stderr : dump_file);
-
- nr_inter = 0;
- nr_spec = 0;
-
- /* Initialize the unused_*_lists. We can't use the ones left over from
- the previous function, because gcc has freed that memory. We can use
- the ones left over from the first sched pass in the second pass however,
- so only clear them on the first sched pass. The first pass is before
- reload if flag_schedule_insns is set, otherwise it is afterwards. */
-
- if (reload_completed == 0 || !flag_schedule_insns)
- {
- unused_insn_list = 0;
- unused_expr_list = 0;
- }
-
- /* initialize issue_rate */
- issue_rate = ISSUE_RATE;
-
- /* do the splitting first for all blocks */
- for (b = 0; b < n_basic_blocks; b++)
- split_block_insns (b, 1);
-
- max_uid = (get_max_uid () + 1);
-
- cant_move = (char *) xmalloc (max_uid * sizeof (char));
- bzero ((char *) cant_move, max_uid * sizeof (char));
-
- fed_by_spec_load = (char *) xmalloc (max_uid * sizeof (char));
- bzero ((char *) fed_by_spec_load, max_uid * sizeof (char));
-
- is_load_insn = (char *) xmalloc (max_uid * sizeof (char));
- bzero ((char *) is_load_insn, max_uid * sizeof (char));
-
- insn_orig_block = (int *) xmalloc (max_uid * sizeof (int));
- insn_luid = (int *) xmalloc (max_uid * sizeof (int));
-
- luid = 0;
- for (b = 0; b < n_basic_blocks; b++)
- for (insn = BLOCK_HEAD (b);; insn = NEXT_INSN (insn))
- {
- INSN_BLOCK (insn) = b;
- INSN_LUID (insn) = luid++;
-
- if (insn == BLOCK_END (b))
- break;
- }
-
- /* after reload, remove inter-blocks dependences computed before reload. */
- if (reload_completed)
- {
- int b;
- rtx insn;
-
- for (b = 0; b < n_basic_blocks; b++)
- for (insn = BLOCK_HEAD (b);; insn = NEXT_INSN (insn))
- {
- rtx link, prev;
-
- if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
- {
- prev = NULL_RTX;
- link = LOG_LINKS (insn);
- while (link)
- {
- rtx x = XEXP (link, 0);
-
- if (INSN_BLOCK (x) != b)
- {
- remove_dependence (insn, x);
- link = prev ? XEXP (prev, 1) : LOG_LINKS (insn);
- }
- else
- prev = link, link = XEXP (prev, 1);
- }
- }
-
- if (insn == BLOCK_END (b))
- break;
- }
- }
-
- nr_regions = 0;
- rgn_table = (region *) alloca ((n_basic_blocks) * sizeof (region));
- rgn_bb_table = (int *) alloca ((n_basic_blocks) * sizeof (int));
- block_to_bb = (int *) alloca ((n_basic_blocks) * sizeof (int));
- containing_rgn = (int *) alloca ((n_basic_blocks) * sizeof (int));
-
- /* compute regions for scheduling */
- if (reload_completed
- || n_basic_blocks == 1
- || !flag_schedule_interblock)
- {
- find_single_block_region ();
- }
- else
- {
- /* verify that a 'good' control flow graph can be built */
- if (is_cfg_nonregular ())
- {
- find_single_block_region ();
- }
- else
- {
- int_list_ptr *s_preds, *s_succs;
- int *num_preds, *num_succs;
- sbitmap *dom, *pdom;
-
- s_preds = (int_list_ptr *) alloca (n_basic_blocks
- * sizeof (int_list_ptr));
- s_succs = (int_list_ptr *) alloca (n_basic_blocks
- * sizeof (int_list_ptr));
- num_preds = (int *) alloca (n_basic_blocks * sizeof (int));
- num_succs = (int *) alloca (n_basic_blocks * sizeof (int));
- dom = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
- pdom = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
-
- /* The scheduler runs after flow; therefore, we can't blindly call
- back into find_basic_blocks since doing so could invalidate the
- info in basic_block_live_at_start.
-
- Consider a block consisting entirely of dead stores; after life
- analysis it would be a block of NOTE_INSN_DELETED notes. If
- we call find_basic_blocks again, then the block would be removed
- entirely and invalidate our the register live information.
-
- We could (should?) recompute register live information. Doing
- so may even be beneficial. */
-
- /* CYGNUS LOCAL edge_splitting/law */
- compute_preds_succs (s_preds, s_succs, num_preds, num_succs, 0);
- /* END CYGNUS LOCAL */
-
- /* Compute the dominators and post dominators. We don't currently use
- post dominators, but we should for speculative motion analysis. */
- compute_dominators (dom, pdom, s_preds, s_succs);
-
- /* build_control_flow will return nonzero if it detects unreachable
- blocks or any other irregularity with the cfg which prevents
- cross block scheduling. */
- if (build_control_flow (s_preds, s_succs, num_preds, num_succs) != 0)
- find_single_block_region ();
- else
- find_rgns (s_preds, s_succs, num_preds, num_succs, dom);
-
- if (sched_verbose >= 3)
- debug_regions ();
-
- /* For now. This will move as more and more of haifa is converted
- to using the cfg code in flow.c */
- free_bb_mem ();
- free (dom);
- free (pdom);
- }
- }
-
- /* Allocate data for this pass. See comments, above,
- for what these vectors do.
-
- We use xmalloc instead of alloca, because max_uid can be very large
- when there is a lot of function inlining. If we used alloca, we could
- exceed stack limits on some hosts for some inputs. */
- insn_priority = (int *) xmalloc (max_uid * sizeof (int));
- insn_reg_weight = (int *) xmalloc (max_uid * sizeof (int));
- insn_tick = (int *) xmalloc (max_uid * sizeof (int));
- insn_costs = (short *) xmalloc (max_uid * sizeof (short));
- insn_units = (short *) xmalloc (max_uid * sizeof (short));
- insn_blockage = (unsigned int *) xmalloc (max_uid * sizeof (unsigned int));
- insn_ref_count = (int *) xmalloc (max_uid * sizeof (int));
-
- /* Allocate for forward dependencies */
- insn_dep_count = (int *) xmalloc (max_uid * sizeof (int));
- insn_depend = (rtx *) xmalloc (max_uid * sizeof (rtx));
-
- if (reload_completed == 0)
- {
- int i;
-
- sched_reg_n_calls_crossed = (int *) alloca (max_regno * sizeof (int));
- sched_reg_live_length = (int *) alloca (max_regno * sizeof (int));
- sched_reg_basic_block = (int *) alloca (max_regno * sizeof (int));
- bb_live_regs = ALLOCA_REG_SET ();
- bzero ((char *) sched_reg_n_calls_crossed, max_regno * sizeof (int));
- bzero ((char *) sched_reg_live_length, max_regno * sizeof (int));
-
- for (i = 0; i < max_regno; i++)
- sched_reg_basic_block[i] = REG_BLOCK_UNKNOWN;
- }
- else
- {
- sched_reg_n_calls_crossed = 0;
- sched_reg_live_length = 0;
- bb_live_regs = 0;
- }
- init_alias_analysis ();
-
- if (write_symbols != NO_DEBUG)
- {
- rtx line;
-
- line_note = (rtx *) xmalloc (max_uid * sizeof (rtx));
- bzero ((char *) line_note, max_uid * sizeof (rtx));
- line_note_head = (rtx *) alloca (n_basic_blocks * sizeof (rtx));
- bzero ((char *) line_note_head, n_basic_blocks * sizeof (rtx));
-
- /* Save-line-note-head:
- Determine the line-number at the start of each basic block.
- This must be computed and saved now, because after a basic block's
- predecessor has been scheduled, it is impossible to accurately
- determine the correct line number for the first insn of the block. */
-
- for (b = 0; b < n_basic_blocks; b++)
- for (line = BLOCK_HEAD (b); line; line = PREV_INSN (line))
- if (GET_CODE (line) == NOTE && NOTE_LINE_NUMBER (line) > 0)
- {
- line_note_head[b] = line;
- break;
- }
- }
-
- bzero ((char *) insn_priority, max_uid * sizeof (int));
- bzero ((char *) insn_reg_weight, max_uid * sizeof (int));
- bzero ((char *) insn_tick, max_uid * sizeof (int));
- bzero ((char *) insn_costs, max_uid * sizeof (short));
- bzero ((char *) insn_units, max_uid * sizeof (short));
- bzero ((char *) insn_blockage, max_uid * sizeof (unsigned int));
- bzero ((char *) insn_ref_count, max_uid * sizeof (int));
-
- /* Initialize for forward dependencies */
- bzero ((char *) insn_depend, max_uid * sizeof (rtx));
- bzero ((char *) insn_dep_count, max_uid * sizeof (int));
-
- /* Find units used in this fuction, for visualization */
- if (sched_verbose)
- init_target_units ();
-
- /* ??? Add a NOTE after the last insn of the last basic block. It is not
- known why this is done. */
-
- insn = BLOCK_END (n_basic_blocks - 1);
- if (NEXT_INSN (insn) == 0
- || (GET_CODE (insn) != NOTE
- && GET_CODE (insn) != CODE_LABEL
- /* Don't emit a NOTE if it would end up between an unconditional
- jump and a BARRIER. */
- && !(GET_CODE (insn) == JUMP_INSN
- && GET_CODE (NEXT_INSN (insn)) == BARRIER)))
- emit_note_after (NOTE_INSN_DELETED, BLOCK_END (n_basic_blocks - 1));
-
- /* Schedule every region in the subroutine */
- for (rgn = 0; rgn < nr_regions; rgn++)
- {
- schedule_region (rgn);
-
-#ifdef USE_C_ALLOCA
- alloca (0);
-#endif
- }
-
- /* Reposition the prologue and epilogue notes in case we moved the
- prologue/epilogue insns. */
- if (reload_completed)
- reposition_prologue_and_epilogue_notes (get_insns ());
-
- /* delete redundant line notes. */
- if (write_symbols != NO_DEBUG)
- rm_redundant_line_notes ();
-
- /* Update information about uses of registers in the subroutine. */
- if (reload_completed == 0)
- update_reg_usage ();
-
- if (sched_verbose)
- {
- if (reload_completed == 0 && flag_schedule_interblock)
- {
- fprintf (dump, "\n;; Procedure interblock/speculative motions == %d/%d \n",
- nr_inter, nr_spec);
- }
- else
- {
- if (nr_inter > 0)
- abort ();
- }
- fprintf (dump, "\n\n");
- }
-
- free (cant_move);
- free (fed_by_spec_load);
- free (is_load_insn);
- free (insn_orig_block);
- free (insn_luid);
-
- free (insn_priority);
- free (insn_reg_weight);
- free (insn_tick);
- free (insn_costs);
- free (insn_units);
- free (insn_blockage);
- free (insn_ref_count);
-
- free (insn_dep_count);
- free (insn_depend);
-
- if (write_symbols != NO_DEBUG)
- free (line_note);
-
- if (bb_live_regs)
- FREE_REG_SET (bb_live_regs);
-
- if (edge_table)
- {
- free (edge_table);
- edge_table = NULL;
- }
-
- if (in_edges)
- {
- free (in_edges);
- in_edges = NULL;
- }
- if (out_edges)
- {
- free (out_edges);
- out_edges = NULL;
- }
-}
-#endif /* INSN_SCHEDULING */
diff --git a/gcc/integrate.c b/gcc/integrate.c
index ca3544b..8186ace 100755
--- a/gcc/integrate.c
+++ b/gcc/integrate.c
@@ -2137,15 +2137,6 @@ expand_inline_function (fndecl, parms, target, ignore, type,
? fndecl : DECL_ABSTRACT_ORIGIN (fndecl));
poplevel (0, 0, 0);
- /* Must mark the line number note after inlined functions as a repeat, so
- that the test coverage code can avoid counting the call twice. This
- just tells the code to ignore the immediately following line note, since
- there already exists a copy of this note before the expanded inline call.
- This line number note is still needed for debugging though, so we can't
- delete it. */
- if (flag_test_coverage)
- emit_note (0, NOTE_REPEATED_LINE_NUMBER);
-
emit_line_note (input_filename, lineno);
/* If the function returns a BLKmode object in a register, copy it
diff --git a/gcc/jump.c b/gcc/jump.c
index 2495ac4..2e6b289 100755
--- a/gcc/jump.c
+++ b/gcc/jump.c
@@ -1664,13 +1664,6 @@ jump_optimize (f, cross_jump, noop_moves, after_regscan)
{
rtx tem = temp;
- /* ??? Optional. Disables some optimizations, but makes
- gcov output more accurate with -O. */
- if (flag_test_coverage && !reload_completed)
- for (tem = insn; tem != temp; tem = NEXT_INSN (tem))
- if (GET_CODE (tem) == NOTE && NOTE_LINE_NUMBER (tem) > 0)
- break;
-
if (tem == temp)
{
delete_jump (insn);
@@ -3573,10 +3566,7 @@ follow_jumps (label)
if (!reload_completed)
for (tem = value; tem != insn; tem = NEXT_INSN (tem))
if (GET_CODE (tem) == NOTE
- && (NOTE_LINE_NUMBER (tem) == NOTE_INSN_LOOP_BEG
- /* ??? Optional. Disables some optimizations, but makes
- gcov output more accurate with -O. */
- || (flag_test_coverage && NOTE_LINE_NUMBER (tem) > 0)))
+ && (NOTE_LINE_NUMBER (tem) == NOTE_INSN_LOOP_BEG))
return value;
/* If we have found a cycle, make the insn jump to itself. */
@@ -3697,10 +3687,7 @@ mark_jump_label (x, insn, cross_jump)
break;
else if (! cross_jump
&& (NOTE_LINE_NUMBER (next) == NOTE_INSN_LOOP_BEG
- || NOTE_LINE_NUMBER (next) == NOTE_INSN_FUNCTION_END
- /* ??? Optional. Disables some optimizations, but
- makes gcov output more accurate with -O. */
- || (flag_test_coverage && NOTE_LINE_NUMBER (next) > 0)))
+ || NOTE_LINE_NUMBER (next) == NOTE_INSN_FUNCTION_END))
break;
}
@@ -4128,18 +4115,6 @@ invert_jump (jump, nlabel)
if (redirect_jump (jump, nlabel))
{
- if (flag_branch_probabilities)
- {
- rtx note = find_reg_note (jump, REG_BR_PROB, 0);
-
- /* An inverted jump means that a probability taken becomes a
- probability not taken. Subtract the branch probability from the
- probability base to convert it back to a taken probability.
- (We don't flip the probability on a branch that's never taken. */
- if (note && XINT (XEXP (note, 0), 0) >= 0)
- XINT (XEXP (note, 0), 0) = REG_BR_PROB_BASE - XINT (XEXP (note, 0), 0);
- }
-
return 1;
}
diff --git a/gcc/profile.c b/gcc/profile.c
deleted file mode 100755
index 12221eb..0000000
--- a/gcc/profile.c
+++ /dev/null
@@ -1,1701 +0,0 @@
-/* Calculate branch probabilities, and basic block execution counts.
- Copyright (C) 1990, 91-94, 96, 97, 1998 Free Software Foundation, Inc.
- Contributed by James E. Wilson, UC Berkeley/Cygnus Support;
- based on some ideas from Dain Samples of UC Berkeley.
- Further mangling by Bob Manson, Cygnus Support.
-
-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 Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
-
-/* ??? Really should not put insns inside of LIBCALL sequences, when putting
- insns after a call, should look for the insn setting the retval, and
- insert the insns after that one. */
-
-/* ??? Register allocation should use basic block execution counts to
- give preference to the most commonly executed blocks. */
-
-/* ??? The .da files are not safe. Changing the program after creating .da
- files or using different options when compiling with -fbranch-probabilities
- can result the arc data not matching the program. Maybe add instrumented
- arc count to .bbg file? Maybe check whether PFG matches the .bbg file? */
-
-/* ??? Should calculate branch probabilities before instrumenting code, since
- then we can use arc counts to help decide which arcs to instrument. */
-
-/* ??? Rearrange code so that the most frequently executed arcs become from
- one block to the next block (i.e. a fall through), move seldom executed
- code outside of loops even at the expense of adding a few branches to
- achieve this, see Dain Sample's UC Berkeley thesis. */
-
-#include "config.h"
-#include "system.h"
-#include "rtl.h"
-#include "flags.h"
-#include "insn-flags.h"
-#include "insn-config.h"
-#include "output.h"
-#include "regs.h"
-#include "tree.h"
-#include "output.h"
-#include "gcov-io.h"
-#include "toplev.h"
-
-/* One of these is dynamically created whenever we identify an arc in the
- function. */
-
-struct adj_list
-{
- int source;
- int target;
- int arc_count;
- unsigned int count_valid : 1;
- unsigned int on_tree : 1;
- unsigned int fake : 1;
- unsigned int fall_through : 1;
- rtx branch_insn;
- struct adj_list *pred_next;
- struct adj_list *succ_next;
-};
-
-#define ARC_TARGET(ARCPTR) (ARCPTR->target)
-#define ARC_SOURCE(ARCPTR) (ARCPTR->source)
-#define ARC_COUNT(ARCPTR) (ARCPTR->arc_count)
-
-/* Count the number of basic blocks, and create an array of these structures,
- one for each bb in the function. */
-
-struct bb_info
-{
- struct adj_list *succ;
- struct adj_list *pred;
- int succ_count;
- int pred_count;
- int exec_count;
- unsigned int count_valid : 1;
- unsigned int on_tree : 1;
- rtx first_insn;
-};
-
-/* Indexed by label number, gives the basic block number containing that
- label. */
-
-static int *label_to_bb;
-
-/* Number of valid entries in the label_to_bb array. */
-
-static int label_to_bb_size;
-
-/* Indexed by block index, holds the basic block graph. */
-
-static struct bb_info *bb_graph;
-
-/* Name and file pointer of the output file for the basic block graph. */
-
-static char *bbg_file_name;
-static FILE *bbg_file;
-
-/* Name and file pointer of the input file for the arc count data. */
-
-static char *da_file_name;
-static FILE *da_file;
-
-/* Pointer of the output file for the basic block/line number map. */
-static FILE *bb_file;
-
-/* Last source file name written to bb_file. */
-
-static char *last_bb_file_name;
-
-/* Indicates whether the next line number note should be output to
- bb_file or not. Used to eliminate a redundant note after an
- expanded inline function call. */
-
-static int ignore_next_note;
-
-/* Used by final, for allocating the proper amount of storage for the
- instrumented arc execution counts. */
-
-int count_instrumented_arcs;
-
-/* Number of executions for the return label. */
-
-int return_label_execution_count;
-
-/* Collect statistics on the performance of this pass for the entire source
- file. */
-
-static int total_num_blocks;
-static int total_num_arcs;
-static int total_num_arcs_instrumented;
-static int total_num_blocks_created;
-static int total_num_passes;
-static int total_num_times_called;
-static int total_hist_br_prob[20];
-static int total_num_never_executed;
-static int total_num_branches;
-
-/* Forward declarations. */
-static void init_arc PROTO((struct adj_list *, int, int, rtx));
-static void find_spanning_tree PROTO((int));
-static void expand_spanning_tree PROTO((int));
-static void fill_spanning_tree PROTO((int));
-static void init_arc_profiler PROTO((void));
-static void output_arc_profiler PROTO((int, rtx));
-
-#ifndef LONG_TYPE_SIZE
-#define LONG_TYPE_SIZE BITS_PER_WORD
-#endif
-
-/* If non-zero, we need to output a constructor to set up the
- per-object-file data. */
-static int need_func_profiler = 0;
-
-
-/* Add arc instrumentation code to the entire insn chain.
-
- F is the first insn of the chain.
- NUM_BLOCKS is the number of basic blocks found in F.
- DUMP_FILE, if nonzero, is an rtl dump file we can write to. */
-
-static void
-instrument_arcs (f, num_blocks, dump_file)
- rtx f;
- int num_blocks;
- FILE *dump_file;
-{
- register int i;
- register struct adj_list *arcptr, *backptr;
- int num_arcs = 0;
- int num_instr_arcs = 0;
- rtx insn;
-
- /* Instrument the program start. */
- /* Handle block 0 specially, since it will always be instrumented,
- but it doesn't have a valid first_insn or branch_insn. We must
- put the instructions before the NOTE_INSN_FUNCTION_BEG note, so
- that they don't clobber any of the parameters of the current
- function. */
- for (insn = f; insn; insn = NEXT_INSN (insn))
- if (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_FUNCTION_BEG)
- break;
- insn = PREV_INSN (insn);
- need_func_profiler = 1;
- output_arc_profiler (total_num_arcs_instrumented + num_instr_arcs++, insn);
-
- for (i = 1; i < num_blocks; i++)
- for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
- if (! arcptr->on_tree)
- {
- if (dump_file)
- fprintf (dump_file, "Arc %d to %d instrumented\n", i,
- ARC_TARGET (arcptr));
-
- /* Check to see if this arc is the only exit from its source block,
- or the only entrance to its target block. In either case,
- we don't need to create a new block to instrument the arc. */
-
- if (bb_graph[i].succ == arcptr && arcptr->succ_next == 0)
- {
- /* Instrument the source block. */
- output_arc_profiler (total_num_arcs_instrumented
- + num_instr_arcs++,
- PREV_INSN (bb_graph[i].first_insn));
- }
- else if (arcptr == bb_graph[ARC_TARGET (arcptr)].pred
- && arcptr->pred_next == 0)
- {
- /* Instrument the target block. */
- output_arc_profiler (total_num_arcs_instrumented
- + num_instr_arcs++,
- PREV_INSN (bb_graph[ARC_TARGET (arcptr)].first_insn));
- }
- else if (arcptr->fall_through)
- {
- /* This is a fall-through; put the instrumentation code after
- the branch that ends this block. */
-
- for (backptr = bb_graph[i].succ; backptr;
- backptr = backptr->succ_next)
- if (backptr != arcptr)
- break;
-
- output_arc_profiler (total_num_arcs_instrumented
- + num_instr_arcs++,
- backptr->branch_insn);
- }
- else
- {
- /* Must emit a new basic block to hold the arc counting code. */
- enum rtx_code code = GET_CODE (PATTERN (arcptr->branch_insn));
-
- if (code == SET)
- {
- /* Create the new basic block right after the branch.
- Invert the branch so that it jumps past the end of the new
- block. The new block will consist of the instrumentation
- code, and a jump to the target of this arc. */
- int this_is_simplejump = simplejump_p (arcptr->branch_insn);
- rtx new_label = gen_label_rtx ();
- rtx old_label, set_src;
- rtx after = arcptr->branch_insn;
-
- /* Simplejumps can't reach here. */
- if (this_is_simplejump)
- abort ();
-
- /* We can't use JUMP_LABEL, because it won't be set if we
- are compiling without optimization. */
-
- set_src = SET_SRC (single_set (arcptr->branch_insn));
- if (GET_CODE (set_src) == LABEL_REF)
- old_label = set_src;
- else if (GET_CODE (set_src) != IF_THEN_ELSE)
- abort ();
- else if (XEXP (set_src, 1) == pc_rtx)
- old_label = XEXP (XEXP (set_src, 2), 0);
- else
- old_label = XEXP (XEXP (set_src, 1), 0);
-
- /* Set the JUMP_LABEL so that redirect_jump will work. */
- JUMP_LABEL (arcptr->branch_insn) = old_label;
-
- /* Add a use for OLD_LABEL that will be needed when we emit
- the JUMP_INSN below. If we don't do this here,
- `invert_jump' might delete it for us. We must add two
- when not optimizing, because the NUSES is zero now,
- but must be at least two to prevent the label from being
- deleted. */
- LABEL_NUSES (old_label) += 2;
-
- /* Emit the insns for the new block in reverse order,
- since that is most convenient. */
-
- if (this_is_simplejump)
- {
- after = NEXT_INSN (arcptr->branch_insn);
- if (! redirect_jump (arcptr->branch_insn, new_label))
- /* Don't know what to do if this branch won't
- redirect. */
- abort ();
- }
- else
- {
- if (! invert_jump (arcptr->branch_insn, new_label))
- /* Don't know what to do if this branch won't invert. */
- abort ();
-
- emit_label_after (new_label, after);
- LABEL_NUSES (new_label)++;
- }
- emit_barrier_after (after);
- emit_jump_insn_after (gen_jump (old_label), after);
- JUMP_LABEL (NEXT_INSN (after)) = old_label;
-
- /* Instrument the source arc. */
- output_arc_profiler (total_num_arcs_instrumented
- + num_instr_arcs++,
- after);
- if (this_is_simplejump)
- {
- emit_label_after (new_label, after);
- LABEL_NUSES (new_label)++;
- }
- }
- else if (code == ADDR_VEC || code == ADDR_DIFF_VEC)
- {
- /* A table jump. Create a new basic block immediately
- after the table, by emitting a barrier, a label, a
- counting note, and a jump to the old label. Put the
- new label in the table. */
-
- rtx new_label = gen_label_rtx ();
- rtx old_lref, new_lref;
- int index;
-
- /* Must determine the old_label reference, do this
- by counting the arcs after this one, which will
- give the index of our label in the table. */
-
- index = 0;
- for (backptr = arcptr->succ_next; backptr;
- backptr = backptr->succ_next)
- index++;
-
- old_lref = XVECEXP (PATTERN (arcptr->branch_insn),
- (code == ADDR_DIFF_VEC), index);
-
- /* Emit the insns for the new block in reverse order,
- since that is most convenient. */
- emit_jump_insn_after (gen_jump (XEXP (old_lref, 0)),
- arcptr->branch_insn);
- JUMP_LABEL (NEXT_INSN (arcptr->branch_insn))
- = XEXP (old_lref, 0);
-
- /* Instrument the source arc. */
- output_arc_profiler (total_num_arcs_instrumented
- + num_instr_arcs++,
- arcptr->branch_insn);
-
- emit_label_after (new_label, arcptr->branch_insn);
- LABEL_NUSES (NEXT_INSN (arcptr->branch_insn))++;
- emit_barrier_after (arcptr->branch_insn);
-
- /* Fix up the table jump. */
- new_lref = gen_rtx_LABEL_REF (Pmode, new_label);
- XVECEXP (PATTERN (arcptr->branch_insn),
- (code == ADDR_DIFF_VEC), index) = new_lref;
- }
- else
- abort ();
-
- num_arcs += 1;
- if (dump_file)
- fprintf (dump_file,
- "Arc %d to %d needed new basic block\n", i,
- ARC_TARGET (arcptr));
- }
- }
-
- total_num_arcs_instrumented += num_instr_arcs;
- count_instrumented_arcs = total_num_arcs_instrumented;
-
- total_num_blocks_created += num_arcs;
- if (dump_file)
- {
- fprintf (dump_file, "%d arcs instrumented\n", num_instr_arcs);
- fprintf (dump_file, "%d extra basic blocks created\n", num_arcs);
- }
-}
-
-/* Output STRING to bb_file, surrounded by DELIMITER. */
-
-static void
-output_gcov_string (string, delimiter)
- char *string;
- long delimiter;
-{
- long temp;
-
- /* Write a delimiter to indicate that a file name follows. */
- __write_long (delimiter, bb_file, 4);
-
- /* Write the string. */
- temp = strlen (string) + 1;
- fwrite (string, temp, 1, bb_file);
-
- /* Append a few zeros, to align the output to a 4 byte boundary. */
- temp = temp & 0x3;
- if (temp)
- {
- char c[4];
-
- c[0] = c[1] = c[2] = c[3] = 0;
- fwrite (c, sizeof (char), 4 - temp, bb_file);
- }
-
- /* Store another delimiter in the .bb file, just to make it easy to find the
- end of the file name. */
- __write_long (delimiter, bb_file, 4);
-}
-
-/* Return TRUE if this insn must be a tablejump entry insn. This works for
- the MIPS port, but may give false negatives for some targets. */
-
-int
-tablejump_entry_p (insn, label)
- rtx insn, label;
-{
- rtx next = next_active_insn (insn);
- enum rtx_code code = GET_CODE (PATTERN (next));
-
- if (code != ADDR_DIFF_VEC && code != ADDR_VEC)
- return 0;
-
- if (PREV_INSN (next) == XEXP (label, 0))
- return 1;
-
- return 0;
-}
-
-/* Instrument and/or analyze program behavior based on program flow graph.
- In either case, this function builds a flow graph for the function being
- compiled. The flow graph is stored in BB_GRAPH.
-
- When FLAG_PROFILE_ARCS is nonzero, this function instruments the arcs in
- the flow graph that are needed to reconstruct the dynamic behavior of the
- flow graph.
-
- When FLAG_BRANCH_PROBABILITIES is nonzero, this function reads auxiliary
- information from a data file containing arc count information from previous
- executions of the function being compiled. In this case, the flow graph is
- annotated with actual execution counts, which are later propagated into the
- rtl for optimization purposes.
-
- Main entry point of this file. */
-
-void
-branch_prob (f, dump_file)
- rtx f;
- FILE *dump_file;
-{
- int i, num_blocks;
- struct adj_list *arcptr;
- int num_arcs, changes, passes;
- int total, prob;
- int hist_br_prob[20], num_never_executed, num_branches;
- /* Set to non-zero if we got bad count information. */
- int bad_counts = 0;
-
- /* start of a function. */
- if (flag_test_coverage)
- output_gcov_string (current_function_name, (long) -2);
-
- /* Execute this only if doing arc profiling or branch probabilities. */
- if (! profile_arc_flag && ! flag_branch_probabilities
- && ! flag_test_coverage)
- abort ();
-
- total_num_times_called++;
-
- /* Create an array label_to_bb of ints of size max_label_num. */
- label_to_bb_size = max_label_num ();
- label_to_bb = (int *) oballoc (label_to_bb_size * sizeof (int));
- bzero ((char *) label_to_bb, label_to_bb_size * sizeof (int));
-
- /* Scan the insns in the function, count the number of basic blocks
- present. When a code label is passed, set label_to_bb[label] = bb
- number. */
-
- /* The first block found will be block 1, so that function entry can be
- block 0. */
-
- {
- register RTX_CODE prev_code = JUMP_INSN;
- register RTX_CODE code;
- register rtx insn;
- register int i;
- int block_separator_emitted = 0;
-
- ignore_next_note = 0;
-
- for (insn = NEXT_INSN (f), i = 0; insn; insn = NEXT_INSN (insn))
- {
- code = GET_CODE (insn);
-
- if (code == BARRIER)
- ;
- else if (code == CODE_LABEL)
- /* This label is part of the next block, but we can't increment
- block number yet since there might be multiple labels. */
- label_to_bb[CODE_LABEL_NUMBER (insn)] = i + 1;
- /* We make NOTE_INSN_SETJMP notes into a block of their own, so that
- they can be the target of the fake arc for the setjmp call.
- This avoids creating cycles of fake arcs, which would happen if
- the block after the setjmp call contained a call insn. */
- else if ((prev_code == JUMP_INSN || prev_code == CALL_INSN
- || prev_code == CODE_LABEL || prev_code == BARRIER)
- && (GET_RTX_CLASS (code) == 'i'
- || (code == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)))
- {
- i += 1;
-
- /* Emit the block separator if it hasn't already been emitted. */
- if (flag_test_coverage && ! block_separator_emitted)
- {
- /* Output a zero to the .bb file to indicate that a new
- block list is starting. */
- __write_long (0, bb_file, 4);
- }
- block_separator_emitted = 0;
- }
- /* If flag_test_coverage is true, then we must add an entry to the
- .bb file for every note. */
- else if (code == NOTE && flag_test_coverage)
- {
- /* Must ignore the line number notes that immediately follow the
- end of an inline function to avoid counting it twice. There
- is a note before the call, and one after the call. */
- if (NOTE_LINE_NUMBER (insn) == NOTE_REPEATED_LINE_NUMBER)
- ignore_next_note = 1;
- else if (NOTE_LINE_NUMBER (insn) > 0)
- {
- if (ignore_next_note)
- ignore_next_note = 0;
- else
- {
- /* Emit a block separator here to ensure that a NOTE
- immediately following a JUMP_INSN or CALL_INSN will end
- up in the right basic block list. */
- if ((prev_code == JUMP_INSN || prev_code == CALL_INSN
- || prev_code == CODE_LABEL || prev_code == BARRIER)
- && ! block_separator_emitted)
- {
- /* Output a zero to the .bb file to indicate that
- a new block list is starting. */
- __write_long (0, bb_file, 4);
-
- block_separator_emitted = 1;
- }
-
- /* If this is a new source file, then output the file's
- name to the .bb file. */
- if (! last_bb_file_name
- || strcmp (NOTE_SOURCE_FILE (insn),
- last_bb_file_name))
- {
- if (last_bb_file_name)
- free (last_bb_file_name);
- last_bb_file_name
- = xmalloc (strlen (NOTE_SOURCE_FILE (insn)) + 1);
- strcpy (last_bb_file_name, NOTE_SOURCE_FILE (insn));
- output_gcov_string (NOTE_SOURCE_FILE (insn), (long)-1);
- }
-
- /* Output the line number to the .bb file. Must be done
- after the output_bb_profile_data() call, and after the
- file name is written, to ensure that it is correctly
- handled by gcov. */
- __write_long (NOTE_LINE_NUMBER (insn), bb_file, 4);
- }
- }
- }
-
- if (code != NOTE)
- prev_code = code;
- else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
- prev_code = CALL_INSN;
- }
-
- /* Allocate last `normal' entry for bb_graph. */
-
- /* The last insn was a jump, call, or label. In that case we have
- a block at the end of the function with no insns. */
- if (prev_code == JUMP_INSN || prev_code == CALL_INSN
- || prev_code == CODE_LABEL || prev_code == BARRIER)
- {
- i++;
-
- /* Emit the block separator if it hasn't already been emitted. */
- if (flag_test_coverage && ! block_separator_emitted)
- {
- /* Output a zero to the .bb file to indicate that a new
- block list is starting. */
- __write_long (0, bb_file, 4);
- }
- }
-
- /* Create another block to stand for EXIT, and make all return insns, and
- the last basic block point here. Add one more to account for block
- zero. */
- num_blocks = i + 2;
- }
-
- total_num_blocks += num_blocks;
- if (dump_file)
- fprintf (dump_file, "%d basic blocks\n", num_blocks);
-
- /* If we are only doing test coverage here, then return now. */
- if (! profile_arc_flag && ! flag_branch_probabilities)
- return;
-
- /* Create and initialize the arrays that will hold bb_graph
- and execution count info. */
-
- bb_graph = (struct bb_info *) alloca (num_blocks * sizeof (struct bb_info));
- bzero ((char *) bb_graph, (sizeof (struct bb_info) * num_blocks));
-
- {
- /* Scan the insns again:
- - at the entry to each basic block, increment the predecessor count
- (and successor of previous block) if it is a fall through entry,
- create adj_list entries for this and the previous block
- - at each jump insn, increment predecessor/successor counts for
- target/source basic blocks, add this insn to pred/succ lists.
-
- This also cannot be broken out as a separate subroutine
- because it uses `alloca'. */
-
- register RTX_CODE prev_code = JUMP_INSN;
- register RTX_CODE code;
- register rtx insn;
- register int i;
- int fall_through = 0;
- struct adj_list *arcptr;
- int dest = 0;
-
- /* Block 0 always falls through to block 1. */
- num_arcs = 0;
- arcptr = (struct adj_list *) alloca (sizeof (struct adj_list));
- init_arc (arcptr, 0, 1, 0);
- arcptr->fall_through = 1;
- num_arcs++;
-
- /* Add a fake fall through arc from the last block to block 0, to make the
- graph complete. */
- arcptr = (struct adj_list *) alloca (sizeof (struct adj_list));
- init_arc (arcptr, num_blocks - 1, 0, 0);
- arcptr->fake = 1;
- num_arcs++;
-
- /* Exit must be one node of the graph, and all exits from the function
- must point there. When see a return branch, must point the arc to the
- exit node. */
-
- /* Must start scan with second insn in function as above. */
- for (insn = NEXT_INSN (f), i = 0; insn; insn = NEXT_INSN (insn))
- {
- code = GET_CODE (insn);
-
- if (code == BARRIER)
- fall_through = 0;
- else if (code == CODE_LABEL)
- ;
- /* We make NOTE_INSN_SETJMP notes into a block of their own, so that
- they can be the target of the fake arc for the setjmp call.
- This avoids creating cycles of fake arcs, which would happen if
- the block after the setjmp call ended with a call. */
- else if ((prev_code == JUMP_INSN || prev_code == CALL_INSN
- || prev_code == CODE_LABEL || prev_code == BARRIER)
- && (GET_RTX_CLASS (code) == 'i'
- || (code == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)))
- {
- /* This is the first insn of the block. */
- i += 1;
- if (fall_through)
- {
- arcptr = (struct adj_list *) alloca (sizeof (struct adj_list));
- init_arc (arcptr, i - 1, i, 0);
- arcptr->fall_through = 1;
-
- num_arcs++;
- }
- fall_through = 1;
- bb_graph[i].first_insn = insn;
- }
- else if (code == NOTE)
- {;}
-
- if (code == CALL_INSN)
- {
- /* In the normal case, the call returns, and this is just like
- a branch fall through. */
- fall_through = 1;
-
- /* Setjmp may return more times than called, so to make the graph
- solvable, add a fake arc from the function entrance to the
- next block.
-
- All other functions may return fewer times than called (if
- a descendent call longjmp or exit), so to make the graph
- solvable, add a fake arc to the function exit from the
- current block.
-
- Distinguish the cases by checking for a SETJUMP note.
- A call_insn can be the last ins of a function, so must check
- to see if next insn actually exists. */
- arcptr = (struct adj_list *) alloca (sizeof (struct adj_list));
- if (NEXT_INSN (insn)
- && GET_CODE (NEXT_INSN (insn)) == NOTE
- && NOTE_LINE_NUMBER (NEXT_INSN (insn)) == NOTE_INSN_SETJMP)
- init_arc (arcptr, 0, i+1, insn);
- else
- init_arc (arcptr, i, num_blocks-1, insn);
- arcptr->fake = 1;
- num_arcs++;
- }
- else if (code == JUMP_INSN)
- {
- rtx tem, pattern = PATTERN (insn);
- rtx tablejump = 0;
-
- /* If running without optimization, then jump label won't be valid,
- so we must search for the destination label in that case.
- We have to handle tablejumps and returns specially anyways, so
- we don't check the JUMP_LABEL at all here. */
-
- /* ??? This code should be rewritten. We need a more elegant way
- to find the LABEL_REF. We need a more elegant way to
- differentiate tablejump entries from computed gotos.
- We should perhaps reuse code from flow to compute the CFG
- instead of trying to compute it here.
-
- We can't use current_function_has_computed_jump, because that
- is calculated later by flow. We can't use computed_jump_p,
- because that returns true for tablejump entry insns for some
- targets, e.g. HPPA and MIPS. */
-
- if (GET_CODE (pattern) == PARALLEL)
- {
- /* This assumes that PARALLEL jumps with a USE are
- tablejump entry jumps. The same assumption can be found
- in computed_jump_p. */
- /* Make an arc from this jump to the label of the
- jump table. This will instrument the number of
- times the switch statement is executed. */
- if (GET_CODE (XVECEXP (pattern, 0, 1)) == USE)
- {
- tem = XEXP (XVECEXP (pattern, 0, 1), 0);
- if (GET_CODE (tem) != LABEL_REF)
- abort ();
- dest = label_to_bb[CODE_LABEL_NUMBER (XEXP (tem, 0))];
- }
- else if (GET_CODE (XVECEXP (pattern, 0, 0)) == SET
- && SET_DEST (XVECEXP (pattern, 0, 0)) == pc_rtx)
- {
- tem = SET_SRC (XVECEXP (pattern, 0, 0));
- if (GET_CODE (tem) == PLUS
- && GET_CODE (XEXP (tem, 1)) == LABEL_REF)
- {
- tem = XEXP (tem, 1);
- dest = label_to_bb [CODE_LABEL_NUMBER (XEXP (tem, 0))];
- }
- }
- else
- abort ();
- }
- else if (GET_CODE (pattern) == ADDR_VEC
- || GET_CODE (pattern) == ADDR_DIFF_VEC)
- tablejump = pattern;
- else if (GET_CODE (pattern) == RETURN)
- dest = num_blocks - 1;
- else if (GET_CODE (pattern) != SET)
- abort ();
- else if ((tem = SET_SRC (pattern))
- && GET_CODE (tem) == LABEL_REF)
- dest = label_to_bb[CODE_LABEL_NUMBER (XEXP (tem, 0))];
- /* Recognize HPPA table jump entry. This code is similar to
- the code above in the PARALLEL case. */
- else if (GET_CODE (tem) == PLUS
- && GET_CODE (XEXP (tem, 0)) == MEM
- && GET_CODE (XEXP (XEXP (tem, 0), 0)) == PLUS
- && GET_CODE (XEXP (XEXP (XEXP (tem, 0), 0), 0)) == PC
- && GET_CODE (XEXP (tem, 1)) == LABEL_REF
- && tablejump_entry_p (insn, XEXP (tem, 1)))
- dest = label_to_bb[CODE_LABEL_NUMBER (XEXP (XEXP (tem, 1), 0))];
- /* Recognize the MIPS table jump entry. */
- else if (GET_CODE (tem) == PLUS
- && GET_CODE (XEXP (tem, 0)) == REG
- && GET_CODE (XEXP (tem, 1)) == LABEL_REF
- && tablejump_entry_p (insn, XEXP (tem, 1)))
- dest = label_to_bb[CODE_LABEL_NUMBER (XEXP (XEXP (tem, 1), 0))];
- else
- {
- rtx label_ref;
-
- /* Must be an IF_THEN_ELSE branch. If it isn't, assume it
- is a computed goto, which aren't supported yet. */
- if (GET_CODE (tem) != IF_THEN_ELSE)
- fatal ("-fprofile-arcs does not support computed gotos");
- if (XEXP (tem, 1) != pc_rtx)
- label_ref = XEXP (tem, 1);
- else
- label_ref = XEXP (tem, 2);
- dest = label_to_bb[CODE_LABEL_NUMBER (XEXP (label_ref, 0))];
- }
-
- if (tablejump)
- {
- int diff_vec_p = GET_CODE (tablejump) == ADDR_DIFF_VEC;
- int len = XVECLEN (tablejump, diff_vec_p);
- int k;
-
- for (k = 0; k < len; k++)
- {
- rtx tem = XEXP (XVECEXP (tablejump, diff_vec_p, k), 0);
- dest = label_to_bb[CODE_LABEL_NUMBER (tem)];
-
- arcptr = (struct adj_list *) alloca (sizeof(struct adj_list));
- init_arc (arcptr, i, dest, insn);
-
- num_arcs++;
- }
- }
- else
- {
- arcptr = (struct adj_list *) alloca (sizeof (struct adj_list));
- init_arc (arcptr, i, dest, insn);
-
- num_arcs++;
- }
-
- /* Determine whether or not this jump will fall through.
- Unconditional jumps and returns are not always followed by
- barriers. */
- pattern = PATTERN (insn);
- if (GET_CODE (pattern) == PARALLEL
- || GET_CODE (pattern) == RETURN)
- fall_through = 0;
- else if (GET_CODE (pattern) == ADDR_VEC
- || GET_CODE (pattern) == ADDR_DIFF_VEC)
- /* These aren't actually jump insns, but they never fall
- through, so... */
- fall_through = 0;
- else
- {
- if (GET_CODE (pattern) != SET || SET_DEST (pattern) != pc_rtx)
- abort ();
- if (GET_CODE (SET_SRC (pattern)) != IF_THEN_ELSE)
- fall_through = 0;
- }
- }
-
- if (code != NOTE)
- prev_code = code;
- else if (NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
- {
- /* Make a fake insn to tag our notes on. */
- bb_graph[i].first_insn = insn
- = emit_insn_after (gen_rtx_USE (VOIDmode, stack_pointer_rtx),
- insn);
- prev_code = CALL_INSN;
- }
- }
-
- /* If the code at the end of the function would give a new block, then
- do the following. */
-
- if (prev_code == JUMP_INSN || prev_code == CALL_INSN
- || prev_code == CODE_LABEL || prev_code == BARRIER)
- {
- if (fall_through)
- {
- arcptr = (struct adj_list *) alloca (sizeof (struct adj_list));
- init_arc (arcptr, i, i + 1, 0);
- arcptr->fall_through = 1;
-
- num_arcs++;
- }
-
- /* This may not be a real insn, but that should not cause a problem. */
- bb_graph[i+1].first_insn = get_last_insn ();
- }
-
- /* There is always a fake arc from the last block of the function
- to the function exit block. */
- arcptr = (struct adj_list *) alloca (sizeof (struct adj_list));
- init_arc (arcptr, num_blocks-2, num_blocks-1, 0);
- arcptr->fake = 1;
- num_arcs++;
- }
-
- total_num_arcs += num_arcs;
- if (dump_file)
- fprintf (dump_file, "%d arcs\n", num_arcs);
-
- /* Create spanning tree from basic block graph, mark each arc that is
- on the spanning tree. */
-
- /* To reduce the instrumentation cost, make two passes over the tree.
- First, put as many must-split (crowded and fake) arcs on the tree as
- possible, then on the second pass fill in the rest of the tree.
- Note that the spanning tree is considered undirected, so that as many
- must-split arcs as possible can be put on it.
-
- Fallthrough arcs which are crowded should not be chosen on the first
- pass, since they do not require creating a new basic block. These
- arcs will have fall_through set. */
-
- find_spanning_tree (num_blocks);
-
- /* Create a .bbg file from which gcov can reconstruct the basic block
- graph. First output the number of basic blocks, and then for every
- arc output the source and target basic block numbers.
- NOTE: The format of this file must be compatible with gcov. */
-
- if (flag_test_coverage)
- {
- int flag_bits;
-
- __write_long (num_blocks, bbg_file, 4);
- __write_long (num_arcs, bbg_file, 4);
-
- for (i = 0; i < num_blocks; i++)
- {
- long count = 0;
- for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
- count++;
- __write_long (count, bbg_file, 4);
-
- for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
- {
- flag_bits = 0;
- if (arcptr->on_tree)
- flag_bits |= 0x1;
- if (arcptr->fake)
- flag_bits |= 0x2;
- if (arcptr->fall_through)
- flag_bits |= 0x4;
-
- __write_long (ARC_TARGET (arcptr), bbg_file, 4);
- __write_long (flag_bits, bbg_file, 4);
- }
- }
-
- /* Emit a -1 to separate the list of all arcs from the list of
- loop back edges that follows. */
- __write_long (-1, bbg_file, 4);
- }
-
- /* For each arc not on the spanning tree, add counting code as rtl. */
-
- if (profile_arc_flag)
- {
- instrument_arcs (f, num_blocks, dump_file);
- allocate_reg_info (max_reg_num (), FALSE, FALSE);
- }
-
- /* Execute the rest only if doing branch probabilities. */
- if (! flag_branch_probabilities)
- return;
-
- /* For each arc not on the spanning tree, set its execution count from
- the .da file. */
-
- /* The first count in the .da file is the number of times that the function
- was entered. This is the exec_count for block zero. */
-
- num_arcs = 0;
- for (i = 0; i < num_blocks; i++)
- for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
- if (! arcptr->on_tree)
- {
- num_arcs++;
- if (da_file)
- {
- long value;
- __read_long (&value, da_file, 8);
- ARC_COUNT (arcptr) = value;
- }
- else
- ARC_COUNT (arcptr) = 0;
- arcptr->count_valid = 1;
- bb_graph[i].succ_count--;
- bb_graph[ARC_TARGET (arcptr)].pred_count--;
- }
-
- if (dump_file)
- fprintf (dump_file, "%d arc counts read\n", num_arcs);
-
- /* For every block in the file,
- - if every exit/entrance arc has a known count, then set the block count
- - if the block count is known, and every exit/entrance arc but one has
- a known execution count, then set the count of the remaining arc
-
- As arc counts are set, decrement the succ/pred count, but don't delete
- the arc, that way we can easily tell when all arcs are known, or only
- one arc is unknown. */
-
- /* The order that the basic blocks are iterated through is important.
- Since the code that finds spanning trees starts with block 0, low numbered
- arcs are put on the spanning tree in preference to high numbered arcs.
- Hence, most instrumented arcs are at the end. Graph solving works much
- faster if we propagate numbers from the end to the start.
-
- This takes an average of slightly more than 3 passes. */
-
- changes = 1;
- passes = 0;
- while (changes)
- {
- passes++;
- changes = 0;
-
- for (i = num_blocks - 1; i >= 0; i--)
- {
- struct bb_info *binfo = &bb_graph[i];
- if (! binfo->count_valid)
- {
- if (binfo->succ_count == 0)
- {
- total = 0;
- for (arcptr = binfo->succ; arcptr;
- arcptr = arcptr->succ_next)
- total += ARC_COUNT (arcptr);
- binfo->exec_count = total;
- binfo->count_valid = 1;
- changes = 1;
- }
- else if (binfo->pred_count == 0)
- {
- total = 0;
- for (arcptr = binfo->pred; arcptr;
- arcptr = arcptr->pred_next)
- total += ARC_COUNT (arcptr);
- binfo->exec_count = total;
- binfo->count_valid = 1;
- changes = 1;
- }
- }
- if (binfo->count_valid)
- {
- if (binfo->succ_count == 1)
- {
- total = 0;
- /* One of the counts will be invalid, but it is zero,
- so adding it in also doesn't hurt. */
- for (arcptr = binfo->succ; arcptr;
- arcptr = arcptr->succ_next)
- total += ARC_COUNT (arcptr);
- /* Calculate count for remaining arc by conservation. */
- total = binfo->exec_count - total;
- /* Search for the invalid arc, and set its count. */
- for (arcptr = binfo->succ; arcptr;
- arcptr = arcptr->succ_next)
- if (! arcptr->count_valid)
- break;
- if (! arcptr)
- abort ();
- arcptr->count_valid = 1;
- ARC_COUNT (arcptr) = total;
- binfo->succ_count--;
-
- bb_graph[ARC_TARGET (arcptr)].pred_count--;
- changes = 1;
- }
- if (binfo->pred_count == 1)
- {
- total = 0;
- /* One of the counts will be invalid, but it is zero,
- so adding it in also doesn't hurt. */
- for (arcptr = binfo->pred; arcptr;
- arcptr = arcptr->pred_next)
- total += ARC_COUNT (arcptr);
- /* Calculate count for remaining arc by conservation. */
- total = binfo->exec_count - total;
- /* Search for the invalid arc, and set its count. */
- for (arcptr = binfo->pred; arcptr;
- arcptr = arcptr->pred_next)
- if (! arcptr->count_valid)
- break;
- if (! arcptr)
- abort ();
- arcptr->count_valid = 1;
- ARC_COUNT (arcptr) = total;
- binfo->pred_count--;
-
- bb_graph[ARC_SOURCE (arcptr)].succ_count--;
- changes = 1;
- }
- }
- }
- }
-
- total_num_passes += passes;
- if (dump_file)
- fprintf (dump_file, "Graph solving took %d passes.\n\n", passes);
-
- /* If the graph has been correctly solved, every block will have a
- succ and pred count of zero. */
- for (i = 0; i < num_blocks; i++)
- {
- struct bb_info *binfo = &bb_graph[i];
- if (binfo->succ_count || binfo->pred_count)
- abort ();
- }
-
- /* For every arc, calculate its branch probability and add a reg_note
- to the branch insn to indicate this. */
-
- for (i = 0; i < 20; i++)
- hist_br_prob[i] = 0;
- num_never_executed = 0;
- num_branches = 0;
-
- for (i = 0; i < num_blocks; i++)
- {
- struct bb_info *binfo = &bb_graph[i];
-
- total = binfo->exec_count;
- for (arcptr = binfo->succ; arcptr; arcptr = arcptr->succ_next)
- {
- if (arcptr->branch_insn)
- {
- /* This calculates the branch probability as an integer between
- 0 and REG_BR_PROB_BASE, properly rounded to the nearest
- integer. Perform the arithmetic in double to avoid
- overflowing the range of ints. */
-
- if (total == 0)
- prob = -1;
- else
- {
- rtx pat = PATTERN (arcptr->branch_insn);
-
- prob = (((double)ARC_COUNT (arcptr) * REG_BR_PROB_BASE)
- + (total >> 1)) / total;
- if (prob < 0 || prob > REG_BR_PROB_BASE)
- {
- if (dump_file)
- fprintf (dump_file, "bad count: prob for %d-%d thought to be %d (forcibly normalized)\n",
- ARC_SOURCE (arcptr), ARC_TARGET (arcptr),
- prob);
-
- bad_counts = 1;
- prob = REG_BR_PROB_BASE / 2;
- }
-
- /* Match up probability with JUMP pattern. */
-
- if (GET_CODE (pat) == SET
- && GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
- {
- if (ARC_TARGET (arcptr) == ARC_SOURCE (arcptr) + 1)
- {
- /* A fall through arc should never have a
- branch insn. */
- abort ();
- }
- else
- {
- /* This is the arc for the taken branch. */
- if (GET_CODE (XEXP (SET_SRC (pat), 2)) != PC)
- prob = REG_BR_PROB_BASE - prob;
- }
- }
- }
-
- if (prob == -1)
- num_never_executed++;
- else
- {
- int index = prob * 20 / REG_BR_PROB_BASE;
- if (index == 20)
- index = 19;
- hist_br_prob[index]++;
- }
- num_branches++;
-
- REG_NOTES (arcptr->branch_insn)
- = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (prob),
- REG_NOTES (arcptr->branch_insn));
- }
- }
-
- /* Add a REG_EXEC_COUNT note to the first instruction of this block. */
- if (! binfo->first_insn
- || GET_RTX_CLASS (GET_CODE (binfo->first_insn)) != 'i')
- {
- /* Block 0 is a fake block representing function entry, and does
- not have a real first insn. The second last block might not
- begin with a real insn. */
- if (i == num_blocks - 1)
- return_label_execution_count = total;
- else if (i != 0 && i != num_blocks - 2)
- abort ();
- }
- else
- {
- REG_NOTES (binfo->first_insn)
- = gen_rtx_EXPR_LIST (REG_EXEC_COUNT, GEN_INT (total),
- REG_NOTES (binfo->first_insn));
- if (i == num_blocks - 1)
- return_label_execution_count = total;
- }
- }
-
- /* This should never happen. */
- if (bad_counts)
- warning ("Arc profiling: some arc counts were bad.");
-
- if (dump_file)
- {
- fprintf (dump_file, "%d branches\n", num_branches);
- fprintf (dump_file, "%d branches never executed\n",
- num_never_executed);
- if (num_branches)
- for (i = 0; i < 10; i++)
- fprintf (dump_file, "%d%% branches in range %d-%d%%\n",
- (hist_br_prob[i]+hist_br_prob[19-i])*100/num_branches,
- 5*i, 5*i+5);
-
- total_num_branches += num_branches;
- total_num_never_executed += num_never_executed;
- for (i = 0; i < 20; i++)
- total_hist_br_prob[i] += hist_br_prob[i];
- }
-
-}
-
-/* Initialize a new arc.
- ARCPTR is the empty adj_list this function fills in.
- SOURCE is the block number of the source block.
- TARGET is the block number of the target block.
- INSN is the insn which transfers control from SOURCE to TARGET,
- or zero if the transfer is implicit. */
-
-static void
-init_arc (arcptr, source, target, insn)
- struct adj_list *arcptr;
- int source, target;
- rtx insn;
-{
- ARC_TARGET (arcptr) = target;
- ARC_SOURCE (arcptr) = source;
-
- ARC_COUNT (arcptr) = 0;
- arcptr->count_valid = 0;
- arcptr->on_tree = 0;
- arcptr->fake = 0;
- arcptr->fall_through = 0;
- arcptr->branch_insn = insn;
-
- arcptr->succ_next = bb_graph[source].succ;
- bb_graph[source].succ = arcptr;
- bb_graph[source].succ_count++;
-
- arcptr->pred_next = bb_graph[target].pred;
- bb_graph[target].pred = arcptr;
- bb_graph[target].pred_count++;
-}
-
-/* This function searches all of the arcs in the program flow graph, and puts
- as many bad arcs as possible onto the spanning tree. Bad arcs include
- fake arcs (needed for setjmp(), longjmp(), exit()) which MUST be on the
- spanning tree as they can't be instrumented. Also, arcs which must be
- split when instrumented should be part of the spanning tree if possible. */
-
-static void
-find_spanning_tree (num_blocks)
- int num_blocks;
-{
- int i;
- struct adj_list *arcptr;
- struct bb_info *binfo = &bb_graph[0];
-
- /* Fake arcs must be part of the spanning tree, and are always safe to put
- on the spanning tree. Fake arcs will either be a successor of node 0,
- a predecessor of the last node, or from the last node to node 0. */
-
- for (arcptr = bb_graph[0].succ; arcptr; arcptr = arcptr->succ_next)
- if (arcptr->fake)
- {
- /* Adding this arc should never cause a cycle. This is a fatal
- error if it would. */
- if (bb_graph[ARC_TARGET (arcptr)].on_tree && binfo->on_tree)
- abort();
- else
- {
- arcptr->on_tree = 1;
- bb_graph[ARC_TARGET (arcptr)].on_tree = 1;
- binfo->on_tree = 1;
- }
- }
-
- binfo = &bb_graph[num_blocks-1];
- for (arcptr = binfo->pred; arcptr; arcptr = arcptr->pred_next)
- if (arcptr->fake)
- {
- /* Adding this arc should never cause a cycle. This is a fatal
- error if it would. */
- if (bb_graph[ARC_SOURCE (arcptr)].on_tree && binfo->on_tree)
- abort();
- else
- {
- arcptr->on_tree = 1;
- bb_graph[ARC_SOURCE (arcptr)].on_tree = 1;
- binfo->on_tree = 1;
- }
- }
- /* The only entrace to node zero is a fake arc. */
- bb_graph[0].pred->on_tree = 1;
-
- /* Arcs which are crowded at both the source and target should be put on
- the spanning tree if possible, except for fall_throuch arcs which never
- require adding a new block even if crowded, add arcs with the same source
- and dest which must always be instrumented. */
- for (i = 0; i < num_blocks; i++)
- {
- binfo = &bb_graph[i];
-
- for (arcptr = binfo->succ; arcptr; arcptr = arcptr->succ_next)
- if (! ((binfo->succ == arcptr && arcptr->succ_next == 0)
- || (bb_graph[ARC_TARGET (arcptr)].pred
- && arcptr->pred_next == 0))
- && ! arcptr->fall_through
- && ARC_TARGET (arcptr) != i)
- {
- /* This is a crowded arc at both source and target. Try to put
- in on the spanning tree. Can do this if either the source or
- target block is not yet on the tree. */
- if (! bb_graph[ARC_TARGET (arcptr)].on_tree || ! binfo->on_tree)
- {
- arcptr->on_tree = 1;
- bb_graph[ARC_TARGET (arcptr)].on_tree = 1;
- binfo->on_tree = 1;
- }
- }
- }
-
- /* Clear all of the basic block on_tree bits, so that we can use them to
- create the spanning tree. */
- for (i = 0; i < num_blocks; i++)
- bb_graph[i].on_tree = 0;
-
- /* Now fill in the spanning tree until every basic block is on it.
- Don't put the 0 to 1 fall through arc on the tree, since it is
- always cheap to instrument, so start filling the tree from node 1. */
-
- for (i = 1; i < num_blocks; i++)
- for (arcptr = bb_graph[i].succ; arcptr; arcptr = arcptr->succ_next)
- if (! arcptr->on_tree
- && ! bb_graph[ARC_TARGET (arcptr)].on_tree)
- {
- fill_spanning_tree (i);
- break;
- }
-}
-
-/* Add arcs reached from BLOCK to the spanning tree if they are needed and
- not already there. */
-
-static void
-fill_spanning_tree (block)
- int block;
-{
- struct adj_list *arcptr;
-
- expand_spanning_tree (block);
-
- for (arcptr = bb_graph[block].succ; arcptr; arcptr = arcptr->succ_next)
- if (! arcptr->on_tree
- && ! bb_graph[ARC_TARGET (arcptr)].on_tree)
- {
- arcptr->on_tree = 1;
- fill_spanning_tree (ARC_TARGET (arcptr));
- }
-}
-
-/* When first visit a block, must add all blocks that are already connected
- to this block via tree arcs to the spanning tree. */
-
-static void
-expand_spanning_tree (block)
- int block;
-{
- struct adj_list *arcptr;
-
- bb_graph[block].on_tree = 1;
-
- for (arcptr = bb_graph[block].succ; arcptr; arcptr = arcptr->succ_next)
- if (arcptr->on_tree && ! bb_graph[ARC_TARGET (arcptr)].on_tree)
- expand_spanning_tree (ARC_TARGET (arcptr));
-
- for (arcptr = bb_graph[block].pred;
- arcptr; arcptr = arcptr->pred_next)
- if (arcptr->on_tree && ! bb_graph[ARC_SOURCE (arcptr)].on_tree)
- expand_spanning_tree (ARC_SOURCE (arcptr));
-}
-
-/* Perform file-level initialization for branch-prob processing. */
-
-void
-init_branch_prob (filename)
- char *filename;
-{
- long len;
- int i;
-
- if (flag_test_coverage)
- {
- /* Open an output file for the basic block/line number map. */
- int len = strlen (filename);
- char *data_file = (char *) alloca (len + 4);
- strcpy (data_file, filename);
- strip_off_ending (data_file, len);
- strcat (data_file, ".bb");
- if ((bb_file = fopen (data_file, "w")) == 0)
- pfatal_with_name (data_file);
-
- /* Open an output file for the program flow graph. */
- len = strlen (filename);
- bbg_file_name = (char *) alloca (len + 5);
- strcpy (bbg_file_name, filename);
- strip_off_ending (bbg_file_name, len);
- strcat (bbg_file_name, ".bbg");
- if ((bbg_file = fopen (bbg_file_name, "w")) == 0)
- pfatal_with_name (bbg_file_name);
-
- /* Initialize to zero, to ensure that the first file name will be
- written to the .bb file. */
- last_bb_file_name = 0;
- }
-
- if (flag_branch_probabilities)
- {
- len = strlen (filename);
- da_file_name = (char *) alloca (len + 4);
- strcpy (da_file_name, filename);
- strip_off_ending (da_file_name, len);
- strcat (da_file_name, ".da");
- if ((da_file = fopen (da_file_name, "r")) == 0)
- warning ("file %s not found, execution counts assumed to be zero.",
- da_file_name);
-
- /* The first word in the .da file gives the number of instrumented arcs,
- which is not needed for our purposes. */
-
- if (da_file)
- __read_long (&len, da_file, 8);
- }
-
- if (profile_arc_flag)
- init_arc_profiler ();
-
- total_num_blocks = 0;
- total_num_arcs = 0;
- total_num_arcs_instrumented = 0;
- total_num_blocks_created = 0;
- total_num_passes = 0;
- total_num_times_called = 0;
- total_num_branches = 0;
- total_num_never_executed = 0;
- for (i = 0; i < 20; i++)
- total_hist_br_prob[i] = 0;
-}
-
-/* Performs file-level cleanup after branch-prob processing
- is completed. */
-
-void
-end_branch_prob (dump_file)
- FILE *dump_file;
-{
- if (flag_test_coverage)
- {
- fclose (bb_file);
- fclose (bbg_file);
- }
-
- if (flag_branch_probabilities)
- {
- if (da_file)
- {
- long temp;
- /* This seems slightly dangerous, as it presumes the EOF
- flag will not be set until an attempt is made to read
- past the end of the file. */
- if (feof (da_file))
- warning (".da file contents exhausted too early\n");
- /* Should be at end of file now. */
- if (__read_long (&temp, da_file, 8) == 0)
- warning (".da file contents not exhausted\n");
- fclose (da_file);
- }
- }
-
- if (dump_file)
- {
- fprintf (dump_file, "\n");
- fprintf (dump_file, "Total number of blocks: %d\n", total_num_blocks);
- fprintf (dump_file, "Total number of arcs: %d\n", total_num_arcs);
- fprintf (dump_file, "Total number of instrumented arcs: %d\n",
- total_num_arcs_instrumented);
- fprintf (dump_file, "Total number of blocks created: %d\n",
- total_num_blocks_created);
- fprintf (dump_file, "Total number of graph solution passes: %d\n",
- total_num_passes);
- if (total_num_times_called != 0)
- fprintf (dump_file, "Average number of graph solution passes: %d\n",
- (total_num_passes + (total_num_times_called >> 1))
- / total_num_times_called);
- fprintf (dump_file, "Total number of branches: %d\n", total_num_branches);
- fprintf (dump_file, "Total number of branches never executed: %d\n",
- total_num_never_executed);
- if (total_num_branches)
- {
- int i;
-
- for (i = 0; i < 10; i++)
- fprintf (dump_file, "%d%% branches in range %d-%d%%\n",
- (total_hist_br_prob[i] + total_hist_br_prob[19-i]) * 100
- / total_num_branches, 5*i, 5*i+5);
- }
- }
-}
-
-/* The label used by the arc profiling code. */
-
-static rtx profiler_label;
-
-/* Initialize the profiler_label. */
-
-static void
-init_arc_profiler ()
-{
- /* Generate and save a copy of this so it can be shared. */
- char *name = xmalloc (20);
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 2);
- profiler_label = gen_rtx_SYMBOL_REF (Pmode, name);
-}
-
-/* Output instructions as RTL to increment the arc execution count. */
-
-static void
-output_arc_profiler (arcno, insert_after)
- int arcno;
- rtx insert_after;
-{
- rtx profiler_target_addr
- = (arcno
- ? gen_rtx_CONST (Pmode,
- gen_rtx_PLUS (Pmode, profiler_label,
- GEN_INT (LONG_TYPE_SIZE / BITS_PER_UNIT * arcno)))
- : profiler_label);
- enum machine_mode mode = mode_for_size (LONG_TYPE_SIZE, MODE_INT, 0);
- rtx profiler_reg = gen_reg_rtx (mode);
- rtx address_reg = gen_reg_rtx (Pmode);
- rtx mem_ref, add_ref;
- rtx sequence;
-
- /* In this case, reload can use explicitly mentioned hard registers for
- reloads. It is not safe to output profiling code between a call
- and the instruction that copies the result to a pseudo-reg. This
- is because reload may allocate one of the profiling code pseudo-regs
- to the return value reg, thus clobbering the return value. So we
- must check for calls here, and emit the profiling code after the
- instruction that uses the return value, if any.
-
- ??? The code here performs the same tests that reload does so hopefully
- all the bases are covered. */
-
- if (SMALL_REGISTER_CLASSES
- && GET_CODE (insert_after) == CALL_INSN
- && (GET_CODE (PATTERN (insert_after)) == SET
- || (GET_CODE (PATTERN (insert_after)) == PARALLEL
- && GET_CODE (XVECEXP (PATTERN (insert_after), 0, 0)) == SET)))
- {
- rtx return_reg;
- rtx next_insert_after = next_nonnote_insn (insert_after);
-
- /* The first insn after the call may be a stack pop, skip it. */
- if (next_insert_after
- && GET_CODE (next_insert_after) == INSN
- && GET_CODE (PATTERN (next_insert_after)) == SET
- && SET_DEST (PATTERN (next_insert_after)) == stack_pointer_rtx)
- next_insert_after = next_nonnote_insn (next_insert_after);
-
- if (next_insert_after
- && GET_CODE (next_insert_after) == INSN)
- {
- if (GET_CODE (PATTERN (insert_after)) == SET)
- return_reg = SET_DEST (PATTERN (insert_after));
- else
- return_reg = SET_DEST (XVECEXP (PATTERN (insert_after), 0, 0));
-
- /* Now, NEXT_INSERT_AFTER may be an instruction that uses the
- return value. However, it could also be something else,
- like a CODE_LABEL, so check that the code is INSN. */
- if (next_insert_after != 0
- && GET_RTX_CLASS (GET_CODE (next_insert_after)) == 'i'
- && reg_referenced_p (return_reg, PATTERN (next_insert_after)))
- insert_after = next_insert_after;
- }
- }
-
- start_sequence ();
-
- emit_move_insn (address_reg, profiler_target_addr);
- mem_ref = gen_rtx_MEM (mode, address_reg);
- emit_move_insn (profiler_reg, mem_ref);
-
- add_ref = gen_rtx_PLUS (mode, profiler_reg, GEN_INT (1));
- emit_move_insn (profiler_reg, add_ref);
-
- /* This is the same rtx as above, but it is not legal to share this rtx. */
- mem_ref = gen_rtx_MEM (mode, address_reg);
- emit_move_insn (mem_ref, profiler_reg);
-
- sequence = gen_sequence ();
- end_sequence ();
- emit_insn_after (sequence, insert_after);
-}
-
-/* Output code for a constructor that will invoke __bb_init_func, if
- this has not already been done. */
-
-void
-output_func_start_profiler ()
-{
- tree fnname, fndecl;
- char *name, *cfnname;
- rtx table_address;
- enum machine_mode mode = mode_for_size (LONG_TYPE_SIZE, MODE_INT, 0);
- int save_flag_inline_functions = flag_inline_functions;
-
- /* It's either already been output, or we don't need it because we're
- not doing profile-arcs. */
- if (! need_func_profiler)
- return;
-
- need_func_profiler = 0;
-
- /* Synthesize a constructor function to invoke __bb_init_func with a
- pointer to this object file's profile block. */
- start_sequence ();
-
- /* Try and make a unique name given the "file function name".
-
- And no, I don't like this either. */
-
- fnname = get_file_function_name ('I');
- cfnname = IDENTIFIER_POINTER (fnname);
- name = xmalloc (strlen (cfnname) + 5);
- sprintf (name, "%sGCOV",cfnname);
- fnname = get_identifier (name);
- free (name);
-
- fndecl = build_decl (FUNCTION_DECL, fnname,
- build_function_type (void_type_node, NULL_TREE));
- DECL_EXTERNAL (fndecl) = 0;
- TREE_PUBLIC (fndecl) = 1;
- DECL_ASSEMBLER_NAME (fndecl) = fnname;
- DECL_RESULT (fndecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
- current_function_decl = fndecl;
- pushlevel (0);
- make_function_rtl (fndecl);
- init_function_start (fndecl, input_filename, lineno);
- expand_function_start (fndecl, 0);
-
- /* Actually generate the code to call __bb_init_func. */
- name = xmalloc (20);
- ASM_GENERATE_INTERNAL_LABEL (name, "LPBX", 0);
- table_address = force_reg (Pmode, gen_rtx_SYMBOL_REF (Pmode, name));
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__bb_init_func"), 0,
- mode, 1, table_address, Pmode);
-
- expand_function_end (input_filename, lineno, 0);
- poplevel (1, 0, 1);
-
- /* Since fndecl isn't in the list of globals, it would never be emitted
- when it's considered to be 'safe' for inlining, so turn off
- flag_inline_functions. */
- flag_inline_functions = 0;
-
- rest_of_compilation (fndecl);
-
- /* Reset flag_inline_functions to its original value. */
- flag_inline_functions = save_flag_inline_functions;
-
- if (! quiet_flag)
- fflush (asm_out_file);
- current_function_decl = NULL_TREE;
-
- assemble_constructor (IDENTIFIER_POINTER (DECL_NAME (fndecl)));
-}
diff --git a/gcc/reorg.c b/gcc/reorg.c
index f9b2cc2..36e4b66 100755
--- a/gcc/reorg.c
+++ b/gcc/reorg.c
@@ -906,26 +906,6 @@ mostly_true_jump (jump_insn, condition)
return -1;
/* END CYGNUS LOCAL -- branch prediction */
- /* If branch probabilities are available, then use that number since it
- always gives a correct answer. */
- if (flag_branch_probabilities)
- {
- rtx note = find_reg_note (jump_insn, REG_BR_PROB, 0);
- if (note)
- {
- int prob = XINT (note, 0);
-
- if (prob >= REG_BR_PROB_BASE * 9 / 10)
- return 2;
- else if (prob >= REG_BR_PROB_BASE / 2)
- return 1;
- else if (prob >= REG_BR_PROB_BASE / 10)
- return 0;
- else
- return -1;
- }
- }
-
/* If this is a branch outside a loop, it is highly unlikely. */
if (GET_CODE (PATTERN (jump_insn)) == SET
&& GET_CODE (SET_SRC (PATTERN (jump_insn))) == IF_THEN_ELSE
diff --git a/gcc/rtl.c b/gcc/rtl.c
index 33cf0cb..6ff5002 100755
--- a/gcc/rtl.c
+++ b/gcc/rtl.c
@@ -190,8 +190,8 @@ char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0",
"REG_EQUAL", "REG_RETVAL", "REG_LIBCALL",
"REG_NONNEG", "REG_NO_CONFLICT", "REG_UNUSED",
"REG_CC_SETTER", "REG_CC_USER", "REG_LABEL",
- "REG_DEP_ANTI", "REG_DEP_OUTPUT", "REG_BR_PROB",
- "REG_EXEC_COUNT", "REG_NOALIAS", "REG_SAVE_AREA",
+ "REG_DEP_ANTI", "REG_DEP_OUTPUT",
+ "REG_NOALIAS", "REG_SAVE_AREA",
"REG_BR_PRED", "REG_EH_CONTEXT",
"REG_FRAME_RELATED_EXPR", "REG_EH_REGION",
"REG_EH_RETHROW" };
diff --git a/gcc/rtl.h b/gcc/rtl.h
index fc77ae1..eed0476 100755
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -331,14 +331,7 @@ typedef struct rtvec_def{
dependencies. REG_DEP_OUTPUT is used in LOG_LINKS which represent output
(write after write) dependencies. Data dependencies, which are the only
type of LOG_LINK created by flow, are represented by a 0 reg note kind. */
-/* REG_BR_PROB is attached to JUMP_INSNs and CALL_INSNs when the flag
- -fbranch-probabilities is given. It has an integer value. For jumps,
- it is the probability that this is a taken branch. For calls, it is the
- probability that this call won't return.
- REG_EXEC_COUNT is attached to the first insn of each basic block, and
- the first insn after each CALL_INSN. It indicates how many times this
- block was executed.
- REG_SAVE_AREA is used to optimize rtl generated by dynamic stack
+/* REG_SAVE_AREA is used to optimize rtl generated by dynamic stack
allocations for targets where SETJMP_VIA_SAVE_AREA is true.
REG_BR_PRED is attached to JUMP_INSNs only, it holds the branch prediction
flags computed by get_jump_flags() after dbr scheduling is complete.
@@ -363,13 +356,11 @@ enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4,
REG_EQUAL = 5, REG_RETVAL = 6, REG_LIBCALL = 7,
REG_NONNEG = 8, REG_NO_CONFLICT = 9, REG_UNUSED = 10,
REG_CC_SETTER = 11, REG_CC_USER = 12, REG_LABEL = 13,
- REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15, REG_BR_PROB = 16,
- REG_EXEC_COUNT = 17, REG_NOALIAS = 18, REG_SAVE_AREA = 19,
- REG_BR_PRED = 20, REG_EH_CONTEXT = 21,
- REG_FRAME_RELATED_EXPR = 22, REG_EH_REGION = 23,
- REG_EH_RETHROW = 24 };
-/* The base value for branch probability notes. */
-#define REG_BR_PROB_BASE 10000
+ REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15,
+ REG_NOALIAS = 16, REG_SAVE_AREA = 17,
+ REG_BR_PRED = 18, REG_EH_CONTEXT = 19,
+ REG_FRAME_RELATED_EXPR = 20, REG_EH_REGION = 21,
+ REG_EH_RETHROW = 22 };
/* Define macros to extract and insert the reg-note kind in an EXPR_LIST. */
#define REG_NOTE_KIND(LINK) ((enum reg_note) GET_MODE (LINK))
@@ -1512,14 +1503,6 @@ extern int reload PROTO ((rtx, int, FILE *));
/* In caller-save.c */
extern void init_caller_save PROTO ((void));
-/* In profile.c */
-extern void init_branch_prob PROTO ((char *));
-#ifdef BUFSIZ
-extern void branch_prob PROTO ((rtx, FILE *));
-extern void end_branch_prob PROTO ((FILE *));
-#endif
-extern void output_func_start_profiler PROTO ((void));
-
/* In reg-stack.c */
#ifdef BUFSIZ
extern void reg_to_stack PROTO ((rtx, FILE *));
diff --git a/gcc/rtl_020422.c b/gcc/rtl_020422.c
index 775525e..291c157 100755
--- a/gcc/rtl_020422.c
+++ b/gcc/rtl_020422.c
@@ -191,8 +191,8 @@ char *reg_note_name[] = { "", "REG_DEAD", "REG_INC", "REG_EQUIV", "REG_WAS_0",
"REG_EQUAL", "REG_RETVAL", "REG_LIBCALL",
"REG_NONNEG", "REG_NO_CONFLICT", "REG_UNUSED",
"REG_CC_SETTER", "REG_CC_USER", "REG_LABEL",
- "REG_DEP_ANTI", "REG_DEP_OUTPUT", "REG_BR_PROB",
- "REG_EXEC_COUNT", "REG_NOALIAS", "REG_SAVE_AREA",
+ "REG_DEP_ANTI", "REG_DEP_OUTPUT",
+ "REG_NOALIAS", "REG_SAVE_AREA",
"REG_BR_PRED", "REG_EH_CONTEXT",
"REG_FRAME_RELATED_EXPR", "REG_EH_REGION",
"REG_EH_RETHROW" };
diff --git a/gcc/rtl_020422.h b/gcc/rtl_020422.h
index d4cb617..5e006a8 100755
--- a/gcc/rtl_020422.h
+++ b/gcc/rtl_020422.h
@@ -331,14 +331,7 @@ typedef struct rtvec_def{
dependencies. REG_DEP_OUTPUT is used in LOG_LINKS which represent output
(write after write) dependencies. Data dependencies, which are the only
type of LOG_LINK created by flow, are represented by a 0 reg note kind. */
-/* REG_BR_PROB is attached to JUMP_INSNs and CALL_INSNs when the flag
- -fbranch-probabilities is given. It has an integer value. For jumps,
- it is the probability that this is a taken branch. For calls, it is the
- probability that this call won't return.
- REG_EXEC_COUNT is attached to the first insn of each basic block, and
- the first insn after each CALL_INSN. It indicates how many times this
- block was executed.
- REG_SAVE_AREA is used to optimize rtl generated by dynamic stack
+/* REG_SAVE_AREA is used to optimize rtl generated by dynamic stack
allocations for targets where SETJMP_VIA_SAVE_AREA is true.
REG_BR_PRED is attached to JUMP_INSNs only, it holds the branch prediction
flags computed by get_jump_flags() after dbr scheduling is complete.
@@ -363,13 +356,11 @@ enum reg_note { REG_DEAD = 1, REG_INC = 2, REG_EQUIV = 3, REG_WAS_0 = 4,
REG_EQUAL = 5, REG_RETVAL = 6, REG_LIBCALL = 7,
REG_NONNEG = 8, REG_NO_CONFLICT = 9, REG_UNUSED = 10,
REG_CC_SETTER = 11, REG_CC_USER = 12, REG_LABEL = 13,
- REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15, REG_BR_PROB = 16,
- REG_EXEC_COUNT = 17, REG_NOALIAS = 18, REG_SAVE_AREA = 19,
- REG_BR_PRED = 20, REG_EH_CONTEXT = 21,
- REG_FRAME_RELATED_EXPR = 22, REG_EH_REGION = 23,
- REG_EH_RETHROW = 24 };
-/* The base value for branch probability notes. */
-#define REG_BR_PROB_BASE 10000
+ REG_DEP_ANTI = 14, REG_DEP_OUTPUT = 15,
+ REG_NOALIAS = 16, REG_SAVE_AREA = 17,
+ REG_BR_PRED = 18, REG_EH_CONTEXT = 19,
+ REG_FRAME_RELATED_EXPR = 20, REG_EH_REGION = 21,
+ REG_EH_RETHROW = 22 };
/* Define macros to extract and insert the reg-note kind in an EXPR_LIST. */
#define REG_NOTE_KIND(LINK) ((enum reg_note) GET_MODE (LINK))
@@ -1513,14 +1504,6 @@ extern int reload PROTO ((rtx, int, FILE *));
/* In caller-save.c */
extern void init_caller_save PROTO ((void));
-/* In profile.c */
-extern void init_branch_prob PROTO ((char *));
-#ifdef BUFSIZ
-extern void branch_prob PROTO ((rtx, FILE *));
-extern void end_branch_prob PROTO ((FILE *));
-#endif
-extern void output_func_start_profiler PROTO ((void));
-
/* In reg-stack.c */
#ifdef BUFSIZ
extern void reg_to_stack PROTO ((rtx, FILE *));
diff --git a/gcc/sched.c b/gcc/sched.c
index a8bcaf7..27a5096 100755
--- a/gcc/sched.c
+++ b/gcc/sched.c
@@ -3906,12 +3906,6 @@ update_flow_info (notes, first, last, orig_insn)
XEXP (note, 0) = first;
break;
- case REG_EXEC_COUNT:
- /* Move a REG_EXEC_COUNT note to the first insn created. */
- XEXP (note, 1) = REG_NOTES (first);
- REG_NOTES (first) = note;
- break;
-
case REG_RETVAL:
/* Move a REG_RETVAL note to the last insn created, and update
the corresponding REG_LIBCALL note. */
@@ -3925,7 +3919,6 @@ update_flow_info (notes, first, last, orig_insn)
break;
case REG_NONNEG:
- case REG_BR_PROB:
/* This should be moved to whichever instruction is a JUMP_INSN. */
for (insn = last; ; insn = PREV_INSN (insn))
diff --git a/gcc/toplev.c b/gcc/toplev.c
index ede8252..c503836 100755
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -278,7 +278,6 @@ int cse_dump = 0;
int gcse_dump = 0;
int loop_dump = 0;
int cse2_dump = 0;
-int branch_prob_dump = 0;
int flow_dump = 0;
int combine_dump = 0;
int regmove_dump = 0;
@@ -370,26 +369,6 @@ lang_expand_expr_t lang_expand_expr = 0;
void (*incomplete_decl_finalize_hook) PROTO((tree)) = 0;
-/* Nonzero if generating code to do profiling. */
-
-int profile_flag = 0;
-
-/* Nonzero if generating code to do profiling on a line-by-line basis. */
-
-int profile_block_flag;
-
-/* Nonzero if generating code to profile program flow graph arcs. */
-
-int profile_arc_flag = 0;
-
-/* Nonzero if generating info for gcov to calculate line test coverage. */
-
-int flag_test_coverage = 0;
-
-/* Nonzero indicates that branch taken probabilities should be calculated. */
-
-int flag_branch_probabilities = 0;
-
/* Nonzero for -pedantic switch: warn about anything
that standard spec forbids. */
@@ -943,12 +922,6 @@ lang_independent_options f_options[] =
"Use setjmp/longjmp to handle exceptions" },
{"asynchronous-exceptions", &asynchronous_exceptions, 1,
"Support asynchronous exceptions" },
- {"profile-arcs", &profile_arc_flag, 1,
- "Insert arc based program profiling code" },
- {"test-coverage", &flag_test_coverage, 1,
- "Create data files needed by gcov" },
- {"branch-probabilities", &flag_branch_probabilities, 1,
- "Use profiling information for branch porbabilities" },
{"fast-math", &flag_fast_math, 1,
"Improve FP speed by violating ANSI & IEEE rules" },
{"common", &flag_no_common, 0,
@@ -1303,7 +1276,6 @@ int cse_time;
int gcse_time;
int loop_time;
int cse2_time;
-int branch_prob_time;
int flow_time;
int combine_time;
int regmove_time;
@@ -2637,7 +2609,6 @@ compile_file (name)
gcse_time = 0;
loop_time = 0;
cse2_time = 0;
- branch_prob_time = 0;
flow_time = 0;
combine_time = 0;
regmove_time = 0;
@@ -2661,8 +2632,7 @@ compile_file (name)
name = init_parse (name);
init_rtl ();
init_emit_once (debug_info_level == DINFO_LEVEL_NORMAL
- || debug_info_level == DINFO_LEVEL_VERBOSE
- || flag_test_coverage);
+ || debug_info_level == DINFO_LEVEL_VERBOSE);
init_regs ();
init_decl_processing ();
init_optabs ();
@@ -2734,12 +2704,6 @@ compile_file (name)
if (graph_dump_format != no_graph)
clean_graph_dump_file (dump_base_name, ".cse2");
}
- if (branch_prob_dump)
- {
- clean_dump_file (".bp");
- if (graph_dump_format != no_graph)
- clean_graph_dump_file (dump_base_name, ".bp");
- }
if (flow_dump)
{
clean_dump_file (".flow");
@@ -2877,8 +2841,6 @@ compile_file (name)
if (flag_syntax_only)
{
write_symbols = NO_DEBUG;
- profile_flag = 0;
- profile_block_flag = 0;
}
else
{
@@ -2923,13 +2885,6 @@ compile_file (name)
}
#endif
- if (flag_function_sections
- && (profile_flag || profile_block_flag))
- {
- warning ("-ffunction-sections disabled; it makes profiling impossible.");
- flag_function_sections = 0;
- }
-
#ifndef OBJECT_FORMAT_ELF
if (flag_function_sections && write_symbols != NO_DEBUG)
warning ("-ffunction-sections may affect debugging on some targets.");
@@ -2944,18 +2899,6 @@ compile_file (name)
Therefore, I took out that change.
In future versions we should find another way to solve
that dbx problem. -- rms, 23 May 93. */
-
- /* Don't let the first function fall at the same address
- as gcc_compiled., if profiling. */
- if (profile_flag || profile_block_flag)
- {
- /* It's best if we can write a nop here since some
- assemblers don't tolerate zeros in the text section. */
- if (insn_template[CODE_FOR_nop] != 0)
- output_asm_insn (insn_template[CODE_FOR_nop], NULL_PTR);
- else
- assemble_zeros (UNITS_PER_WORD);
- }
/* If dbx symbol table desired, initialize writing it
and output the predefined types. */
@@ -2985,7 +2928,6 @@ compile_file (name)
/* Initialize yet another pass. */
init_final (main_input_filename);
- init_branch_prob (dump_base_name);
start_time = get_run_time ();
@@ -3108,15 +3050,6 @@ compile_file (name)
}
}
- /* This must occur after the loop to output deferred functions. Else
- the profiler initializer would not be emitted if all the functions
- in this compilation unit were deferred.
-
- output_func_start_profiler can not cause any additional functions or
- data to need to be output, so it need not be in the deferred function
- loop above. */
- output_func_start_profiler ();
-
/* Now that all possible functions have been output, we can dump
the exception table. */
@@ -3256,14 +3189,6 @@ compile_file (name)
end_final (dump_base_name);
- if (branch_prob_dump)
- open_dump_file (".bp", NULL);
-
- TIMEVAR (dump_time, end_branch_prob (rtl_dump_file));
-
- if (branch_prob_dump)
- close_dump_file (NULL, NULL_RTX);
-
#ifdef ASM_FILE_END
ASM_FILE_END (asm_out_file);
#endif
@@ -3321,8 +3246,6 @@ compile_file (name)
finish_graph_dump_file (dump_base_name, ".loop");
if (cse2_dump)
finish_graph_dump_file (dump_base_name, ".cse2");
- if (branch_prob_dump)
- finish_graph_dump_file (dump_base_name, ".bp");
if (flow_dump)
finish_graph_dump_file (dump_base_name, ".flow");
if (combine_dump)
@@ -3371,7 +3294,6 @@ compile_file (name)
print_time ("gcse", gcse_time);
print_time ("loop", loop_time);
print_time ("cse2", cse2_time);
- print_time ("branch-prob", branch_prob_time);
print_time ("flow", flow_time);
print_time ("combine", combine_time);
print_time ("regmove", regmove_time);
@@ -3939,25 +3861,6 @@ rest_of_compilation (decl)
}
}
- if (profile_arc_flag || flag_test_coverage || flag_branch_probabilities)
- {
- if (branch_prob_dump)
- open_dump_file (".bp", decl_printable_name (decl, 2));
-
- TIMEVAR
- (branch_prob_time,
- {
- branch_prob (insns, rtl_dump_file);
- });
-
- if (branch_prob_dump)
- {
- close_dump_file (print_rtl, insns);
- if (graph_dump_format != no_graph)
- print_rtl_graph_with_bb (dump_base_name, ".bp", insns);
- }
- }
-
/* We are no longer anticipating cse in this function, at least. */
cse_not_expected = 1;
@@ -4471,13 +4374,6 @@ display_help ()
printf (" -Wid-clash-<num> Warn if 2 identifiers have the same first <num> chars\n");
printf (" -Wlarger-than-<number> Warn if an object is larger than <number> bytes\n");
- printf (" -p Enable function profiling\n");
-#if defined (BLOCK_PROFILER) || defined (FUNCTION_BLOCK_PROFILER)
- printf (" -a Enable block profiling \n");
-#endif
-#if defined (BLOCK_PROFILER) || defined (FUNCTION_BLOCK_PROFILER) || defined FUNCTION_BLOCK_PROFILER_EXIT
- printf (" -ax Enable jump profiling \n");
-#endif
printf (" -o <file> Place output into <file> \n");
printf (" -G <number> Put global and static data smaller than <number>\n");
printf (" bytes into a special section (on some targets)\n");
@@ -4878,7 +4774,6 @@ main (argc, argv)
switch (*p++)
{
case 'a':
- branch_prob_dump = 1;
combine_dump = 1;
#ifdef DELAY_SLOTS
dbr_sched_dump = 1;
@@ -4909,9 +4804,6 @@ main (argc, argv)
case 'A':
flag_debug_asm = 1;
break;
- case 'b':
- branch_prob_dump = 1;
- break;
case 'c':
combine_dump = 1;
break;
@@ -5148,24 +5040,15 @@ main (argc, argv)
}
else if (!strcmp (str, "p"))
{
- profile_flag = 1;
+ warning ("`-p' option (function profiling) not supported");
}
else if (!strcmp (str, "a"))
{
-#if !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER)
warning ("`-a' option (basic block profile) not supported");
-#else
- profile_block_flag = (profile_block_flag < 2) ? 1 : 3;
-#endif
}
else if (!strcmp (str, "ax"))
{
-#if !defined (FUNCTION_BLOCK_PROFILER_EXIT) || !defined (BLOCK_PROFILER) || !defined (FUNCTION_BLOCK_PROFILER)
warning ("`-ax' option (jump profiling) not supported");
-#else
- profile_block_flag = (!profile_block_flag
- || profile_block_flag == 2) ? 2 : 3;
-#endif
}
else if (str[0] == 'g')
{
@@ -5343,12 +5226,6 @@ main (argc, argv)
#endif
}
- if (profile_block_flag == 3)
- {
- warning ("`-ax' and `-a' are conflicting options. `-a' ignored.");
- profile_block_flag = 2;
- }
-
/* Unrolling all loops implies that standard loop unrolling must also
be done. */
if (flag_unroll_all_loops)