I'm investigating the reason why the amount of stack used by some pieces
of code increased significantly since gcc 3.3. Notably,
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=25505 has a testcase which
went up from 512 bytes to 20K between gcc 3.3 and 4.x, then back down to
~5K with later optimizations.
I've tracked down the remainder of the regression to variables recorded
during gimple lowering - these variables have a scope larger than they
really need to (the entire function), so don't share stack space with
each other or anything else. The two testcases attached (st1.c and
st2.c) should ideally use the same amount of stack but currently don't.
I've been playing with variations on the attached patch (which is
against gcc 4.1 but I think the mainline version will be very similar).
This does cause the compiler to generate the same assembly for st1.c as
for st2.c. However, it ICEs on this simple testcase:
int foo (void)
{
if (0)
{
return 0;
}
return 1;
}
This seems to be because in gimple-low.c, the BLOCK_SUBBLOCKS of the
DECL_INITIAL block is cleared and never reconstructed, due to new_block
and old_block being the same in lower_bind_expr. This leads to the
VAR_DECLs of the temporary variables not being seen during expand_used_vars.
I'm not quite sure what it is about the trees I'm generating that
gimple-low.c doesn't like.
Is there a way to make this patch work, or is this approach doomed for
some reason that I haven't understood?
Thanks,
Andrew
typedef struct
{
int a,b,c,d,e,f,g;
} L;
extern L bar();
extern L baz(L z);
void foo(int x)
{
baz(bar());
baz(bar());
}
typedef struct
{
int a,b,c,d,e,f,g;
} L;
extern L bar();
extern L baz(L z);
void foo(int x)
{
{
L t1 = bar();
baz(t1);
}
{
L t2 = bar();
baz(t2);
}
}
Index: gimplify.c
===================================================================
--- gimplify.c (revision 371010)
+++ gimplify.c (working copy)
@@ -61,6 +61,13 @@ static struct gimplify_ctx
bool into_ssa;
} *gimplify_ctxp;
+/* True if temporary variables should go in a BIND_EXPR associated with the
+ current statement. */
+bool use_statement_temps = false;
+
+/* If we are associating temporary variables with the statement, this will
+ hold them until the statement is finished. */
+tree statement_temps;
/* Formal (expression) temporary table handling: Multiple occurrences of
the same scalar expression are evaluated into the same temporary. */
@@ -596,7 +603,12 @@ gimple_add_tmp_var (tree tmp)
DECL_CONTEXT (tmp) = current_function_decl;
DECL_SEEN_IN_BIND_EXPR_P (tmp) = 1;
- if (gimplify_ctxp)
+ if (use_statement_temps)
+ {
+ TREE_CHAIN (tmp) = statement_temps;
+ statement_temps = tmp;
+ }
+ else if (gimplify_ctxp)
{
TREE_CHAIN (tmp) = gimplify_ctxp->temps;
gimplify_ctxp->temps = tmp;
@@ -4046,13 +4058,80 @@ gimplify_target_expr (tree *expr_p, tree
/* Gimplification of expression trees. */
+/* Given a gimplified statment *STMT_P, convert it to a STATEMENT_LIST
+ containing that statememtn. */
+
+void
+gimplify_convert_stmt_to_stmt_list (tree *stmt_p)
+{
+ if (!*stmt_p)
+ *stmt_p = alloc_stmt_list ();
+ else if (TREE_CODE (*stmt_p) != STATEMENT_LIST)
+ {
+ tree t = *stmt_p;
+ *stmt_p = alloc_stmt_list ();
+ append_to_statement_list (t, stmt_p);
+ }
+}
+
+static void
+add_block_to_enclosing (tree block)
+{
+ tree enclosing;
+
+ for (enclosing = gimple_current_bind_expr ();
+ enclosing; enclosing = TREE_CHAIN (enclosing))
+ if (BIND_EXPR_BLOCK (enclosing))
+ break;
+
+ enclosing = BIND_EXPR_BLOCK (enclosing);
+ BLOCK_SUBBLOCKS (enclosing) = chainon (BLOCK_SUBBLOCKS (enclosing), block);
+}
+
/* Gimplify an expression which appears at statement context; usually, this
means replacing it with a suitably gimple STATEMENT_LIST. */
void
gimplify_stmt (tree *stmt_p)
{
+ /* Save statement temporary information because we might be compiling a
+ statement inside another statement (i.e. in a statement expression). */
+ tree saved_statement_temps = statement_temps;
+ bool saved_use_statement_temps = use_statement_temps;
+ tree statement_block;
+ tree t;
+ tree block;
+
+ /* Clear STATEMENT_TEMPS so we know if any temporaries were created for this
+ statement. */
+ statement_temps = NULL_TREE;
+
+ /* Make sure that we are associating the temporaries with the statement. */
+ use_statement_temps = true;
+
+ /* Do the actual gimplification, which modifies STATEMENT_TEMPS as
+ necessary. */
gimplify_expr (stmt_p, NULL, NULL, is_gimple_stmt, fb_none);
+
+ /* If we have any STATEMENT_TEMPS, turn our statement into a BIND_EXPR
+ holding them and the statement that was gimplified. */
+ if (statement_temps != NULL_TREE)
+ {
+ for (t = statement_temps; t ; t = TREE_CHAIN (t))
+ DECL_GIMPLE_FORMAL_TEMP_P (t) = 0;
+ gimplify_convert_stmt_to_stmt_list (stmt_p);
+ block = make_node (BLOCK);
+ BLOCK_VARS (block) = statement_temps;
+ statement_block = build (BIND_EXPR, void_type_node, NULL_TREE, *stmt_p,
+ block);
+ add_block_to_enclosing (block);
+ declare_tmp_vars (statement_temps, statement_block);
+ *stmt_p = statement_block;
+ }
+
+ /* Restore the saved statement temporary information. */
+ statement_temps = saved_statement_temps;
+ use_statement_temps = saved_use_statement_temps;
}
/* Similarly, but force the result to be a STATEMENT_LIST. */
@@ -4061,14 +4140,7 @@ void
gimplify_to_stmt_list (tree *stmt_p)
{
gimplify_stmt (stmt_p);
- if (!*stmt_p)
- *stmt_p = alloc_stmt_list ();
- else if (TREE_CODE (*stmt_p) != STATEMENT_LIST)
- {
- tree t = *stmt_p;
- *stmt_p = alloc_stmt_list ();
- append_to_statement_list (t, stmt_p);
- }
+ gimplify_convert_stmt_to_stmt_list (stmt_p);
}