diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index a27835d9ff6acbff4916f436eeffd5974b312afa..07d016c072603ae9b8d963a233ef15496318e746 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,12 @@
+2003-06-24  Zdenek Dvorak  <rakdver@atrey.karlin.mff.cuni.cz>
+
+	* Makefile.in (cfgrtl.o): Add expr.h dependency.
+	* cfgrtl.c: Include expr.h.
+	(mark_killed_regs, safe_insert_insn_on_edge): New
+	functions.
+	* config/i386/i386.h (AVOID_CCMODE_COPIES): Define.
+	* basic-block.h (safe_insert_insn_on_edge): Declare.
+
 2003-06-26  Neil Booth  <neil@daikokuya.co.uk>
 
 	* c-opts.c (missing_arg): Make non-static.
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index a3cc5df30ecc9d0325a480eb4caafe16bc768f44..5029c52db229660339297e8649b02a3d9854b2c6 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1659,7 +1659,7 @@ cfghooks.o: cfghooks.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TR
    $(BASIC_BLOCK_H) cfglayout.h
 cfgrtl.o : cfgrtl.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h \
    insn-config.h $(BASIC_BLOCK_H) $(REGS_H) hard-reg-set.h output.h toplev.h $(RECOG_H) \
-   function.h except.h $(GGC_H) $(TM_P_H) insn-config.h
+   function.h except.h $(GGC_H) $(TM_P_H) insn-config.h $(EXPR_H)
 cfganal.o : cfganal.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \
    $(BASIC_BLOCK_H) hard-reg-set.h insn-config.h $(RECOG_H) $(GGC_H) $(TM_P_H)
 cfgbuild.o : cfgbuild.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) flags.h \
diff --git a/gcc/basic-block.h b/gcc/basic-block.h
index 40775b9ad9a0b5e6e2b89a6d55de03fdc485a7da..b26b14399d11122158b1ce4f8c370dbb8efd63a6 100644
--- a/gcc/basic-block.h
+++ b/gcc/basic-block.h
@@ -340,6 +340,7 @@ extern void update_bb_for_insn		PARAMS ((basic_block));
 extern void free_basic_block_vars	PARAMS ((int));
 
 extern void insert_insn_on_edge		PARAMS ((rtx, edge));
+bool safe_insert_insn_on_edge (rtx, edge);
 
 extern void commit_edge_insertions	PARAMS ((void));
 extern void commit_edge_insertions_watch_calls	PARAMS ((void));
diff --git a/gcc/cfgrtl.c b/gcc/cfgrtl.c
index 569a6049882c93c28a244fdff0fcefe05eb5147a..a08d29f3da94da025d181a60f039e04c8c9dfa63 100644
--- a/gcc/cfgrtl.c
+++ b/gcc/cfgrtl.c
@@ -56,6 +56,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "obstack.h"
 #include "insn-config.h"
 #include "cfglayout.h"
+#include "expr.h"
 
 /* Stubs in case we don't have a return insn.  */
 #ifndef HAVE_return
@@ -88,6 +89,7 @@ static bool rtl_redirect_edge_and_branch (edge, basic_block);
 static edge rtl_split_block (basic_block, void *);
 static void rtl_dump_bb (basic_block, FILE *);
 static int rtl_verify_flow_info_1 (void);
+static void mark_killed_regs (rtx, rtx, void *);
 
 /* Return true if NOTE is not one of the ones that must be kept paired,
    so that we may simply delete it.  */
@@ -1305,6 +1307,101 @@ insert_insn_on_edge (rtx pattern, edge e)
   end_sequence ();
 }
 
+/* Called from safe_insert_insn_on_edge through note_stores, marks live
+   registers that are killed by the store.  */
+static void
+mark_killed_regs (rtx reg, rtx set ATTRIBUTE_UNUSED, void *data)
+{
+  regset killed = data;
+  int regno, i;
+
+  if (GET_CODE (reg) == SUBREG)
+    reg = SUBREG_REG (reg);
+  if (!REG_P (reg))
+    return;
+  regno = REGNO (reg);
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    SET_REGNO_REG_SET (killed, regno);
+  else
+    {
+      for (i = 0; i < HARD_REGNO_NREGS (regno, GET_MODE (reg)); i++)
+	SET_REGNO_REG_SET (killed, regno + i);
+    }
+}
+
+/* Similar to insert_insn_on_edge, tries to put INSN to edge E.  Additionally
+   it checks whether this will not clobber the registers that are live on the
+   edge (i.e. it requieres liveness information to be up-to-date) and if there
+   are some, then it tries to save and restore them.  Returns true if
+   succesful.  */
+bool
+safe_insert_insn_on_edge (rtx insn, edge e)
+{
+  rtx x;
+  regset_head killed_head;
+  regset killed = INITIALIZE_REG_SET (killed_head);
+  rtx save_regs = NULL_RTX;
+  int regno, noccmode;
+  enum machine_mode mode;
+
+#ifdef AVOID_CCMODE_COPIES
+  noccmode = true;
+#else
+  noccmode = false;
+#endif
+
+  for (x = insn; x; x = NEXT_INSN (x))
+    if (INSN_P (x))
+      note_stores (PATTERN (x), mark_killed_regs, killed);
+  bitmap_operation (killed, killed, e->dest->global_live_at_start,
+		    BITMAP_AND);
+
+  EXECUTE_IF_SET_IN_REG_SET (killed, 0, regno,
+    {
+      mode = regno < FIRST_PSEUDO_REGISTER
+	      ? reg_raw_mode[regno]
+	      : GET_MODE (regno_reg_rtx[regno]);
+      if (mode == VOIDmode)
+	return false;
+
+      if (noccmode && mode == CCmode)
+	return false;
+	
+      save_regs = alloc_EXPR_LIST (0,
+				   alloc_EXPR_LIST (0,
+						    gen_reg_rtx (mode),
+						    gen_raw_REG (mode, regno)),
+				   save_regs);
+    });
+
+  if (save_regs)
+    {
+      rtx from, to;
+
+      start_sequence ();
+      for (x = save_regs; x; x = XEXP (x, 1))
+	{
+	  from = XEXP (XEXP (x, 0), 1);
+	  to = XEXP (XEXP (x, 0), 0);
+	  emit_move_insn (to, from);
+	}
+      emit_insn (insn);
+      for (x = save_regs; x; x = XEXP (x, 1))
+	{
+	  from = XEXP (XEXP (x, 0), 0);
+	  to = XEXP (XEXP (x, 0), 1);
+	  emit_move_insn (to, from);
+	}
+      insn = get_insns ();
+      end_sequence ();
+      free_EXPR_LIST_list (&save_regs);
+    }
+  insert_insn_on_edge (insn, e);
+  
+  FREE_REG_SET (killed);
+  return true;
+}
+
 /* Update the CFG for the instructions queued on edge E.  */
 
 static void
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 5f390f0e1552676cf1ec3319d154aa11ed0f8c63..c115f6a21997a959e95b05974fa962a2c4d293df 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1123,6 +1123,9 @@ do {									\
 	       && (TARGET_64BIT || !TARGET_PARTIAL_REG_STALL))	\
 	   || ((MODE2) == DImode && TARGET_64BIT))))
 
+/* It is possible to write patterns to move flags; but until someone
+   does it,  */
+#define AVOID_CCMODE_COPIES
 
 /* Specify the modes required to caller save a given hard regno.
    We do this on i386 to prevent flags from being saved at all.