This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

pretty-ipa merge 12: return value substitution


Hi,
this is mainline verison of patch
http://archives.free.net.ph/message/20090325.111943.2bcb4f52.el.html

It undoes temporary created by gimplifier:
/* If aggregate_value_p is true, then we can return the bare RESULT_DECL.
   Recall that aggregate_value_p is FALSE for any aggregate type that is
   returned in registers. If we're returning values in registers, then
   we don't want to extend the lifetime of the RESULT_DECL, particularly
   across another call. In addition, for those aggregates for which
   hard_function_value generates a PARALLEL, we'll die during normal
   expansion of structure assignments; there's special code in
   expand_return
   to handle this case that does not exist in expand_expr. */
if (!result_decl
    || aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))
 result = result_decl; 

On pretty ipa in the following testcase:

struct a
t (struct a a)
{
  return a;
}

struct a
q (struct a a)
{
  return t (a);
}

We produce no temporaries and do retval=a statement in q().  On mainline
we need 2 temporaries, this patch kills first one.  I will send the
second half of subtitution patch along with testcase once I solve most
effective way to figure out that argument is read only in function.

This patch bootstrapped/regtested x86_64-linux.  On tramp3d it save
about 500 temporaries, on DLV about 7000. Most of these are later
eliminated by SRA and copyprop, but it is not always the case (such as
in the testcase above where we won't propagate this at all) and it is
cheaper to handle these this way and easier to keep debug info intact.
Also with escape and capture info we can make overlap better even for
addressable functions, so I think it makes sense to handle these special
cases in inliner rather than relying purely on post-inlining pass (and
in particular nonexistent memory copypropagation).

Honza

	* tree-inline.c (lookup_return_stmt): New function.
	(declare_return_variable): Dump reason why substitution failed;
	try to substitute return source operand.
Index: tree-inline.c
===================================================================
--- tree-inline.c	(revision 145291)
+++ tree-inline.c	(working copy)
@@ -2210,6 +2210,25 @@ initialize_inlined_parameters (copy_body
   declare_inline_vars (id->block, vars);
 }
 
+/* Look for return statement in function FUN and return it.
+   Return NULL if not found.  */
+
+static gimple
+lookup_return_stmt (struct function *fun)
+{
+  basic_block bb = EXIT_BLOCK_PTR_FOR_FUNCTION (fun);
+  edge e;
+  edge_iterator ei;
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+  {
+    gimple_stmt_iterator bsi = gsi_last_bb (e->src);
+    gimple stmt = gsi_stmt (bsi);
+    if (gimple_code (stmt) == GIMPLE_RETURN)
+      return stmt;
+  }
+  return NULL;
+}
 
 /* Declare a return variable to replace the RESULT_DECL for the
    function we are calling.  An appropriate DECL_STMT is returned.
@@ -2307,21 +2326,40 @@ declare_return_variable (copy_body_data 
     {
       bool use_it = false;
 
+      if (dump_file)
+	{
+          fprintf (dump_file, "Trying to substitute return value to: ");
+	  print_generic_expr (dump_file, modify_dest, 0);
+          fprintf (dump_file, "\n");
+	}
+
       /* We can't use MODIFY_DEST if there's type promotion involved.  */
       if (!useless_type_conversion_p (callee_type, caller_type))
-	use_it = false;
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Type mismatch.\n");
+	  use_it = false;
+	}
 
       /* ??? If we're assigning to a variable sized type, then we must
 	 reuse the destination variable, because we've no good way to
 	 create variable sized temporaries at this point.  */
       else if (TREE_CODE (TYPE_SIZE_UNIT (caller_type)) != INTEGER_CST)
-	use_it = true;
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "VLA, forcing substitution.\n");
+	  use_it = true;
+	}
 
       /* If the callee cannot possibly modify MODIFY_DEST, then we can
 	 reuse it as the result of the call directly.  Don't do this if
 	 it would promote MODIFY_DEST to addressable.  */
       else if (TREE_ADDRESSABLE (result))
-	use_it = false;
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "Result is addressable, not substituting.\n");
+	  use_it = false;
+	}
       else
 	{
 	  tree base_m = get_base_address (modify_dest);
@@ -2329,22 +2367,70 @@ declare_return_variable (copy_body_data 
 	  /* If the base isn't a decl, then it's a pointer, and we don't
 	     know where that's going to go.  */
 	  if (!DECL_P (base_m))
-	    use_it = false;
+	    {
+	      if (dump_file)
+	         fprintf (dump_file, "Not variable.\n");
+	      use_it = false;
+	    }
 	  else if (is_global_var (base_m))
-	    use_it = false;
+	    {
+	      if (dump_file)
+	         fprintf (dump_file, "Global variable.\n");
+	      use_it = false;
+	    }
 	  else if ((TREE_CODE (TREE_TYPE (result)) == COMPLEX_TYPE
 		    || TREE_CODE (TREE_TYPE (result)) == VECTOR_TYPE)
 		   && !DECL_GIMPLE_REG_P (result)
 		   && DECL_GIMPLE_REG_P (base_m))
-	    use_it = false;
-	  else if (!TREE_ADDRESSABLE (base_m))
-	    use_it = true;
+	    {
+	      if (dump_file)
+	         fprintf (dump_file, "Complex or vector not subtituted.\n");
+	      use_it = false;
+	    }
+	  else if (!TREE_ADDRESSABLE (base_m)
+		   || (flags_from_decl_or_type (id->src_fn)
+		       & (ECF_PURE | ECF_CONST | ECF_LOOPING_CONST_OR_PURE)))
+	    {
+	      if (dump_file)
+	         fprintf (dump_file, "Destination is validated.\n");
+	      use_it = true;
+	    }
+	  else if (dump_file)
+	    fprintf (dump_file, "Destination is addressable.\n");
 	}
 
       if (use_it)
 	{
+	  /* For values return in registers gimplifier always create temporary
+	     variable to store value to avoid extending lifetimes of the
+	     registers.  Inlining such function would lead to unnecesary
+	     copy that is, in case of non-gimple-temporaries, difficult to
+	     optimize later.  */
+	  gimple return_stmt = lookup_return_stmt (id->src_cfun);
+	  if (dump_file)
+	     fprintf (dump_file, "Substituted.\n");
+	  if (return_stmt)
+	    {
+	      tree retval2 = gimple_return_retval (return_stmt);
+	      tree base = retval2 ? get_base_address (retval2) : NULL;
+
+	      if (retval2
+		  && ((TREE_CODE (base) == VAR_DECL
+		       && !!is_global_var (base))
+		      || TREE_CODE (base) == PARM_DECL
+		      || TREE_CODE (base) == RESULT_DECL)
+		  && !TREE_ADDRESSABLE (base))
+		 {
+		   if (dump_file)
+		     fprintf (dump_file, "Operand of return statement substituted too.\n");
+  	           insert_decl_map (id, retval2, modify_dest);
+		 }
+	      else if (dump_file)
+		 fprintf (dump_file, "Operand of return statement not substituted.\n");
+	    }
 	  var = modify_dest;
 	  use = NULL;
+	  
 	  goto done;
 	}
     }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]