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 (&current_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 (&current_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);