This is a regression present on the mainline. The compiler crashes during the
function unnesting pass because of an out-of-context temporary, but the root
cause of the problem is incorrect sharing of a tree node. The problem has
probably been latent since gimplification was devised: while the DECL_SIZE and
DECL_SIZE_UNIT trees of VAR_DECLs are visited by walk_tree (via BIND_EXPR),
this isn't the case for RESULT_DECL. As a result, if it has variable size
(this now happens much more often in Ada), its subtrees aren't unshared and
this is problematic.
The proposed fix is to unshared them manually in unshare_body. To implement
this, the awkward interface to gimplify_body/unshare_body, where you pass both
a pointer to the body and the decl itself and later need to test whether they
are related, is simplified as the 2 calls of gimplify_body are idiomatic.
Tested on i586-suse-linux, OK for the mainline?
2012-01-10 Eric Botcazou ebotca...@adacore.com
* gimple.h (gimplify_body): Remove first argument.
* gimplify.c (copy_if_shared): Add DATA argument. Do not create the
pointer set here, instead just pass DATA to walk_tree.
(unshare_body): Remove BODY_P argument and adjust. Create the pointer
set here and invoke copy_if_shared on the size trees of DECL_RESULT.
(unvisit_body): Likewise, but with unmark_visited.
(gimplify_body): Remove BODY_P argument and adjust.
(gimplify_function_tree): Adjust call to gimplify_body.
* omp-low.c (finalize_task_copyfn): Likewise.
2012-01-10 Eric Botcazou ebotca...@adacore.com
* gnat.dg/array19.ad[sb]: New test.
--
Eric Botcazou
Index: gimple.h
===
--- gimple.h (revision 182780)
+++ gimple.h (working copy)
@@ -1099,7 +1099,7 @@ extern enum gimplify_status gimplify_exp
extern void gimplify_type_sizes (tree, gimple_seq *);
extern void gimplify_one_sizepos (tree *, gimple_seq *);
extern bool gimplify_stmt (tree *, gimple_seq *);
-extern gimple gimplify_body (tree *, tree, bool);
+extern gimple gimplify_body (tree, bool);
extern void push_gimplify_context (struct gimplify_ctx *);
extern void pop_gimplify_context (gimple);
extern void gimplify_and_add (tree, gimple_seq *);
Index: gimplify.c
===
--- gimplify.c (revision 182780)
+++ gimplify.c (working copy)
@@ -951,31 +951,33 @@ copy_if_shared_r (tree *tp, int *walk_su
/* Unshare most of the shared trees rooted at *TP. */
static inline void
-copy_if_shared (tree *tp)
+copy_if_shared (tree *tp, void *data)
{
- /* If the language requires deep unsharing, we need a pointer set to make
- sure we don't repeatedly unshare subtrees of unshareable nodes. */
- struct pointer_set_t *visited
-= lang_hooks.deep_unsharing ? pointer_set_create () : NULL;
- walk_tree (tp, copy_if_shared_r, visited, NULL);
- if (visited)
-pointer_set_destroy (visited);
+ walk_tree (tp, copy_if_shared_r, data, NULL);
}
-/* Unshare all the trees in BODY_P, a pointer into the body of FNDECL, and the
- bodies of any nested functions if we are unsharing the entire body of
- FNDECL. */
+/* Unshare all the trees in the body of FNDECL, as well as in the bodies of
+ any nested functions. */
static void
-unshare_body (tree *body_p, tree fndecl)
+unshare_body (tree fndecl)
{
struct cgraph_node *cgn = cgraph_get_node (fndecl);
+ /* If the language requires deep unsharing, we need a pointer set to make
+ sure we don't repeatedly unshare subtrees of unshareable nodes. */
+ struct pointer_set_t *visited
+= lang_hooks.deep_unsharing ? pointer_set_create () : NULL;
- copy_if_shared (body_p);
+ copy_if_shared (DECL_SAVED_TREE (fndecl), visited);
+ copy_if_shared (DECL_SIZE (DECL_RESULT (fndecl)), visited);
+ copy_if_shared (DECL_SIZE_UNIT (DECL_RESULT (fndecl)), visited);
+
+ if (visited)
+pointer_set_destroy (visited);
- if (cgn body_p == DECL_SAVED_TREE (fndecl))
+ if (cgn)
for (cgn = cgn-nested; cgn; cgn = cgn-next_nested)
- unshare_body (DECL_SAVED_TREE (cgn-decl), cgn-decl);
+ unshare_body (cgn-decl);
}
/* Callback for walk_tree to unmark the visited trees rooted at *TP.
@@ -1008,15 +1010,17 @@ unmark_visited (tree *tp)
/* Likewise, but mark all trees as not visited. */
static void
-unvisit_body (tree *body_p, tree fndecl)
+unvisit_body (tree fndecl)
{
struct cgraph_node *cgn = cgraph_get_node (fndecl);
- unmark_visited (body_p);
+ unmark_visited (DECL_SAVED_TREE (fndecl));
+ unmark_visited (DECL_SIZE (DECL_RESULT (fndecl)));
+ unmark_visited (DECL_SIZE_UNIT (DECL_RESULT (fndecl)));
- if (cgn body_p == DECL_SAVED_TREE (fndecl))
+ if (cgn)
for (cgn = cgn-nested; cgn; cgn = cgn-next_nested)
- unvisit_body (DECL_SAVED_TREE (cgn-decl), cgn-decl);
+ unvisit_body (cgn-decl);
}
/* Unconditionally make an unshared copy of EXPR.