Index: ipa-inline.c
===================================================================
--- ipa-inline.c	(revision 257174)
+++ ipa-inline.c	(working copy)
@@ -289,18 +289,16 @@ sanitize_attrs_match_for_inline_p (const
       (opts_for_fn (caller->decl)->x_##flag		\
        != opts_for_fn (callee->decl)->x_##flag)
 
- /* Decide if we can inline the edge and possibly update
+/* Decide if we can inline the edge and possibly update
    inline_failed reason.  
    We check whether inlining is possible at all and whether
    caller growth limits allow doing so.  
 
-   if REPORT is true, output reason to the dump file.  
-
-   if DISREGARD_LIMITS is true, ignore size limits.*/
+   if REPORT is true, output reason to the dump file. */
 
 static bool
 can_inline_edge_p (struct cgraph_edge *e, bool report,
-		   bool disregard_limits = false, bool early = false)
+		   bool early = false)
 {
   gcc_checking_assert (e->inline_failed);
 
@@ -316,9 +314,6 @@ can_inline_edge_p (struct cgraph_edge *e
   cgraph_node *caller = e->caller->global.inlined_to
 		        ? e->caller->global.inlined_to : e->caller;
   cgraph_node *callee = e->callee->ultimate_alias_target (&avail, caller);
-  tree caller_tree = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (caller->decl);
-  tree callee_tree
-    = callee ? DECL_FUNCTION_SPECIFIC_OPTIMIZATION (callee->decl) : NULL;
 
   if (!callee->definition)
     {
@@ -379,12 +374,47 @@ can_inline_edge_p (struct cgraph_edge *e
       e->inline_failed = CIF_ATTRIBUTE_MISMATCH;
       inlinable = false;
     }
+  if (!inlinable && report)
+    report_inline_failed_reason (e);
+  return inlinable;
+}
+
+/* Decide if we can inline the edge and possibly update
+   inline_failed reason.  
+   We check whether inlining is possible at all and whether
+   caller growth limits allow doing so.  
+
+   if REPORT is true, output reason to the dump file.
+
+   if DISREGARD_LIMITS is true, ignore size limits.  */
+
+static bool
+can_inline_edge_by_limits_p (struct cgraph_edge *e, bool report,
+		             bool disregard_limits = false, bool early = false)
+{
+  gcc_checking_assert (e->inline_failed);
+
+  if (cgraph_inline_failed_type (e->inline_failed) == CIF_FINAL_ERROR)
+    {
+      if (report)
+        report_inline_failed_reason (e);
+      return false;
+    }
+
+  bool inlinable = true;
+  enum availability avail;
+  cgraph_node *caller = e->caller->global.inlined_to
+		        ? e->caller->global.inlined_to : e->caller;
+  cgraph_node *callee = e->callee->ultimate_alias_target (&avail, caller);
+  tree caller_tree = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (caller->decl);
+  tree callee_tree
+    = callee ? DECL_FUNCTION_SPECIFIC_OPTIMIZATION (callee->decl) : NULL;
   /* Check if caller growth allows the inlining.  */
-  else if (!DECL_DISREGARD_INLINE_LIMITS (callee->decl)
-	   && !disregard_limits
-	   && !lookup_attribute ("flatten",
-				 DECL_ATTRIBUTES (caller->decl))
-           && !caller_growth_limits (e))
+  if (!DECL_DISREGARD_INLINE_LIMITS (callee->decl)
+      && !disregard_limits
+      && !lookup_attribute ("flatten",
+     		 DECL_ATTRIBUTES (caller->decl))
+      && !caller_growth_limits (e))
     inlinable = false;
   /* Don't inline a function with a higher optimization level than the
      caller.  FIXME: this is really just tip of iceberg of handling
@@ -541,7 +571,8 @@ can_early_inline_edge_p (struct cgraph_e
 	fprintf (dump_file, "  edge not inlinable: not in SSA form\n");
       return false;
     }
-  if (!can_inline_edge_p (e, true, false, true))
+  if (!can_inline_edge_p (e, true, true)
+      || !can_inline_edge_by_limits_p (e, true, false, true))
     return false;
   return true;
 }
@@ -925,6 +956,8 @@ check_callers (struct cgraph_node *node,
          return true;
        if (e->recursive_p ())
 	 return true;
+       if (!can_inline_edge_by_limits_p (e, true))
+         return true;
        if (!(*(bool *)has_hot_call) && e->maybe_hot_p ())
 	 *(bool *)has_hot_call = true;
      }
@@ -1317,8 +1350,9 @@ update_caller_keys (edge_heap_t *heap, s
         if (!check_inlinablity_for
 	    || check_inlinablity_for == edge)
 	  {
-	    if (want_inline_small_function_p (edge, false)
-		&& can_inline_edge_p (edge, false))
+	    if (can_inline_edge_p (edge, false)
+		&& want_inline_small_function_p (edge, false)
+		&& can_inline_edge_by_limits_p (edge, false))
 	      update_edge_key (heap, edge);
 	    else if (edge->aux)
 	      {
@@ -1361,8 +1395,9 @@ update_callee_keys (edge_heap_t *heap, s
 	    && avail >= AVAIL_AVAILABLE
 	    && !bitmap_bit_p (updated_nodes, callee->uid))
 	  {
-	    if (want_inline_small_function_p (e, false)
-		&& can_inline_edge_p (e, false))
+	    if (can_inline_edge_p (e, false)
+		&& want_inline_small_function_p (e, false)
+		&& can_inline_edge_by_limits_p (e, false))
 	      update_edge_key (heap, e);
 	    else if (e->aux)
 	      {
@@ -1449,7 +1484,8 @@ recursive_inlining (struct cgraph_edge *
       struct cgraph_edge *curr = heap.extract_min ();
       struct cgraph_node *cnode, *dest = curr->callee;
 
-      if (!can_inline_edge_p (curr, true))
+      if (!can_inline_edge_p (curr, true)
+	  || can_inline_edge_by_limits_p (curr, true))
 	continue;
 
       /* MASTER_CLONE is produced in the case we already started modified
@@ -1569,7 +1605,8 @@ add_new_edges_to_heap (edge_heap_t *heap
       gcc_assert (!edge->aux);
       if (edge->inline_failed
 	  && can_inline_edge_p (edge, true)
-	  && want_inline_small_function_p (edge, true))
+	  && want_inline_small_function_p (edge, true)
+	  && can_inline_edge_by_limits_p (edge, true))
         edge->aux = heap->insert (edge_badness (edge, false), edge);
     }
 }
@@ -1630,7 +1667,9 @@ speculation_useful_p (struct cgraph_edge
   if (!anticipate_inlining && e->inline_failed && !target->local.local)
     return false;
   /* For overwritable targets there is not much to do.  */
-  if (e->inline_failed && !can_inline_edge_p (e, false, true))
+  if (e->inline_failed
+      && (!can_inline_edge_p (e, false)
+	  || !can_inline_edge_by_limits_p (e, false, true)))
     return false;
   /* OK, speculation seems interesting.  */
   return true;
@@ -1790,6 +1829,7 @@ inline_small_functions (void)
 	      && !edge->aux
 	      && can_inline_edge_p (edge, true)
 	      && want_inline_small_function_p (edge, true)
+	      && can_inline_edge_by_limits_p (edge, true)
 	      && edge->inline_failed)
 	    {
 	      gcc_assert (!edge->aux);
@@ -1890,7 +1930,8 @@ inline_small_functions (void)
 	    badness = current_badness;
 	}
 
-      if (!can_inline_edge_p (edge, true))
+      if (!can_inline_edge_p (edge, true)
+	  || !can_inline_edge_by_limits_p (edge, true))
 	{
 	  resolve_noninline_speculation (&edge_heap, edge);
 	  continue;
@@ -2101,6 +2142,7 @@ flatten_function (struct cgraph_node *no
 	 too.  */
       if (!early
 	  ? !can_inline_edge_p (e, true)
+	    && !can_inline_edge_by_limits_p (e, true)
 	  : !can_early_inline_edge_p (e))
 	continue;
 
@@ -2155,6 +2197,7 @@ inline_to_all_callers_1 (struct cgraph_n
       struct cgraph_node *caller = node->callers->caller;
 
       if (!can_inline_edge_p (node->callers, true)
+	  || !can_inline_edge_by_limits_p (node->callers, true)
 	  || node->callers->recursive_p ())
 	{
 	  if (dump_file)
Index: testsuite/g++.dg/torture/pr81360.C
===================================================================
--- testsuite/g++.dg/torture/pr81360.C	(revision 0)
+++ testsuite/g++.dg/torture/pr81360.C	(working copy)
@@ -0,0 +1,79 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-early-inlining"  } */
+
+template <int dim> class B;
+template <int, int dim> class TriaObjectAccessor;
+template <int, typename Accessor> class A;
+template <int dim> class TriaDimensionInfo {
+public:
+  typedef A<3, TriaObjectAccessor<2, 3> > raw_quad_iterator;
+  typedef A<3, B<3> > raw_hex_iterator;
+  typedef raw_hex_iterator raw_cell_iterator;
+};
+template <int dim> class Triangulation : public TriaDimensionInfo<1> {
+  public:
+  typedef typename TriaDimensionInfo<dim>::raw_quad_iterator raw_quad_iterator;
+  TriaDimensionInfo::raw_cell_iterator end() const;
+  raw_quad_iterator end_quad() const {
+    return raw_quad_iterator(const_cast<Triangulation *>(this), 0, 0);
+  }
+};
+template <int dim> class TriaAccessor {
+public:
+  typedef void AccessorData;
+  TriaAccessor(const Triangulation<dim> * = 0);
+  Triangulation<1> *tria;
+
+  int a, b, c;
+};
+template <int dim> class TriaObjectAccessor<2, dim> : public TriaAccessor<dim> {
+public:
+  typedef typename TriaAccessor<dim>::AccessorData AccessorData;
+  TriaObjectAccessor(const Triangulation<dim> * = 0);
+};
+template <int dim> class TriaObjectAccessor<3, dim> : public TriaAccessor<dim> {
+public:
+  typedef typename TriaAccessor<dim>::AccessorData AccessorData;
+  TriaObjectAccessor(const Triangulation<dim> * = 0);
+};
+template <int dim> class B : public TriaObjectAccessor<dim, dim> {
+public:
+  typedef typename TriaObjectAccessor<dim, dim>::AccessorData AccessorData;
+  B(const Triangulation<dim> * = 0);
+};
+template <int dim, typename Accessor> class A {
+public:
+  A(const A &);
+  A(const Triangulation<dim> *, int, int);
+  Accessor accessor;
+};
+template class Triangulation<3>;
+template <int dim, typename Accessor>
+A<dim, Accessor>::A(const Triangulation<dim> *, int, int) {}
+template <int dim>
+TriaAccessor<dim>::TriaAccessor(const Triangulation<dim> *)
+    : tria(), a(-1), b(-2), c(-3) {}
+template <int dim>
+TriaObjectAccessor<2, dim>::TriaObjectAccessor(const Triangulation<dim> *) {}
+template <int dim>
+TriaObjectAccessor<3, dim>::TriaObjectAccessor(const Triangulation<dim> *) {}
+template <int dim> B<dim>::B(const Triangulation<dim> *) {}
+template <>
+TriaDimensionInfo<3>::raw_cell_iterator Triangulation<3>::end() const {
+  return raw_hex_iterator(const_cast<Triangulation *>(this), 0, 0);
+}
+
+#pragma GCC optimize ("-O0")
+int main()
+{
+  Triangulation <3> t;
+  Triangulation<3>::raw_quad_iterator i1 = t.end_quad();
+  TriaDimensionInfo<3>::raw_cell_iterator i2 = t.end();
+
+  if(i2.accessor.c != -3)
+    return 1;
+
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Equal symbols: 0" "icf"  } } */
