diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 9c0ad71cae24122797792a9082f1fb092c01c8c1..5686f48a7f546093fc9cfc8139225fefc9e897d1 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,19 @@
+2001-02-22  Jakub Jelinek  <jakub@redhat.com>
+
+	* cp-tree.h (struct lang_decl_inlined_fns): New.
+	(struct lang_decls): Add inlined_fns.
+	(DECL_INLINED_FNS): New macro.
+	* optimize.c (struct inline_data): Add inlined_fns.
+	(declare_return_variable): Use VARRAY_ACTIVE_SIZE macro.
+	(inlinable_function_p): Likewise, fix typo in comment,
+	function is not inlinable if it already inlined function currently
+	being optimized.
+	(expand_call_inline): Add fn to inlined_fns if neccessary.
+	(optimize_function): Initialize inlined_fns.
+	Save inlined_fns into DECL_INLINED_FNS after expanding inlines.
+	* decl.c (mark_inlined_fns): New function.
+	(lang_mark_tree): Call it.
+
 2001-02-21  Jason Merrill  <jason@redhat.com>
 
 	* cp-tree.h (struct lang_decl_flags): Remove uninlinable flag.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 240832a9c717ade679d38cfe4dee6c64419a5dcb..2aca7d1de1fa17144abec7273f90a105da91a4ad 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1855,6 +1855,12 @@ struct lang_decl_flags
 
 struct unparsed_text;
 
+struct lang_decl_inlined_fns
+{
+  size_t num_fns;
+  tree fns[1];
+};
+
 struct lang_decl
 {
   struct lang_decl_flags decl_flags;
@@ -1868,6 +1874,9 @@ struct lang_decl
   /* In a FUNCTION_DECL, this is DECL_CLONED_FUNCTION.  */
   tree cloned_function;
 
+  /* In a FUNCTION_DECL, this is a list of trees inlined into its body.  */
+  struct lang_decl_inlined_fns *inlined_fns;
+
   union
   {
     tree sorted_fields;
@@ -1978,6 +1987,10 @@ struct lang_decl
 #define DECL_CLONED_FUNCTION(NODE) \
   (DECL_LANG_SPECIFIC (NODE)->cloned_function)
 
+/* List of FUNCION_DECLs inlined into this function's body.  */
+#define DECL_INLINED_FNS(NODE) \
+  (DECL_LANG_SPECIFIC (NODE)->inlined_fns)
+
 /* Non-zero if the VTT parm has been added to NODE.  */
 #define DECL_HAS_VTT_PARM_P(NODE) \
   (DECL_LANG_SPECIFIC (NODE)->decl_flags.has_vtt_parm_p)
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 85e9fa6b178f85eef2e2f8b73382df760fc04033..39008956f4af09d8b6d3e3db84b0511c97430be6 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -14332,6 +14332,19 @@ pop_cp_function_context (f)
   f->language = 0;
 }
 
+/* Mark I for GC.  */
+
+static void
+mark_inlined_fns (i)
+     struct lang_decl_inlined_fns *i;
+{
+  int n;
+
+  for (n = i->num_fns - 1; n >= 0; n--)
+    ggc_mark_tree (i->fns [n]);
+  ggc_set_mark (i);
+}
+
 /* Mark P for GC.  */
 
 static void
@@ -14418,6 +14431,8 @@ lang_mark_tree (t)
 	      ggc_mark_tree (ld->befriending_classes);
 	      ggc_mark_tree (ld->context);
 	      ggc_mark_tree (ld->cloned_function);
+	      if (ld->inlined_fns)
+		mark_inlined_fns (ld->inlined_fns);
 	      if (TREE_CODE (t) == TYPE_DECL)
 		ggc_mark_tree (ld->u.sorted_fields);
 	      else if (TREE_CODE (t) == FUNCTION_DECL
diff --git a/gcc/cp/optimize.c b/gcc/cp/optimize.c
index 21c94eafee68a31122952ab18fb93862c6a36275..9850c6ff9718f16636c9aec39c18c4fbc4e26ba9 100644
--- a/gcc/cp/optimize.c
+++ b/gcc/cp/optimize.c
@@ -28,6 +28,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "input.h"
 #include "integrate.h"
 #include "varray.h"
+#include "ggc.h"
 
 /* To Do:
 
@@ -62,6 +63,8 @@ typedef struct inline_data
   int in_target_cleanup_p;
   /* A stack of the TARGET_EXPRs that we are currently processing.  */
   varray_type target_exprs;
+  /* A list of the functions current function has inlined.  */
+  varray_type inlined_fns;
 } inline_data;
 
 /* Prototypes.  */
@@ -520,7 +523,7 @@ declare_return_variable (id, use_stmt)
      references to the RESULT into references to the target.  */
   if (aggregate_return_p)
     {
-      my_friendly_assert (id->target_exprs->elements_used != 0,
+      my_friendly_assert (VARRAY_ACTIVE_SIZE (id->target_exprs) != 0,
 			  20000430);
       var = TREE_OPERAND (VARRAY_TOP_TREE (id->target_exprs), 0);
       my_friendly_assert 
@@ -605,14 +608,23 @@ inlinable_function_p (fn, id)
     inlinable = 0;
 
   /* Don't do recursive inlining, either.  We don't record this in
-     DECL_UNLINABLE; we may be able to inline this function later.  */
+     DECL_UNINLINABLE; we may be able to inline this function later.  */
   if (inlinable)
     {
       size_t i;
 
-      for (i = 0; i < id->fns->elements_used; ++i)
+      for (i = 0; i < VARRAY_ACTIVE_SIZE (id->fns); ++i)
 	if (VARRAY_TREE (id->fns, i) == fn)
-	  inlinable = 0;
+	  return 0;
+
+      if (inlinable && DECL_LANG_SPECIFIC (fn) && DECL_INLINED_FNS (fn))
+	{
+	  struct lang_decl_inlined_fns *ifn = DECL_INLINED_FNS (fn);
+
+	  for (i = 0; i < ifn->num_fns; ++i)
+	    if (ifn->fns [i] == VARRAY_TREE (id->fns, 0))
+	      return 0;
+	}
     }
 
   /* Return the result.  */
@@ -728,6 +740,19 @@ expand_call_inline (tp, walk_subtrees, data)
      recursing into it.  */
   VARRAY_PUSH_TREE (id->fns, fn);
 
+  /* Record the function we are about to inline if optimize_function
+     has not been called on it yet and we don't have it in the list.  */
+  if (DECL_LANG_SPECIFIC (fn) && !DECL_INLINED_FNS (fn))
+    {
+      int i;
+
+      for (i = VARRAY_ACTIVE_SIZE (id->inlined_fns) - 1; i >= 0; i--)
+	if (VARRAY_TREE (id->inlined_fns, i) == fn)
+	  break;
+      if (i < 0)
+	VARRAY_PUSH_TREE (id->inlined_fns, fn);
+    }
+
   /* Return statements in the function body will be replaced by jumps
      to the RET_LABEL.  */
   id->ret_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
@@ -874,6 +899,9 @@ optimize_function (fn)
       /* Create the stack of TARGET_EXPRs.  */
       VARRAY_TREE_INIT (id.target_exprs, 32, "target_exprs");
 
+      /* Create the list of functions this call will inline.  */
+      VARRAY_TREE_INIT (id.inlined_fns, 32, "inlined_fns");
+
       /* Replace all calls to inline functions with the bodies of those
 	 functions.  */
       expand_calls_inline (&DECL_SAVED_TREE (fn), &id);
@@ -881,6 +909,19 @@ optimize_function (fn)
       /* Clean up.  */
       VARRAY_FREE (id.fns);
       VARRAY_FREE (id.target_exprs);
+      if (DECL_LANG_SPECIFIC (fn))
+	{
+	  struct lang_decl_inlined_fns *ifn;
+
+	  ifn = ggc_alloc (sizeof (struct lang_decl_inlined_fns)
+			   + (VARRAY_ACTIVE_SIZE (id.inlined_fns) - 1)
+			     * sizeof (tree));
+	  ifn->num_fns = VARRAY_ACTIVE_SIZE (id.inlined_fns);
+	  memcpy (&ifn->fns[0], &VARRAY_TREE (id.inlined_fns, 0),
+		  ifn->num_fns * sizeof (tree));
+	  DECL_INLINED_FNS (fn) = ifn;
+	}
+      VARRAY_FREE (id.inlined_fns);
     }
 
   /* Undo the call to ggc_push_context above.  */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 682b3a973b3fe58fdfbb6dbfca5aea4ae0dab2ee..a41d374f5c8aa1765477610834e2326661d1de48 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2001-02-22  Jakub Jelinek  <jakub@redhat.com>
+
+	* g++.old-deja/g++.other/inline20.C: New test.
+
 Wed Feb 21 12:38:22 2001  Ovidiu Predescu  <ovidiu@cup.hp.com>
 
 	* objc/execute/nested-1.m: Added (contributed by Nicola Pero).
diff --git a/gcc/testsuite/g++.old-deja/g++.other/inline20.C b/gcc/testsuite/g++.old-deja/g++.other/inline20.C
new file mode 100644
index 0000000000000000000000000000000000000000..a4af375f1642e1d25f46c88c08b0da33e3392d28
--- /dev/null
+++ b/gcc/testsuite/g++.old-deja/g++.other/inline20.C
@@ -0,0 +1,58 @@
+// Build don't link:
+
+struct A {
+  int a, b, c, d;
+};
+
+inline void foo (int, A &);
+
+struct D {
+};
+
+struct E: public D {
+  void f (A &y)
+  {
+    foo (1, y);
+  }
+};
+
+struct F: public D {
+  void f (A &y)
+  {
+    foo (2, y);
+  }
+};
+
+E *d;
+F *e;
+
+inline int baz (int y)
+{
+  A a;
+  if (y) {
+    d->f (a);
+  } else {
+    e->f (a);
+  }
+  return 0;
+}
+
+inline void foo (int y, A &z)
+{
+  z.a = baz (y);
+  z.b = baz (y);
+  z.c = baz (y);
+  z.d = baz (y);
+}
+
+struct G {
+  E a;
+  F b;
+  void bar (A &);
+};
+
+void G::bar(A &y)
+{
+  a.f(y);
+  b.f(y);
+}