diff --git a/gcc/cgraph.h b/gcc/cgraph.h

index 8ab7ae1..2132d91 100644

--- a/gcc/cgraph.h

+++ b/gcc/cgraph.h

@@ -251,6 +251,15 @@ struct GTY(()) cgraph_node {

   /* Unique id of the node.  */

   int uid;

 

+  /* Call unsaved hard registers really used by the corresponding

+     function (including ones used by functions called by the

+     function).  */

+  HARD_REG_SET function_used_regs;

+  /* Set if function_used_regs is initialized.  */

+  unsigned function_used_regs_initialized: 1;

+  /* Set if function_used_regs is valid.  */

+  unsigned function_used_regs_valid: 1;

+

   /* Set when decl is an abstract function pointed to by the

      ABSTRACT_DECL_ORIGIN of a reachable function.  */

   unsigned abstract_and_needed : 1;

diff --git a/gcc/final.c b/gcc/final.c

index d25b8e0..4e0fd01 100644

--- a/gcc/final.c

+++ b/gcc/final.c

@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see

 #include "tm.h"

 

 #include "tree.h"

+#include "hard-reg-set.h"

 #include "rtl.h"

 #include "tm_p.h"

 #include "regs.h"

@@ -56,7 +57,6 @@ along with GCC; see the file COPYING3.  If not see

 #include "recog.h"

 #include "conditions.h"

 #include "flags.h"

-#include "hard-reg-set.h"

 #include "output.h"

 #include "except.h"

 #include "function.h"

@@ -222,6 +222,7 @@ static int alter_cond (rtx);

 static int final_addr_vec_align (rtx);

 #endif

 static int align_fuzz (rtx, rtx, int, unsigned);

+static void collect_fn_hard_reg_usage (void);

 

 /* Initialize data in final at the beginning of a compilation.  */

 

@@ -4328,6 +4329,8 @@ rest_of_handle_final (void)

   rtx x;

   const char *fnname;

 

+  collect_fn_hard_reg_usage ();

+

   /* Get the function's name, as described by its RTL.  This may be

      different from the DECL_NAME name used in the source file.  */

 

@@ -4584,3 +4587,121 @@ struct rtl_opt_pass pass_clean_state =

   0                                     /* todo_flags_finish */

  }

 };

+

+/* Collect hard register usage for the current function.  */

+

+static void

+collect_fn_hard_reg_usage (void)

+{

+  rtx insn;

+  int i;

+  struct cgraph_node *node;

+  struct hard_reg_set_container other_usage;

+

+  if (!flag_use_caller_save)

+    return;

+

+  node = cgraph_get_node (current_function_decl);

+  gcc_assert (node != NULL);

+

+  gcc_assert (!node->function_used_regs_initialized);

+  node->function_used_regs_initialized = 1;

+

+  for (insn = get_insns (); insn != NULL_RTX; insn = next_insn (insn))

+    {

+      HARD_REG_SET insn_used_regs;

+

+      if (!NONDEBUG_INSN_P (insn))

+	continue;

+

+      find_all_hard_reg_sets (insn, &insn_used_regs, false);

+

+      if (CALL_P (insn)

+	  && !get_call_reg_set_usage (insn, &insn_used_regs, call_used_reg_set))

+	{

+	  CLEAR_HARD_REG_SET (node->function_used_regs);

+	  return;

+	}

+

+      IOR_HARD_REG_SET (node->function_used_regs, insn_used_regs);

+    }

+

+  /* Be conservative - mark fixed and global registers as used.  */

+  IOR_HARD_REG_SET (node->function_used_regs, fixed_reg_set);

+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)

+    if (global_regs[i])

+      SET_HARD_REG_BIT (node->function_used_regs, i);

+

+#ifdef STACK_REGS

+  /* Handle STACK_REGS conservatively, since the df-framework does not

+     provide accurate information for them.  */

+

+  for (i = FIRST_STACK_REG; i <= LAST_STACK_REG; i++)

+    SET_HARD_REG_BIT (node->function_used_regs, i);

+#endif

+

+  CLEAR_HARD_REG_SET (other_usage.set);

+  targetm.fn_other_hard_reg_usage (&other_usage);

+  IOR_HARD_REG_SET (node->function_used_regs, other_usage.set);

+

+  node->function_used_regs_valid = 1;

+}

+

+/* Get the declaration of the function called by INSN.  */

+

+static tree

+get_call_fndecl (rtx insn)

+{

+  rtx note, datum;

+

+  if (!flag_use_caller_save)

+    return NULL_TREE;

+

+  note = find_reg_note (insn, REG_CALL_DECL, NULL_RTX);

+  if (note == NULL_RTX)

+    return NULL_TREE;

+

+  datum = XEXP (note, 0);

+  if (datum != NULL_RTX)

+    return SYMBOL_REF_DECL (datum);

+

+  return NULL_TREE;

+}

+

+static struct cgraph_node *

+get_call_cgraph_node (rtx insn)

+{

+  tree fndecl;

+

+  if (insn == NULL_RTX)

+    return NULL;

+

+  fndecl = get_call_fndecl (insn);

+  if (fndecl == NULL_TREE

+      || !targetm.binds_local_p (fndecl))

+    return NULL;

+

+  return cgraph_get_node (fndecl);

+}

+

+/* Find hard registers used by function call instruction INSN, and return them

+   in REG_SET.  Return DEFAULT_SET in REG_SET if not found.  */

+

+bool

+get_call_reg_set_usage (rtx insn, HARD_REG_SET *reg_set,

+			HARD_REG_SET default_set)

+{

+  struct cgraph_node *node = get_call_cgraph_node (insn);

+  if (node != NULL

+      && node->function_used_regs_valid)

+    {

+      COPY_HARD_REG_SET (*reg_set, node->function_used_regs);

+      AND_HARD_REG_SET (*reg_set, default_set);

+      return true;

+    }

+  else

+    {

+      COPY_HARD_REG_SET (*reg_set, default_set);

+      return false;

+    }

+}

diff --git a/gcc/regs.h b/gcc/regs.h

index 090d6b6..ec71ad4 100644

--- a/gcc/regs.h

+++ b/gcc/regs.h

@@ -421,4 +421,8 @@ range_in_hard_reg_set_p (const HARD_REG_SET set, unsigned regno, int nregs)

   return true;

 }

 

+/* Get registers used by given function call instruction.  */

+extern bool get_call_reg_set_usage (rtx insn, HARD_REG_SET *reg_set,

+				    HARD_REG_SET default_set);

+

 #endif /* GCC_REGS_H */
