This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
pretty-ipa merge 12: return value substitution
- From: Jan Hubicka <hubicka at ucw dot cz>
- To: gcc-patches at gcc dot gnu dot org
- Date: Tue, 31 Mar 2009 00:21:06 +0200
- Subject: 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;
}
}