diff --git a/gcc/ChangeLog b/gcc/ChangeLog index cf7a1e2879b7e797e2209c1115a5468edff0a488..2d7522426988075f07c820b442325ce07dfc989b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,45 @@ +2000-08-01 Jeffrey Oldham <oldham@codesourcery.com> + Mark Mitchell <mark@codesourcery.com> + + * Makefile.in (OBJS): Added dce.o. + (ssa.o): Updated target to include ssa.h. + (flow.o): Likewise. + (toplev.o): Likewise. + (dce.o): Created target. + * basic-block.h: Added comments. + (INVALID_BLOCK): Added definition. + (connect_infinite_loops_to_exit): Added declaration. + Moved SSA declarations to ssa.h. + * flow.c: Added inclusion of ssa.h. + (struct depth_first_search_dsS, depth_first_search_ds): + Added definitions. + (compute_immediate_postdominators): Added definition. + (connect_infinite_loops_to_exit): Likewise. + (flow_dfs_compute_reverse_init): Likewise. + (flow_dfs_compute_reverse_add_bb): Likewise. + (flow_dfs_compute_reverse_execute): Likewise. + (flow_dfs_compute_reverse_finish): Likewise. + * rtl.h (rtx/in_struct): Added use to determine insn necessity. + (LABEL_P): Added definition. + (JUMP_P): Likewise. + (NOTE_P): Likewise. + (BARRIER_P): Likewise. + (JUMP_TABLE_DATA_P): Likewise. + (INSN_DEAD_CODE_P): Likewise. + * ssa.c: Replaced inclusions with ssa.h inclusion. + (CONVERT_HARD_REGISTER_TO_SSA_P): Moved to ssa.h. + (rename_registers): Removed unnecessary variables. + * ssa.h: Created by moving declarations from ssa.c and + basic-block.h. + * timevar.def: Defined TV_DEAD_CODE_ELIM. + * toplev.c: Added ssa.h inclusion. + (dump_file_index): Added DFI_dce. + (dump_file): Added "dce" entry. + Defined flag_ssa. + (f_options): Added dce entry. + * invoke.texi: Document -fdce. Emphasize experimental status of + -fssa. + 2000-08-01 Zack Weinberg <zack@wolery.cumb.org> * cpperror.c (v_message): Split into _cpp_begin_message and diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 564cc1d851f117103826851d1569242376c40562..31ab344f9d18258b6feb165da775ff5acce21933 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -696,7 +696,7 @@ OBJS = diagnostic.o \ profile.o insn-attrtab.o $(out_object_file) $(EXTRA_OBJS) convert.o \ mbchar.o splay-tree.o graph.o sbitmap.o resource.o hash.o predict.o \ lists.o ggc-common.o $(GGC) simplify-rtx.o ssa.o bb-reorder.o \ - sibcall.o conflict.o timevar.o ifcvt.o + sibcall.o conflict.o timevar.o ifcvt.o dce.o # GEN files are listed separately, so they can be built before doing parallel # makes for cc1 or cc1plus. Otherwise sequent parallel make attempts to load @@ -1242,7 +1242,7 @@ toplev.o : toplev.c $(CONFIG_H) system.h $(TREE_H) $(RTL_H) function.h \ flags.h input.h $(INSN_ATTR_H) xcoffout.h defaults.h output.h diagnostic.h \ insn-codes.h insn-config.h intl.h $(RECOG_H) Makefile toplev.h dwarfout.h \ dwarf2out.h sdbout.h dbxout.h $(EXPR_H) hard-reg-set.h $(BASIC_BLOCK_H) \ - graph.h loop.h except.h regs.h $(TIMEVAR_H) $(lang_options_files) + graph.h loop.h except.h regs.h $(TIMEVAR_H) $(lang_options_files) ssa.h $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(INCLUDES) $(MAYBE_USE_COLLECT2) \ -DTARGET_NAME=\"$(target_alias)\" \ -c `echo $(srcdir)/toplev.c | sed 's,^\./,,'` @@ -1326,10 +1326,11 @@ resource.o : resource.c $(CONFIG_H) $(RTL_H) hard-reg-set.h system.h \ $(INSN_ATTR_H) except.h lcm.o : lcm.c $(CONFIG_H) system.h $(RTL_H) $(REGS_H) hard-reg-set.h flags.h \ real.h insn-config.h $(INSN_ATTR_H) $(RECOG_H) $(EXPR_H) $(BASIC_BLOCK_H) -ssa.o : ssa.c $(CONFIG_H) system.h $(RTL_H) varray.h sbitmap.h \ - $(HASHTAB_H) $(REGS_H) hard-reg-set.h flags.h function.h real.h \ - insn-config.h $(RECOG_H) $(BASIC_BLOCK_H) \ - output.h +ssa.o : ssa.c $(CONFIG_H) system.h $(REGS_H) varray.h \ + hard-reg-set.h flags.h function.h real.h insn-config.h $(RECOG_H) \ + $(BASIC_BLOCK_H) output.h ssa.h +dce.o : dce.c $(CONFIG_H) system.h $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) \ + ssa.h insn-config.h $(RECOG_H) output.h conflict.o : conflict.c $(CONFIG_H) system.h $(OBSTACK_H) $(HASHTAB_H) \ $(RTL_H) hard-reg-set.h $(BASIC_BLOCK_H) profile.o : profile.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h \ @@ -1345,7 +1346,7 @@ unroll.o : unroll.c $(CONFIG_H) system.h $(RTL_H) insn-config.h function.h \ hard-reg-set.h varray.h $(BASIC_BLOCK_H) flow.o : flow.c $(CONFIG_H) system.h $(RTL_H) $(TREE_H) flags.h insn-config.h \ $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \ - insn-flags.h function.h except.h $(EXPR_H) + insn-flags.h function.h except.h $(EXPR_H) ssa.h combine.o : combine.c $(CONFIG_H) system.h $(RTL_H) flags.h function.h \ insn-config.h insn-flags.h insn-codes.h $(INSN_ATTR_H) $(REGS_H) $(EXPR_H) \ $(BASIC_BLOCK_H) $(RECOG_H) real.h hard-reg-set.h toplev.h diff --git a/gcc/basic-block.h b/gcc/basic-block.h index 2e540758b33d1cd82242b9ea62c5d0206f3d8b46..a018f23770b481f6533ea28a64ed67d0611d75eb 100644 --- a/gcc/basic-block.h +++ b/gcc/basic-block.h @@ -141,6 +141,18 @@ typedef struct edge_def { #define EDGE_COMPLEX (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL | EDGE_EH) +/* Basic blocks need not start with a label nor end with a jump insn. + For example, a previous basic block may just "conditionally fall" + into the succeeding basic block, and the last basic block need not + end with a jump insn. Block 0 is a descendant of the entry block. + + A basic block beginning with two labels cannot have notes between + the labels. + + Data for jump tables are stored in jump_insns that occur in no + basic block even though these insns can follow or precede insns in + basic blocks. */ + /* Basic block information indexed by block number. */ typedef struct basic_block_def { /* The first and last insns of the block. */ @@ -210,6 +222,9 @@ extern regset regs_live_at_setjmp; #define ENTRY_BLOCK (-1) #define EXIT_BLOCK (-2) +/* Special block number not valid for any block. */ +#define INVALID_BLOCK (-3) + /* Similarly, block pointers for the edge list. */ extern struct basic_block_def entry_exit_blocks[2]; #define ENTRY_BLOCK_PTR (&entry_exit_blocks[0]) @@ -230,6 +245,7 @@ extern void insert_insn_on_edge PARAMS ((rtx, edge)); extern void commit_edge_insertions PARAMS ((void)); extern void remove_fake_edges PARAMS ((void)); extern void add_noreturn_fake_exit_edges PARAMS ((void)); +extern void connect_infinite_loops_to_exit PARAMS ((void)); extern rtx flow_delete_insn PARAMS ((rtx)); extern void flow_delete_insn_chain PARAMS ((rtx, rtx)); extern void make_edge PARAMS ((sbitmap *, basic_block, @@ -514,13 +530,4 @@ extern conflict_graph conflict_graph_compute PARAMS ((regset, partition)); -/* In ssa.c */ -extern void convert_to_ssa PARAMS ((void)); -extern void convert_from_ssa PARAMS ((void)); -typedef int (*successor_phi_fn) PARAMS ((rtx, int, int, void *)); -extern int for_each_successor_phi PARAMS ((basic_block bb, - successor_phi_fn, - void *)); -extern int in_ssa_form; - #endif /* _BASIC_BLOCK_H */ diff --git a/gcc/dce.c b/gcc/dce.c new file mode 100644 index 0000000000000000000000000000000000000000..f385afd31ea3012c42938796fb53bd67ce96d53e --- /dev/null +++ b/gcc/dce.c @@ -0,0 +1,620 @@ +/* Dead-code elimination pass for the GNU compiler. + Copyright (C) 2000 Free Software Foundation, Inc. + Written by Jeffrey D. Oldham <oldham@codesourcery.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 +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ + +/* Dead-code elimination is the removal of instructions which have no + impact on the program's output. "Dead instructions" have no impact + on the program's output, while "necessary instructions" may have + impact on the output. + + The algorithm consists of three phases: + 1) marking as necessary all instructions known to be necessary, + e.g., writing a value to memory, + 2) propagating necessary instructions, e.g., the instructions + giving values to operands in necessary instructions, and + 3) removing dead instructions (except replacing dead conditionals + with unconditional jumps). + + Side Effects: + The last step can require adding labels, deleting insns, and + modifying basic block structures. Some conditional jumps may be + converted to unconditional jumps so the control-flow graph may be + out-of-date. + + Edges from some infinite loops to the exit block can be added to + the control-flow graph. + + It Does Not Perform: + We decided to not simultaneously perform jump optimization and dead + loop removal during dead-code elimination. Thus, all jump + instructions originally present remain after dead-code elimination + but 1) unnecessary conditional jump instructions are changed to + unconditional jump instructions and 2) all unconditional jump + instructions remain. + + Assumptions: + 1) SSA has been performed. + 2) The basic block and control-flow graph structures are accurate. + 3) The flow graph permits constructing an edge_list. + 4) note rtxes should be saved. + + Unfinished: + When replacing unnecessary conditional jumps with unconditional + jumps, the control-flow graph is not updated. It should be. + + References: + Building an Optimizing Compiler + Robert Morgan + Butterworth-Heinemann, 1998 + Section 8.9 +*/ + +#include "config.h" +#include "system.h" + +#include "rtl.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "ssa.h" +#include "insn-config.h" +#include "recog.h" +#include "output.h" + +/* We cannot use <assert.h> in GCC source, since that would include + GCC's assert.h, which may not be compatible with the host compiler. */ +#undef assert +#ifdef NDEBUG +# define assert(e) +#else +# define assert(e) do { if (! (e)) abort (); } while (0) +#endif + +/* A map from blocks to the edges on which they are control dependent. */ +typedef struct { + /* An dynamically allocated array. The Nth element corresponds to + the block with index N + 2. The Ith bit in the bitmap is set if + that block is dependent on the Ith edge. */ + bitmap *data; + /* The number of elements in the array. */ + int length; +} control_dependent_block_to_edge_map_s, *control_dependent_block_to_edge_map; + +/* Local function prototypes. */ +static control_dependent_block_to_edge_map control_dependent_block_to_edge_map_create + PARAMS((size_t num_basic_blocks)); +static void set_control_dependent_block_to_edge_map_bit + PARAMS ((control_dependent_block_to_edge_map c, basic_block bb, + int edge_index)); +static void control_dependent_block_to_edge_map_free + PARAMS ((control_dependent_block_to_edge_map c)); +static void find_all_control_dependences + PARAMS ((struct edge_list *el, int *pdom, + control_dependent_block_to_edge_map cdbte)); +static void find_control_dependence + PARAMS ((struct edge_list *el, int edge_index, int *pdom, + control_dependent_block_to_edge_map cdbte)); +static basic_block find_pdom + PARAMS ((int *pdom, basic_block block)); +static int inherently_necessary_register_1 + PARAMS ((rtx *current_rtx, void *data)); +static int inherently_necessary_register + PARAMS ((rtx current_rtx)); +static int find_inherently_necessary + PARAMS ((rtx current_rtx)); +static int propagate_necessity_through_operand + PARAMS ((rtx *current_rtx, void *data)); + +/* Unnecessary insns are indicated using insns' in_struct bit. */ + +/* Indicate INSN is dead-code; returns nothing. */ +#define KILL_INSN(INSN) INSN_DEAD_CODE_P(INSN) = 1 +/* Indicate INSN is necessary, i.e., not dead-code; returns nothing. */ +#define RESURRECT_INSN(INSN) INSN_DEAD_CODE_P(INSN) = 0 +/* Return nonzero if INSN is unnecessary. */ +#define UNNECESSARY_P(INSN) INSN_DEAD_CODE_P(INSN) +static void mark_all_insn_unnecessary + PARAMS ((void)); +/* Execute CODE with free variable INSN for all unnecessary insns in + an unspecified order, producing no output. */ +#define EXECUTE_IF_UNNECESSARY(INSN, CODE) \ +{ \ + rtx INSN; \ + \ + for (INSN = get_insns (); INSN != NULL_RTX; INSN = NEXT_INSN (INSN)) \ + if (INSN_DEAD_CODE_P (INSN)) { \ + CODE; \ + } \ +} +/* Find the label beginning block BB. */ +static rtx find_block_label + PARAMS ((basic_block bb)); +/* Remove INSN, updating its basic block structure. */ +static void delete_insn_bb + PARAMS ((rtx insn)); + +/* Recording which blocks are control dependent on which edges. We + expect each block to be control dependent on very few edges so we + use a bitmap for each block recording its edges. An array holds + the bitmap. Its position 0 entry holds the bitmap for block + INVALID_BLOCK+1 so that all blocks, including the entry and exit + blocks can participate in the data structure. */ + +/* Create a control_dependent_block_to_edge_map, given the number + NUM_BASIC_BLOCKS of non-entry, non-exit basic blocks, e.g., + n_basic_blocks. This memory must be released using + control_dependent_block_to_edge_map_free (). */ + +static control_dependent_block_to_edge_map +control_dependent_block_to_edge_map_create (num_basic_blocks) + size_t num_basic_blocks; +{ + int i; + control_dependent_block_to_edge_map c + = xmalloc (sizeof (control_dependent_block_to_edge_map_s)); + c->length = num_basic_blocks - (INVALID_BLOCK+1); + c->data = xmalloc ((size_t) c->length*sizeof (bitmap)); + for (i = 0; i < c->length; ++i) + c->data[i] = BITMAP_XMALLOC (); + + return c; +} + +/* Indicate block BB is control dependent on an edge with index + EDGE_INDEX in the mapping C of blocks to edges on which they are + control-dependent. */ + +static void +set_control_dependent_block_to_edge_map_bit (c, bb, edge_index) + control_dependent_block_to_edge_map c; + basic_block bb; + int edge_index; +{ + assert(bb->index - (INVALID_BLOCK+1) < c->length); + bitmap_set_bit (c->data[bb->index - (INVALID_BLOCK+1)], + edge_index); +} + +/* Execute CODE for each edge (given number EDGE_NUMBER within the + CODE) for which the block containing INSN is control dependent, + returning no output. CDBTE is the mapping of blocks to edges on + which they are control-dependent. */ + +#define EXECUTE_IF_CONTROL_DEPENDENT(CDBTE, INSN, EDGE_NUMBER, CODE) \ + EXECUTE_IF_SET_IN_BITMAP \ + (CDBTE->data[BLOCK_NUM (INSN) - (INVALID_BLOCK+1)], 0, \ + EDGE_NUMBER, CODE) + +/* Destroy a control_dependent_block_to_edge_map C. */ + +static void +control_dependent_block_to_edge_map_free (c) + control_dependent_block_to_edge_map c; +{ + int i; + for (i = 0; i < c->length; ++i) + BITMAP_XFREE (c->data[i]); + free ((PTR) c); +} + +/* Record all blocks' control dependences on all edges in the edge + list EL, ala Morgan, Section 3.6. The mapping PDOM of blocks to + their postdominators are used, and results are stored in CDBTE, + which should be empty. */ + +static void +find_all_control_dependences (el, pdom, cdbte) + struct edge_list *el; + int *pdom; + control_dependent_block_to_edge_map cdbte; +{ + int i; + + for (i = 0; i < NUM_EDGES (el); ++i) + find_control_dependence (el, i, pdom, cdbte); +} + +/* Determine all blocks' control dependences on the given edge with + edge_list EL index EDGE_INDEX, ala Morgan, Section 3.6. The + mapping PDOM of blocks to their postdominators are used, and + results are stored in CDBTE, which is assumed to be initialized + with zeros in each (block b', edge) position. */ + +static void +find_control_dependence (el, edge_index, pdom, cdbte) + struct edge_list *el; + int edge_index; + int *pdom; + control_dependent_block_to_edge_map cdbte; +{ + basic_block current_block; + basic_block ending_block; + + assert (INDEX_EDGE_PRED_BB (el, edge_index) != EXIT_BLOCK_PTR); + ending_block = + (INDEX_EDGE_PRED_BB (el, edge_index) == ENTRY_BLOCK_PTR) + ? BASIC_BLOCK (0) + : find_pdom (pdom, INDEX_EDGE_PRED_BB (el, edge_index)); + + for (current_block = INDEX_EDGE_SUCC_BB (el, edge_index); + current_block != ending_block && current_block != EXIT_BLOCK_PTR; + current_block = find_pdom (pdom, current_block)) + { + set_control_dependent_block_to_edge_map_bit (cdbte, + current_block, + edge_index); + } +} + +/* Find the immediate postdominator PDOM of the specified basic block + BLOCK. This function is necessary because some blocks have + negative numbers. */ + +static basic_block +find_pdom (pdom, block) + int *pdom; + basic_block block; +{ + assert (block != NULL); + assert (block->index != INVALID_BLOCK); + if (block == ENTRY_BLOCK_PTR) + return BASIC_BLOCK (0); + else if (block == EXIT_BLOCK_PTR || pdom[block->index] == EXIT_BLOCK) + return EXIT_BLOCK_PTR; + else + return BASIC_BLOCK (pdom[block->index]); +} + +/* Determine if the given CURRENT_RTX uses a hard register not + converted to SSA. Returns nonzero only if it uses such a hard + register. DATA is not used. + + The program counter (PC) is not considered inherently necessary + since code should be position-independent and thus not depend on + particular PC values. */ + +static int +inherently_necessary_register_1 (current_rtx, data) + rtx *current_rtx; + void *data ATTRIBUTE_UNUSED; +{ + rtx x = *current_rtx; + + if (x == NULL_RTX) + return 0; + switch (GET_CODE (x)) + { + case CLOBBER: + /* Do not traverse the rest of the clobber. */ + return -1; + break; + case PC: + return 0; + break; + case REG: + if (CONVERT_REGISTER_TO_SSA_P (REGNO (x)) || x == pc_rtx) + return 0; + else + return !0; + break; + default: + return 0; + break; + } +} + +/* Return nonzero if the insn CURRENT_RTX is inherently necessary. */ + +static int +inherently_necessary_register (current_rtx) + rtx current_rtx; +{ + return for_each_rtx (¤t_rtx, + &inherently_necessary_register_1, NULL); +} + +/* Mark X as inherently necessary if appropriate. For example, + function calls and storing values into memory are inherently + necessary. This function is to be used with for_each_rtx (). + Return nonzero iff inherently necessary. */ + +static int +find_inherently_necessary (x) + rtx x; +{ + rtx pattern; + if (x == NULL_RTX) + return 0; + else if (inherently_necessary_register (x)) + return !0; + else + switch (GET_CODE (x)) + { + case CALL_INSN: + case CODE_LABEL: + case NOTE: + case BARRIER: + return !0; + break; + case JUMP_INSN: + return JUMP_TABLE_DATA_P (x) || computed_jump_p (x) != 0; + break; + case INSN: + pattern = PATTERN (x); + switch (GET_CODE (pattern)) + { + case SET: + case PRE_DEC: + case PRE_INC: + case POST_DEC: + case POST_INC: + return GET_CODE (SET_DEST (pattern)) == MEM; + case CALL: + case RETURN: + case USE: + case CLOBBER: + return !0; + break; + case ASM_INPUT: + /* We treat assembler instructions as inherently + necessary, and we hope that its operands do not need to + be propagated. */ + return !0; + break; + default: + return 0; + } + default: + /* Found an impossible insn type. */ + abort(); + break; + } +} + +/* Propagate necessity through REG and SUBREG operands of CURRENT_RTX. + This function is called with for_each_rtx () on necessary + instructions. The DATA must be a varray of unprocessed + instructions. */ + +static int +propagate_necessity_through_operand (current_rtx, data) + rtx *current_rtx; + void *data; +{ + rtx x = *current_rtx; + varray_type *unprocessed_instructions = (varray_type *) data; + + if (x == NULL_RTX) + return 0; + switch ( GET_CODE (x)) + { + case REG: + if (CONVERT_REGISTER_TO_SSA_P (REGNO (x))) + { + rtx insn = VARRAY_RTX (ssa_definition, REGNO (x)); + if (insn != NULL_RTX && UNNECESSARY_P (insn)) + { + RESURRECT_INSN (insn); + VARRAY_PUSH_RTX (*unprocessed_instructions, insn); + } + } + return 0; + + default: + return 0; + } +} + +/* Indicate all insns initially assumed to be unnecessary. */ + +static void +mark_all_insn_unnecessary () +{ + rtx insn; + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + KILL_INSN (insn); +} + +/* Find the label beginning block BB, adding one if necessary. */ + +static rtx +find_block_label (bb) + basic_block bb; +{ + rtx insn = bb->head; + if (LABEL_P (insn)) + return insn; + else + { + rtx new_label = emit_label_before (gen_label_rtx (), insn); + if (insn == bb->head) + bb->head = new_label; + return new_label; + } +} + +/* Remove INSN, updating its basic block structure. */ + +static void +delete_insn_bb (insn) + rtx insn; +{ + basic_block bb; + assert (insn != NULL_RTX); + bb = BLOCK_FOR_INSN (insn); + assert (bb != 0); + if (bb->head == bb->end) + { + /* Delete the insn by converting it to a note. */ + PUT_CODE (insn, NOTE); + NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED; + return; + } + else if (insn == bb->head) + bb->head = NEXT_INSN (insn); + else if (insn == bb->end) + bb->end = PREV_INSN (insn); + delete_insn (insn); +} + +/* Perform the dead-code elimination. */ + +void +eliminate_dead_code () +{ + int i; + rtx insn; + /* Necessary instructions with operands to explore. */ + varray_type unprocessed_instructions; + /* Map element (b,e) is nonzero if the block is control dependent on + edge. "cdbte" abbreviates control dependent block to edge. */ + control_dependent_block_to_edge_map cdbte; + sbitmap *postdominators; + /* Element I is the immediate postdominator of block I. */ + int *pdom; + struct edge_list *el; + + int max_insn_uid = get_max_uid (); + + /* Initialize the data structures. */ + mark_all_insn_unnecessary (); + VARRAY_RTX_INIT (unprocessed_instructions, 64, + "unprocessed instructions"); + cdbte = control_dependent_block_to_edge_map_create (n_basic_blocks); + + /* Prepare for use of BLOCK_NUM (). */ + connect_infinite_loops_to_exit (); + /* Be careful not to clear the added edges. */ + compute_bb_for_insn (max_insn_uid); + + /* Compute control dependence. */ + postdominators = sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks); + compute_flow_dominators (NULL, postdominators); + pdom = (int *) xmalloc (n_basic_blocks * sizeof (int)); + for (i = 0; i < n_basic_blocks; ++i) + pdom[i] = INVALID_BLOCK; + compute_immediate_postdominators (pdom, postdominators); + /* Assume there is a path from each node to the exit block. */ + for (i = 0; i < n_basic_blocks; ++i) + if (pdom[i] == INVALID_BLOCK) + pdom[i] = EXIT_BLOCK; + sbitmap_vector_free (postdominators); + el = create_edge_list(); + find_all_control_dependences (el, pdom, cdbte); + + /* Find inherently necessary instructions. */ + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + if (find_inherently_necessary (insn)) + { + RESURRECT_INSN (insn); + VARRAY_PUSH_RTX (unprocessed_instructions, insn); + } + + /* Propagate necessity using the operands of necessary instructions. */ + while (VARRAY_ACTIVE_SIZE (unprocessed_instructions) > 0) + { + rtx current_instruction; + int edge_number; + + current_instruction = VARRAY_TOP_RTX (unprocessed_instructions); + VARRAY_POP (unprocessed_instructions); + + /* Make corresponding control dependent edges necessary. */ + /* Assume the only JUMP_INSN is the block's last insn. It appears + that the last instruction of the program need not be a + JUMP_INSN. */ + + if (INSN_P (current_instruction) + && !JUMP_TABLE_DATA_P (current_instruction)) + { + /* Notes and labels contain no interesting operands. */ + EXECUTE_IF_CONTROL_DEPENDENT + (cdbte, current_instruction, edge_number, + { + rtx jump_insn = (INDEX_EDGE_PRED_BB (el, edge_number))->end; + if (GET_CODE (jump_insn) == JUMP_INSN && + UNNECESSARY_P (jump_insn)) { + RESURRECT_INSN (jump_insn); + VARRAY_PUSH_RTX (unprocessed_instructions, jump_insn); + } + }); + + /* Propagate through the operands. */ + for_each_rtx (¤t_instruction, + &propagate_necessity_through_operand, + (PTR) &unprocessed_instructions); + + } + } + + /* Remove the unnecessary instructions. */ + EXECUTE_IF_UNNECESSARY (insn, + { + if (any_condjump_p (insn)) + { + /* Convert unnecessary conditional insn to an unconditional + jump to immediate postdominator block. */ + rtx old_label = JUMP_LABEL (insn); + int pdom_block_number = + find_pdom (pdom, BLOCK_FOR_INSN (insn))->index; + + /* Prevent the conditional jump's label from being deleted so + we do not have to modify the basic block structure. */ + ++LABEL_NUSES (old_label); + + if (pdom_block_number != EXIT_BLOCK + && pdom_block_number != INVALID_BLOCK) + { + rtx lbl = find_block_label (BASIC_BLOCK (pdom_block_number)); + rtx new_jump = emit_jump_insn_before (gen_jump (lbl), insn); + + /* Let jump know that label is in use. */ + JUMP_LABEL (new_jump) = lbl; + ++LABEL_NUSES (lbl); + + delete_insn_bb (insn); + + /* A conditional branch is unnecessary if and only if any + block control-dependent on it is unnecessary. Thus, + any phi nodes in these unnecessary blocks are also + removed and these nodes need not be updated. */ + + /* A barrier must follow any unconditional jump. Barriers + are not in basic blocks so this must occur after + deleting the conditional jump. */ + emit_barrier_after (new_jump); + } + else + /* The block drops off the end of the function and the + ending conditional jump is not needed. */ + delete_insn_bb (insn); + } + else if (!JUMP_P (insn)) + delete_insn_bb (insn); + }); + + /* Release allocated memory. */ + for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn)) + RESURRECT_INSN (insn); + assert (VARRAY_ACTIVE_SIZE(unprocessed_instructions) == 0); + VARRAY_FREE (unprocessed_instructions); + control_dependent_block_to_edge_map_free (cdbte); + free ((PTR) pdom); + free_edge_list (el); +} diff --git a/gcc/flow.c b/gcc/flow.c index 688e256c3391f51158f57c299f5650d838b65661..3b5539e83192137cc427fc043ee361ac0e0bc46a 100644 --- a/gcc/flow.c +++ b/gcc/flow.c @@ -136,6 +136,7 @@ Boston, MA 02111-1307, USA. */ #include "recog.h" #include "insn-flags.h" #include "expr.h" +#include "ssa.h" #include "obstack.h" #include "splay-tree.h" @@ -308,6 +309,20 @@ struct propagate_block_info int flags; }; +/* Store the data structures necessary for depth-first search. */ +struct depth_first_search_dsS { + /* stack for backtracking during the algorithm */ + basic_block *stack; + + /* number of edges in the stack. That is, positions 0, ..., sp-1 + have edges. */ + unsigned int sp; + + /* record of basic blocks already seen by depth-first search */ + sbitmap visited_blocks; +}; +typedef struct depth_first_search_dsS *depth_first_search_ds; + /* Forward declarations */ static int count_basic_blocks PARAMS ((rtx)); static void find_basic_blocks_1 PARAMS ((rtx)); @@ -398,6 +413,14 @@ static int flow_loop_nested_p PARAMS ((struct loop *, struct loop *)); static int flow_loop_exits_find PARAMS ((const sbitmap, edge **)); static int flow_loop_nodes_find PARAMS ((basic_block, basic_block, sbitmap)); static int flow_depth_first_order_compute PARAMS ((int *, int *)); +static void flow_dfs_compute_reverse_init + PARAMS ((depth_first_search_ds)); +static void flow_dfs_compute_reverse_add_bb + PARAMS ((depth_first_search_ds, basic_block)); +static basic_block flow_dfs_compute_reverse_execute + PARAMS ((depth_first_search_ds)); +static void flow_dfs_compute_reverse_finish + PARAMS ((depth_first_search_ds)); static basic_block flow_loop_pre_header_find PARAMS ((basic_block, const sbitmap *)); static void flow_loop_tree_node_add PARAMS ((struct loop *, struct loop *)); static void flow_loops_tree_build PARAMS ((struct loops *)); @@ -3741,7 +3764,7 @@ init_propagate_block_info (bb, live, local_set, flags) && GET_CODE (SET_DEST (PATTERN (insn))) == MEM) { rtx mem = SET_DEST (PATTERN (insn)); - + if (XEXP (mem, 0) == frame_pointer_rtx || (GET_CODE (XEXP (mem, 0)) == PLUS && XEXP (XEXP (mem, 0), 0) == frame_pointer_rtx @@ -6254,7 +6277,6 @@ compute_immediate_postdominators (idom, postdominators) sbitmap *postdominators; { compute_immediate_dominators (idom, postdominators); - return; } /* Recompute register set/reference counts immediately prior to register @@ -6888,7 +6910,6 @@ verify_edge_list (f, elist) /* This routine will determine what, if any, edge there is between a specified predecessor and successor. */ - int find_edge_index (edge_list, pred, succ) struct edge_list *edge_list; @@ -6984,8 +7005,44 @@ add_noreturn_fake_exit_edges () make_edge (NULL, BASIC_BLOCK (x), EXIT_BLOCK_PTR, EDGE_FAKE); } -/* Redirect an edge's successor from one block to another. */ +/* This function adds a fake edge between any infinite loops to the + exit block. Some optimizations require a path from each node to + the exit node. + See also Morgan, Figure 3.10, pp. 82-83. + + The current implementation is ugly, not attempting to minimize the + number of inserted fake edges. To reduce the number of fake edges + to insert, add fake edges from _innermost_ loops containing only + nodes not reachable from the exit block. */ +void +connect_infinite_loops_to_exit () +{ + basic_block unvisited_block; + + /* Perform depth-first search in the reverse graph to find nodes + reachable from the exit block. */ + struct depth_first_search_dsS dfs_ds; + + flow_dfs_compute_reverse_init (&dfs_ds); + flow_dfs_compute_reverse_add_bb (&dfs_ds, EXIT_BLOCK_PTR); + + /* Repeatedly add fake edges, updating the unreachable nodes. */ + while (1) + { + unvisited_block = flow_dfs_compute_reverse_execute (&dfs_ds); + if (!unvisited_block) + break; + make_edge (NULL, unvisited_block, EXIT_BLOCK_PTR, EDGE_FAKE); + flow_dfs_compute_reverse_add_bb (&dfs_ds, unvisited_block); + } + + flow_dfs_compute_reverse_finish (&dfs_ds); + + return; +} + +/* Redirect an edge's successor from one block to another. */ void redirect_edge_succ (e, new_succ) edge e; @@ -7005,7 +7062,6 @@ redirect_edge_succ (e, new_succ) } /* Redirect an edge's predecessor from one block to another. */ - void redirect_edge_pred (e, new_pred) edge e; @@ -7427,6 +7483,115 @@ flow_depth_first_order_compute (dfs_order, rc_order) } +/* Compute the depth first search order on the _reverse_ graph and + store in the array DFS_ORDER, marking the nodes visited in VISITED. + Returns the number of nodes visited. + + The computation is split into three pieces: + + flow_dfs_compute_reverse_init () creates the necessary data + structures. + + flow_dfs_compute_reverse_add_bb () adds a basic block to the data + structures. The block will start the search. + + flow_dfs_compute_reverse_execute () continues (or starts) the + search using the block on the top of the stack, stopping when the + stack is empty. + + flow_dfs_compute_reverse_finish () destroys the necessary data + structures. + + Thus, the user will probably call ..._init(), call ..._add_bb() to + add a beginning basic block to the stack, call ..._execute(), + possibly add another bb to the stack and again call ..._execute(), + ..., and finally call _finish(). */ + +/* Initialize the data structures used for depth-first search on the + reverse graph. If INITIALIZE_STACK is nonzero, the exit block is + added to the basic block stack. DATA is the current depth-first + search context. If INITIALIZE_STACK is non-zero, there is an + element on the stack. */ + +static void +flow_dfs_compute_reverse_init (data) + depth_first_search_ds data; +{ + /* Allocate stack for back-tracking up CFG. */ + data->stack = + (basic_block *) xmalloc ((n_basic_blocks - (INVALID_BLOCK+1)) + * sizeof (basic_block)); + data->sp = 0; + + /* Allocate bitmap to track nodes that have been visited. */ + data->visited_blocks + = sbitmap_alloc (n_basic_blocks - (INVALID_BLOCK + 1)); + + /* None of the nodes in the CFG have been visited yet. */ + sbitmap_zero (data->visited_blocks); + + return; +} + +/* Add the specified basic block to the top of the dfs data + structures. When the search continues, it will start at the + block. */ + +static void +flow_dfs_compute_reverse_add_bb (data, bb) + depth_first_search_ds data; + basic_block bb; +{ + data->stack[data->sp++] = bb; + return; +} + +/* Continue the depth-first search through the reverse graph starting + with the block at the stack's top and ending when the stack is + empty. Visited nodes are marked. Returns an unvisited basic + block, or NULL if there is none available. */ +static basic_block +flow_dfs_compute_reverse_execute (data) + depth_first_search_ds data; +{ + basic_block bb; + edge e; + int i; + + while (data->sp > 0) + { + bb = data->stack[--data->sp]; + + /* Mark that we have visited this node. */ + if (!TEST_BIT (data->visited_blocks, bb->index - (INVALID_BLOCK+1))) + { + SET_BIT (data->visited_blocks, bb->index - (INVALID_BLOCK+1)); + + /* Perform depth-first search on adjacent vertices. */ + for (e = bb->pred; e; e = e->pred_next) + flow_dfs_compute_reverse_add_bb (data, e->src); + } + } + + /* Determine if there are unvisited basic blocks. */ + for (i = n_basic_blocks - (INVALID_BLOCK+1); --i >= 0; ) + if (!TEST_BIT (data->visited_blocks, i)) + return BASIC_BLOCK (i + (INVALID_BLOCK+1)); + return NULL; +} + +/* Destroy the data structures needed for depth-first search on the + reverse graph. */ + +static void +flow_dfs_compute_reverse_finish (data) + depth_first_search_ds data; +{ + free (data->stack); + sbitmap_free (data->visited_blocks); + return; +} + /* Return the block for the pre-header of the loop with header HEADER where DOM specifies the dominator information. Return NULL if there is no pre-header. */ diff --git a/gcc/invoke.texi b/gcc/invoke.texi index 382fb7bb7736168f0ee5b242be5bf87c2b27fd15..912428ba9eef9cf97b47ceb579ce1eae54b2576e 100644 --- a/gcc/invoke.texi +++ b/gcc/invoke.texi @@ -168,7 +168,7 @@ in the following sections. -falign-functions=@var{n} -falign-labels=@var{n} -falign-loops=@var{n} -falign-jumps=@var{n} -fbranch-probabilities -fcaller-saves -fcse-follow-jumps -fcse-skip-blocks --fdelayed-branch -fdelete-null-pointer-checks -fexpensive-optimizations +-fdce -fdelayed-branch -fdelete-null-pointer-checks -fexpensive-optimizations -ffast-math -ffloat-store -fforce-addr -fforce-mem -fno-math-errno -fdata-sections -ffunction-sections -fgcse -finline-functions -finline-limit=@var{n} -fkeep-inline-functions @@ -2900,9 +2900,12 @@ If @var{n} is not specified, use a machine-dependent default. @item -fssa Perform optimizations in static single assignment form. Each function's flow graph is translated into SSA form, optimizations are performed, and -the flow graph is translated back from SSA form. (Currently, no -SSA-based optimizations are implemented, but converting into and out of -SSA form is not an invariant operation, and generated code may differ.) +the flow graph is translated back from SSA form. User's should not +specify this option, since it is not yet ready for production use. + +@item -fdce +Perform dead-code elimination in SSA form. Requires @samp{-fssa}. Like +@samp{-fssa}, this is an experimental feature. @item -fsingle-precision-constant Treat floating point constant as single precision constant instead of diff --git a/gcc/rtl.h b/gcc/rtl.h index b96c53f326744a883ee206e9dd32694fea27befb..41caa802be6b9187f9e85f691d4ea4ee2214fe44 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -148,7 +148,9 @@ typedef struct rtx_def together with the preceding insn. Valid only within sched. 1 in an INSN, JUMP_INSN, or CALL_INSN if insn is in a delay slot and from the target of a branch. Valid from reorg until end of compilation; - cleared before used. */ + cleared before used. + 1 in an INSN if this insn is dead code. Valid only during + dead-code elimination phase; cleared before use. */ unsigned int in_struct : 1; /* 1 if this rtx is used. This is used for copying shared structure. See `unshare_all_rtl'. @@ -202,10 +204,26 @@ typedef struct rtvec_def{ #define GET_NUM_ELEM(RTVEC) ((RTVEC)->num_elem) #define PUT_NUM_ELEM(RTVEC, NUM) ((RTVEC)->num_elem = (NUM)) -/* 1 if X is a REG. */ - +/* Predicate yielding nonzero iff X is an rtl for a register. */ #define REG_P(X) (GET_CODE (X) == REG) +/* Predicate yielding nonzero iff X is a label insn. */ +#define LABEL_P(X) (GET_CODE (X) == CODE_LABEL) + +/* Predicate yielding nonzero iff X is a jump insn. */ +#define JUMP_P(X) (GET_CODE (X) == JUMP_INSN) + +/* Predicate yielding nonzero iff X is a note insn. */ +#define NOTE_P(X) (GET_CODE (X) == NOTE) + +/* Predicate yielding nonzero iff X is a barrier insn. */ +#define BARRIER_P(X) (GET_CODE (X) == BARRIER) + +/* Predicate yielding nonzero iff X is a data for a jump table. */ +#define JUMP_TABLE_DATA_P(INSN) \ + (JUMP_P (INSN) && (GET_CODE (PATTERN (INSN)) == ADDR_VEC || \ + GET_CODE (PATTERN (INSN)) == ADDR_DIFF_VEC)) + /* 1 if X is a constant value that is an integer. */ #define CONSTANT_P(X) \ @@ -374,6 +392,9 @@ extern void rtvec_check_failed_bounds PARAMS ((rtvec, int, delay slots, i.e., it is an annulled branch. */ #define INSN_ANNULLED_BRANCH_P(INSN) ((INSN)->unchanging) +/* 1 if insn is a dead code. Valid only for dead-code elimination phase. */ +#define INSN_DEAD_CODE_P(INSN) ((INSN)->in_struct) + /* 1 if insn is in a delay slot and is from the target of the branch. If the branch insn has INSN_ANNULLED_BRANCH_P set, this insn should only be executed if the branch is taken. For annulled branches with this bit diff --git a/gcc/ssa.c b/gcc/ssa.c index 61ce4ced7e89d3448c136bf9ec88fd304e28e360..bb4bda71aab2ef71a02f0ddb498f0cb85217471e 100644 --- a/gcc/ssa.c +++ b/gcc/ssa.c @@ -46,6 +46,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "recog.h" #include "basic-block.h" #include "output.h" +#include "ssa.h" /* We cannot use <assert.h> in GCC source, since that would include GCC's assert.h, which may not be compatible with the host compiler. */ @@ -91,24 +92,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA left in for a limited time only, as a debugging tool until the coalescing algorithm is validated. */ -/* All pseudo-registers (having register number >= - FIRST_PSEUDO_REGISTER) and hard registers satisfying - CONVERT_HARD_REGISTER_TO_SSA_P are converted to SSA form. */ - -/* Given a hard register number REG_NO, return nonzero if and only if - the register should be converted to SSA. */ - -#ifndef CONVERT_HARD_REGISTER_TO_SSA_P -#define CONVERT_HARD_REGISTER_TO_SSA_P(REG_NO) (0) /* default of no hard registers */ -#endif /* CONVERT_HARD_REGISTER_TO_SSA_P */ - -/* Given a register number REG_NO, return nonzero if and only if the - register should be converted to SSA. */ - -#define CONVERT_REGISTER_TO_SSA_P(REG_NO) \ - ((!HARD_REGISTER_NUM_P (REG_NO)) || \ - (CONVERT_HARD_REGISTER_TO_SSA_P (REG_NO))) - static int conservative_reg_partition; /* This flag is set when the CFG is in SSA form. */ @@ -152,20 +135,20 @@ struct ssa_rename_from_hash_table_data { partition reg_partition; }; -void ssa_rename_from_initialize +static void ssa_rename_from_initialize PARAMS ((void)); -rtx ssa_rename_from_lookup +static rtx ssa_rename_from_lookup PARAMS ((int reg)); -unsigned int original_register +static unsigned int original_register PARAMS ((unsigned int regno)); -void ssa_rename_from_insert +static void ssa_rename_from_insert PARAMS ((unsigned int reg, rtx r)); -void ssa_rename_from_free +static void ssa_rename_from_free PARAMS ((void)); typedef int (*srf_trav) PARAMS ((int regno, rtx r, sbitmap canonical_elements, partition reg_partition)); static void ssa_rename_from_traverse PARAMS ((htab_trav callback_function, sbitmap canonical_elements, partition reg_partition)); -static void ssa_rename_from_print +/*static Avoid warnign message. */ void ssa_rename_from_print PARAMS ((void)); static int ssa_rename_from_print_1 PARAMS ((void **slot, void *data)); @@ -299,7 +282,7 @@ ssa_rename_to_insert(reg, r) /* Prepare ssa_rename_from for use. */ -void +static void ssa_rename_from_initialize () { /* We use an arbitrary initial hash table size of 64. */ @@ -312,7 +295,7 @@ ssa_rename_from_initialize () /* Find the REG entry in ssa_rename_from. Return NULL_RTX if no entry is found. */ -rtx +static rtx ssa_rename_from_lookup (reg) int reg; { @@ -329,7 +312,7 @@ ssa_rename_from_lookup (reg) the register is a pseudo, return the original register's number. Otherwise, return this register number REGNO. */ -unsigned int +static unsigned int original_register (regno) unsigned int regno; { @@ -339,7 +322,7 @@ original_register (regno) /* Add mapping from R to REG to ssa_rename_from even if already present. */ -void +static void ssa_rename_from_insert (reg, r) unsigned int reg; rtx r; @@ -359,7 +342,7 @@ ssa_rename_from_insert (reg, r) CANONICAL_ELEMENTS and REG_PARTITION pass data needed by the only current use of this function. */ -void +static void ssa_rename_from_traverse (callback_function, canonical_elements, reg_partition) htab_trav callback_function; @@ -374,7 +357,7 @@ ssa_rename_from_traverse (callback_function, /* Destroy ssa_rename_from. */ -void +static void ssa_rename_from_free () { htab_delete (ssa_rename_from_ht); @@ -382,7 +365,8 @@ ssa_rename_from_free () /* Print the contents of ssa_rename_from. */ -static void +/* static Avoid erroneous error message. */ +void ssa_rename_from_print () { printf ("ssa_rename_from's hash table contents:\n"); @@ -1146,9 +1130,6 @@ rename_registers (nregs, idom) int nregs; int *idom; { - int reg; - int mach_mode; - VARRAY_RTX_INIT (ssa_definition, nregs * 3, "ssa_definition"); VARRAY_RTX_INIT (ssa_uses, nregs * 3, "ssa_uses"); ssa_rename_from_initialize (); diff --git a/gcc/ssa.h b/gcc/ssa.h new file mode 100644 index 0000000000000000000000000000000000000000..463fef8b60cabde1f2d86e57448e73a1626aea88 --- /dev/null +++ b/gcc/ssa.h @@ -0,0 +1,64 @@ +/* Static Single Assignment (SSA) definitions for GNU C-Compiler + Copyright (C) 2000 Free Software Foundation, Inc. + Written by Jeffrey D. Oldham <oldham@codesourcery.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 Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + + +/* Main SSA routines. */ +extern void convert_to_ssa PARAMS ((void)); +extern void convert_from_ssa PARAMS ((void)); +typedef int (*successor_phi_fn) PARAMS ((rtx, int, int, void *)); +extern int for_each_successor_phi PARAMS ((basic_block bb, + successor_phi_fn, + void *)); + +/* Optimizations. */ +/* In dce.c */ +extern void eliminate_dead_code PARAMS ((void)); + +/* SSA definitions and uses. */ +/* This flag is set when the CFG is in SSA form. */ +extern int in_ssa_form; + +/* Element I is the single instruction that sets register I. */ +extern varray_type ssa_definition; + +/* Element I is an INSN_LIST of instructions that use register I. */ +extern varray_type ssa_uses; + + +/* Specify which hard registers should be converted. */ + +/* All pseudo-registers (having register number >= + FIRST_PSEUDO_REGISTER) and hard registers satisfying + CONVERT_HARD_REGISTER_TO_SSA_P are converted to SSA form. */ + +/* Given a hard register number REG_NO, return nonzero if and only if + the register should be converted to SSA. */ + +#ifndef CONVERT_HARD_REGISTER_TO_SSA_P +#define CONVERT_HARD_REGISTER_TO_SSA_P(REG_NO) (0) /* default of no hard registers */ +#endif /* CONVERT_HARD_REGISTER_TO_SSA_P */ + +/* Given a register number REG_NO, return nonzero if and only if the + register should be converted to SSA. */ + +#define CONVERT_REGISTER_TO_SSA_P(REG_NO) \ + ((!HARD_REGISTER_NUM_P (REG_NO)) || \ + (CONVERT_HARD_REGISTER_TO_SSA_P (REG_NO))) diff --git a/gcc/timevar.def b/gcc/timevar.def index 2f3458a91110574b1bd510e908109a0782d0f54c..19a3bb26e3bd4d8389105c7f02bf8e6c8f4a7705 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -68,6 +68,7 @@ DEFTIMEVAR (TV_REORDER_BLOCKS , "reorder blocks") DEFTIMEVAR (TV_SHORTEN_BRANCH , "shorten branches") DEFTIMEVAR (TV_REG_STACK , "reg stack") DEFTIMEVAR (TV_TO_SSA , "convert to SSA") +DEFTIMEVAR (TV_DEAD_CODE_ELIM , "eliminate dead code") DEFTIMEVAR (TV_FROM_SSA , "convert from SSA") DEFTIMEVAR (TV_FINAL , "final") DEFTIMEVAR (TV_SYMOUT , "symout") diff --git a/gcc/toplev.c b/gcc/toplev.c index fde14d16b5f1f8319fb3926a9bdd6de38aac833b..b13e580ce59141c50c0a8a8d522b1dad5ba0731c 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -63,6 +63,7 @@ Boston, MA 02111-1307, USA. */ #include "regs.h" #include "timevar.h" #include "diagnostic.h" +#include "ssa.h" #ifndef ACCUMULATE_OUTGOING_ARGS #define ACCUMULATE_OUTGOING_ARGS 0 @@ -259,6 +260,7 @@ enum dump_file_index DFI_cse, DFI_addressof, DFI_ssa, + DFI_dce, DFI_ussa, DFI_gcse, DFI_loop, @@ -291,7 +293,7 @@ enum dump_file_index Remaining -d letters: " h o q u " - " H K OPQ TUVWXYZ" + " H K OPQ TUVW YZ" */ struct dump_file_info dump_file[DFI_MAX] = @@ -302,6 +304,7 @@ struct dump_file_info dump_file[DFI_MAX] = { "cse", 's', 0, 0, 0 }, { "addressof", 'F', 0, 0, 0 }, { "ssa", 'e', 1, 0, 0 }, + { "dce", 'X', 1, 0, 0 }, { "ussa", 'e', 1, 0, 0 }, /* Yes, duplicate enable switch. */ { "gcse", 'G', 1, 0, 0 }, { "loop", 'L', 1, 0, 0 }, @@ -786,6 +789,9 @@ int flag_gnu_linker = 1; /* Enable SSA. */ int flag_ssa = 0; +/* Enable dead code elimination. */ +int flag_dce = 0; + /* Tag all structures with __attribute__(packed) */ int flag_pack_struct = 0; @@ -1094,6 +1100,8 @@ lang_independent_options f_options[] = "Instrument function entry/exit with profiling calls"}, {"ssa", &flag_ssa, 1, "Enable SSA optimizations" }, + {"dce", &flag_dce, 1, + "Enable dead code elimination" }, {"leading-underscore", &flag_leading_underscore, 1, "External symbols have a leading underscore" }, {"ident", &flag_no_ident, 0, @@ -2976,13 +2984,25 @@ rest_of_compilation (decl) close_dump_file (DFI_ssa, print_rtl_with_bb, insns); timevar_pop (TV_TO_SSA); - /* Currently, there's nothing to do in SSA form. */ - /* The SSA implementation uses basic block numbers in its phi nodes. Thus, changing the control-flow graph or the basic blocks, e.g., calling find_basic_blocks () or cleanup_cfg (), may cause problems. */ + if (flag_dce) + { + /* Remove dead code. */ + + timevar_push (TV_DEAD_CODE_ELIM); + open_dump_file (DFI_dce, decl); + + insns = get_insns (); + eliminate_dead_code(); + + close_dump_file (DFI_dce, print_rtl_with_bb, insns); + timevar_pop (TV_DEAD_CODE_ELIM); + } + /* Convert from SSA form. */ timevar_push (TV_FROM_SSA);