/* Nested function decomposition for trees.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of GCC.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING. If not, write to
- the Free Software Foundation, 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "config.h"
#include "system.h"
been written as independent functions without change. */
-struct var_map_elt
+struct var_map_elt GTY(())
{
tree old;
tree new;
};
-struct nesting_info
+struct nesting_info GTY ((chain_next ("%h.next")))
{
struct nesting_info *outer;
struct nesting_info *inner;
struct nesting_info *next;
- htab_t var_map;
+ htab_t GTY ((param_is (struct var_map_elt))) field_map;
+ htab_t GTY ((param_is (struct var_map_elt))) var_map;
+ bitmap suppress_expansion;
+
tree context;
tree new_local_var_chain;
+ tree debug_var_chain;
tree frame_type;
tree frame_decl;
tree chain_field;
bool any_parm_remapped;
bool any_tramp_created;
+ char static_chain_added;
};
static hashval_t
var_map_hash (const void *x)
{
- const struct var_map_elt *a = x;
+ const struct var_map_elt *a = (const struct var_map_elt *) x;
return htab_hash_pointer (a->old);
}
static int
var_map_eq (const void *x, const void *y)
{
- const struct var_map_elt *a = x;
- const struct var_map_elt *b = y;
+ const struct var_map_elt *a = (const struct var_map_elt *) x;
+ const struct var_map_elt *b = (const struct var_map_elt *) y;
return a->old == b->old;
}
DECL_CONTEXT (tmp_var) = info->context;
TREE_CHAIN (tmp_var) = info->new_local_var_chain;
DECL_SEEN_IN_BIND_EXPR_P (tmp_var) = 1;
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ DECL_COMPLEX_GIMPLE_REG_P (tmp_var) = 1;
+
info->new_local_var_chain = tmp_var;
return tmp_var;
}
-/* Take the address of EXP. Mark it for addressability as necessary. */
+/* Take the address of EXP to be used within function CONTEXT.
+ Mark it for addressability as necessary. */
tree
-build_addr (tree exp)
+build_addr (tree exp, tree context)
{
tree base = exp;
+ tree save_context;
+ tree retval;
- while (TREE_CODE (base) == REALPART_EXPR || TREE_CODE (base) == IMAGPART_EXPR
- || handled_component_p (base))
+ while (handled_component_p (base))
base = TREE_OPERAND (base, 0);
if (DECL_P (base))
TREE_ADDRESSABLE (base) = 1;
- return build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);
+ /* Building the ADDR_EXPR will compute a set of properties for
+ that ADDR_EXPR. Those properties are unfortunately context
+ specific. ie, they are dependent on CURRENT_FUNCTION_DECL.
+
+ Temporarily set CURRENT_FUNCTION_DECL to the desired context,
+ build the ADDR_EXPR, then restore CURRENT_FUNCTION_DECL. That
+ way the properties are for the ADDR_EXPR are computed properly. */
+ save_context = current_function_decl;
+ current_function_decl = context;
+ retval = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (exp)), exp);
+ current_function_decl = save_context;;
+ return retval;
}
/* Insert FIELD into TYPE, sorted by alignment requirements. */
-static void
+void
insert_field_into_struct (tree type, tree field)
{
tree *p;
info->frame_type = type;
info->frame_decl = create_tmp_var_for (info, type, "FRAME");
+
+ /* ??? Always make it addressable for now, since it is meant to
+ be pointed to by the static chain pointer. This pessimizes
+ when it turns out that no static chains are needed because
+ the nested functions referencing non-local variables are not
+ reachable, but the true pessimization is to create the non-
+ local frame structure in the first place. */
+ TREE_ADDRESSABLE (info->frame_decl) = 1;
}
return type;
}
tree field;
dummy.old = decl;
- slot = htab_find_slot (info->var_map, &dummy, insert);
+ slot = htab_find_slot (info->field_map, &dummy, insert);
if (!slot)
{
gcc_assert (insert != INSERT);
return NULL;
}
- elt = *slot;
+ elt = (struct var_map_elt *) *slot;
if (!elt && insert == INSERT)
{
insert_field_into_struct (get_frame_type (info), field);
- elt = xmalloc (sizeof (*elt));
+ elt = GGC_NEW (struct var_map_elt);
elt->old = decl;
elt->new = field;
*slot = elt;
tree t, stmt;
t = create_tmp_var_for (info, TREE_TYPE (exp), NULL);
- stmt = build (MODIFY_EXPR, TREE_TYPE (t), t, exp);
+ stmt = build2 (MODIFY_EXPR, TREE_TYPE (t), t, exp);
SET_EXPR_LOCUS (stmt, EXPR_LOCUS (tsi_stmt (*tsi)));
tsi_link_before (tsi, stmt, TSI_SAME_STMT);
return init_tmp_var (info, exp, tsi);
}
+/* Similarly, but copy from the temporary and insert the statement
+ after the iterator. */
+
+static tree
+save_tmp_var (struct nesting_info *info, tree exp,
+ tree_stmt_iterator *tsi)
+{
+ tree t, stmt;
+
+ t = create_tmp_var_for (info, TREE_TYPE (exp), NULL);
+ stmt = build2 (MODIFY_EXPR, TREE_TYPE (t), exp, t);
+ SET_EXPR_LOCUS (stmt, EXPR_LOCUS (tsi_stmt (*tsi)));
+ tsi_link_after (tsi, stmt, TSI_SAME_STMT);
+
+ return t;
+}
+
/* Build or return the type used to represent a nested function trampoline. */
static GTY(()) tree trampoline_type;
gcc_assert (insert != INSERT);
return NULL;
}
- elt = *slot;
+ elt = (struct var_map_elt *) *slot;
if (!elt && insert == INSERT)
{
insert_field_into_struct (get_frame_type (info), field);
- elt = xmalloc (sizeof (*elt));
+ elt = GGC_NEW (struct var_map_elt);
elt->old = decl;
elt->new = field;
*slot = elt;
return field;
}
\f
-/* Convenience routines to walk all statements of a gimple function.
-
- For each statement, we invoke CALLBACK via walk_tree. The passed
- data is a walk_stmt_info structure. Of note here is a TSI that
- points to the current statement being walked. The VAL_ONLY flag
- that indicates whether the *TP being examined may be replaced
- with something that matches is_gimple_val (if true) or something
- slightly more complicated (if false). "Something" technically
- means the common subset of is_gimple_lvalue and is_gimple_rhs,
- but we never try to form anything more complicated than that, so
- we don't bother checking. */
-
-struct walk_stmt_info
-{
- walk_tree_fn callback;
- tree_stmt_iterator tsi;
- struct nesting_info *info;
- bool val_only;
- bool changed;
-};
+/* Iterate over all sub-statements of *TP calling walk_tree with
+ WI->CALLBACK for every sub-expression in each statement found. */
-/* A subroutine of walk_function. Iterate over all sub-statements of *TP. */
-
-static void
+void
walk_stmts (struct walk_stmt_info *wi, tree *tp)
{
tree t = *tp;
+ int walk_subtrees;
+
if (!t)
return;
+ if (wi->want_locations && EXPR_HAS_LOCATION (t))
+ input_location = EXPR_LOCATION (t);
+
switch (TREE_CODE (t))
{
case STATEMENT_LIST:
walk_stmts (wi, &TREE_OPERAND (t, 0));
walk_stmts (wi, &TREE_OPERAND (t, 1));
break;
+
case BIND_EXPR:
+ if (wi->want_bind_expr)
+ {
+ walk_subtrees = 1;
+ wi->callback (tp, &walk_subtrees, wi);
+ if (!walk_subtrees)
+ break;
+ }
walk_stmts (wi, &BIND_EXPR_BODY (t));
break;
case RETURN_EXPR:
+ if (wi->want_return_expr)
+ {
+ walk_subtrees = 1;
+ wi->callback (tp, &walk_subtrees, wi);
+ if (!walk_subtrees)
+ break;
+ }
walk_stmts (wi, &TREE_OPERAND (t, 0));
break;
case MODIFY_EXPR:
- /* The immediate arguments of a MODIFY_EXPR may use COMPONENT_REF. */
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), wi->callback, wi, NULL);
- wi->val_only = false;
+ /* A formal temporary lhs may use a COMPONENT_REF rhs. */
+ wi->val_only = !is_gimple_formal_tmp_var (TREE_OPERAND (t, 0));
walk_tree (&TREE_OPERAND (t, 1), wi->callback, wi, NULL);
+
+ /* If the rhs is appropriate for a memory, we may use a
+ COMPONENT_REF on the lhs. */
+ wi->val_only = !is_gimple_mem_rhs (TREE_OPERAND (t, 1));
+ wi->is_lhs = true;
+ walk_tree (&TREE_OPERAND (t, 0), wi->callback, wi, NULL);
+
wi->val_only = true;
+ wi->is_lhs = false;
break;
default:
}
}
-/* Invoke CALLBACK on all statements of INFO->CONTEXT. */
+/* Invoke CALLBACK on all statements of *STMT_P. */
static void
-walk_function (walk_tree_fn callback, struct nesting_info *info)
+walk_body (walk_tree_fn callback, struct nesting_info *info, tree *stmt_p)
{
struct walk_stmt_info wi;
wi.info = info;
wi.val_only = true;
- walk_stmts (&wi, &DECL_SAVED_TREE (info->context));
+ walk_stmts (&wi, stmt_p);
+}
+
+/* Invoke CALLBACK on all statements of INFO->CONTEXT. */
+
+static inline void
+walk_function (walk_tree_fn callback, struct nesting_info *info)
+{
+ walk_body (callback, info, &DECL_SAVED_TREE (info->context));
}
/* Similarly for ROOT and all functions nested underneath, depth first. */
while (root);
}
\f
-/* We have to check for a fairly pathalogical case. The operands of function
+/* We have to check for a fairly pathological case. The operands of function
nested function are to be interpreted in the context of the enclosing
function. So if any are variably-sized, they will get remapped when the
enclosing function is inlined. But that remapping would also have to be
static struct nesting_info *
create_nesting_tree (struct cgraph_node *cgn)
{
- struct nesting_info *info = xcalloc (1, sizeof (*info));
- info->var_map = htab_create (7, var_map_hash, var_map_eq, free);
+ struct nesting_info *info = GGC_CNEW (struct nesting_info);
+ info->field_map = htab_create_ggc (7, var_map_hash, var_map_eq, ggc_free);
+ info->var_map = htab_create_ggc (7, var_map_hash, var_map_eq, ggc_free);
+ info->suppress_expansion = BITMAP_GGC_ALLOC ();
info->context = cgn->decl;
for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
if (info->context == target_context)
{
- x = build_addr (info->frame_decl);
+ x = build_addr (info->frame_decl, target_context);
}
else
{
tree field = get_chain_field (i);
x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
- x = build (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
+ x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
x = init_tmp_var (info, x, tsi);
}
}
tree field = get_chain_field (i);
x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
- x = build (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
+ x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
x = init_tmp_var (info, x, tsi);
}
x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
}
- x = build (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
+ x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
return x;
}
+/* A subroutine of convert_nonlocal_reference. Create a local variable
+ in the nested function with DECL_VALUE_EXPR set to reference the true
+ variable in the parent function. This is used both for debug info
+ and in OpenMP lowering. */
+
+static tree
+get_nonlocal_debug_decl (struct nesting_info *info, tree decl)
+{
+ struct var_map_elt *elt, dummy;
+ tree target_context;
+ struct nesting_info *i;
+ tree x, field, new_decl;
+ void **slot;
+
+ dummy.old = decl;
+ slot = htab_find_slot (info->var_map, &dummy, INSERT);
+ elt = *slot;
+
+ if (elt)
+ return elt->new;
+
+ target_context = decl_function_context (decl);
+
+ /* A copy of the code in get_frame_field, but without the temporaries. */
+ if (info->context == target_context)
+ {
+ /* Make sure frame_decl gets created. */
+ (void) get_frame_type (info);
+ x = info->frame_decl;
+ i = info;
+ }
+ else
+ {
+ x = get_chain_decl (info);
+ for (i = info->outer; i->context != target_context; i = i->outer)
+ {
+ field = get_chain_field (i);
+ x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
+ x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
+ }
+ x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
+ }
+
+ field = lookup_field_for_decl (i, decl, INSERT);
+ x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
+ if (use_pointer_in_frame (decl))
+ x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
+
+ /* ??? We should be remapping types as well, surely. */
+ new_decl = build_decl (VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl));
+ DECL_CONTEXT (new_decl) = info->context;
+ DECL_SOURCE_LOCATION (new_decl) = DECL_SOURCE_LOCATION (decl);
+ DECL_ARTIFICIAL (new_decl) = DECL_ARTIFICIAL (decl);
+ DECL_IGNORED_P (new_decl) = DECL_IGNORED_P (decl);
+ TREE_THIS_VOLATILE (new_decl) = TREE_THIS_VOLATILE (decl);
+ TREE_SIDE_EFFECTS (new_decl) = TREE_SIDE_EFFECTS (decl);
+ TREE_READONLY (new_decl) = TREE_READONLY (decl);
+ TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (decl);
+ DECL_SEEN_IN_BIND_EXPR_P (new_decl) = 1;
+
+ SET_DECL_VALUE_EXPR (new_decl, x);
+ DECL_HAS_VALUE_EXPR_P (new_decl) = 1;
+
+ elt = ggc_alloc (sizeof (*elt));
+ elt->old = decl;
+ elt->new = new_decl;
+ *slot = elt;
+
+ TREE_CHAIN (new_decl) = info->debug_var_chain;
+ info->debug_var_chain = new_decl;
+
+ return new_decl;
+}
+
/* Called via walk_function+walk_tree, rewrite all references to VAR
and PARM_DECLs that belong to outer functions.
be CHAIN->FOO. For two levels it'll be CHAIN->__chain->FOO. Further
indirections apply to decls for which use_pointer_in_frame is true. */
+static bool convert_nonlocal_omp_clauses (tree *, struct walk_stmt_info *);
+
static tree
convert_nonlocal_reference (tree *tp, int *walk_subtrees, void *data)
{
- struct walk_stmt_info *wi = data;
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
struct nesting_info *info = wi->info;
tree t = *tp;
+ tree save_local_var_chain;
+ bitmap save_suppress;
*walk_subtrees = 0;
switch (TREE_CODE (t))
case PARM_DECL:
if (decl_function_context (t) != info->context)
{
- tree target_context = decl_function_context (t);
- struct nesting_info *i;
tree x;
wi->changed = true;
- for (i = info->outer; i->context != target_context; i = i->outer)
- continue;
- x = lookup_field_for_decl (i, t, INSERT);
- x = get_frame_field (info, target_context, x, &wi->tsi);
- if (use_pointer_in_frame (t))
+ x = get_nonlocal_debug_decl (info, t);
+ if (!bitmap_bit_p (info->suppress_expansion, DECL_UID (t)))
{
- x = init_tmp_var (info, x, &wi->tsi);
- x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
+ tree target_context = decl_function_context (t);
+ struct nesting_info *i;
+ for (i = info->outer; i->context != target_context; i = i->outer)
+ continue;
+ x = lookup_field_for_decl (i, t, INSERT);
+ x = get_frame_field (info, target_context, x, &wi->tsi);
+ if (use_pointer_in_frame (t))
+ {
+ x = init_tmp_var (info, x, &wi->tsi);
+ x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
+ }
}
+
if (wi->val_only)
- x = init_tmp_var (info, x, &wi->tsi);
+ {
+ if (wi->is_lhs)
+ x = save_tmp_var (info, x, &wi->tsi);
+ else
+ x = init_tmp_var (info, x, &wi->tsi);
+ }
*tp = x;
}
{
*walk_subtrees = 1;
wi->val_only = true;
+ wi->is_lhs = false;
}
break;
{
bool save_val_only = wi->val_only;
- wi->changed = false;
wi->val_only = false;
+ wi->is_lhs = false;
+ wi->changed = false;
walk_tree (&TREE_OPERAND (t, 0), convert_nonlocal_reference, wi, NULL);
wi->val_only = true;
if (wi->changed)
{
+ tree save_context;
+
/* If we changed anything, then TREE_INVARIANT is be wrong,
since we're no longer directly referencing a decl. */
- recompute_tree_invarant_for_addr_expr (t);
+ save_context = current_function_decl;
+ current_function_decl = info->context;
+ recompute_tree_invariant_for_addr_expr (t);
+ current_function_decl = save_context;
/* If the callback converted the address argument in a context
where we only accept variables (and min_invariant, presumably),
anything that describes the references. Otherwise, we lose track
of whether a NOP_EXPR or VIEW_CONVERT_EXPR needs a simple value. */
wi->val_only = true;
- for (; handled_component_p (t)
- || TREE_CODE (t) == REALPART_EXPR || TREE_CODE (t) == IMAGPART_EXPR;
- tp = &TREE_OPERAND (t, 0), t = *tp)
+ wi->is_lhs = false;
+ for (; handled_component_p (t); tp = &TREE_OPERAND (t, 0), t = *tp)
{
if (TREE_CODE (t) == COMPONENT_REF)
walk_tree (&TREE_OPERAND (t, 2), convert_nonlocal_reference, wi,
walk_tree (tp, convert_nonlocal_reference, wi, NULL);
break;
+ case OMP_PARALLEL:
+ save_suppress = info->suppress_expansion;
+ if (convert_nonlocal_omp_clauses (&OMP_PARALLEL_CLAUSES (t), wi))
+ {
+ tree c, decl;
+ decl = get_chain_decl (info);
+ c = build_omp_clause (OMP_CLAUSE_FIRSTPRIVATE);
+ OMP_CLAUSE_DECL (c) = decl;
+ OMP_CLAUSE_CHAIN (c) = OMP_PARALLEL_CLAUSES (t);
+ OMP_PARALLEL_CLAUSES (t) = c;
+ }
+
+ save_local_var_chain = info->new_local_var_chain;
+ info->new_local_var_chain = NULL;
+
+ walk_body (convert_nonlocal_reference, info, &OMP_PARALLEL_BODY (t));
+
+ if (info->new_local_var_chain)
+ declare_vars (info->new_local_var_chain, OMP_PARALLEL_BODY (t), false);
+ info->new_local_var_chain = save_local_var_chain;
+ info->suppress_expansion = save_suppress;
+ break;
+
+ case OMP_FOR:
+ case OMP_SECTIONS:
+ case OMP_SINGLE:
+ save_suppress = info->suppress_expansion;
+ convert_nonlocal_omp_clauses (&OMP_CLAUSES (t), wi);
+ walk_body (convert_nonlocal_reference, info, &OMP_BODY (t));
+ info->suppress_expansion = save_suppress;
+ break;
+
+ case OMP_SECTION:
+ case OMP_MASTER:
+ case OMP_ORDERED:
+ walk_body (convert_nonlocal_reference, info, &OMP_BODY (t));
+ break;
+
default:
if (!IS_TYPE_OR_DECL_P (t))
{
*walk_subtrees = 1;
wi->val_only = true;
+ wi->is_lhs = false;
}
break;
}
return NULL_TREE;
}
+static bool
+convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
+{
+ struct nesting_info *info = wi->info;
+ bool need_chain = false;
+ tree clause, decl;
+ int dummy;
+ bitmap new_suppress;
+
+ new_suppress = BITMAP_GGC_ALLOC ();
+ bitmap_copy (new_suppress, info->suppress_expansion);
+
+ for (clause = *pclauses; clause ; clause = OMP_CLAUSE_CHAIN (clause))
+ {
+ switch (OMP_CLAUSE_CODE (clause))
+ {
+ case OMP_CLAUSE_PRIVATE:
+ case OMP_CLAUSE_FIRSTPRIVATE:
+ case OMP_CLAUSE_LASTPRIVATE:
+ case OMP_CLAUSE_REDUCTION:
+ case OMP_CLAUSE_COPYPRIVATE:
+ case OMP_CLAUSE_SHARED:
+ decl = OMP_CLAUSE_DECL (clause);
+ if (decl_function_context (decl) != info->context)
+ {
+ bitmap_set_bit (new_suppress, DECL_UID (decl));
+ OMP_CLAUSE_DECL (clause) = get_nonlocal_debug_decl (info, decl);
+ need_chain = true;
+ }
+ break;
+
+ case OMP_CLAUSE_SCHEDULE:
+ if (OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (clause) == NULL)
+ break;
+ /* FALLTHRU */
+ case OMP_CLAUSE_IF:
+ case OMP_CLAUSE_NUM_THREADS:
+ wi->val_only = true;
+ wi->is_lhs = false;
+ convert_nonlocal_reference (&OMP_CLAUSE_OPERAND (clause, 0), &dummy,
+ wi);
+ break;
+
+ case OMP_CLAUSE_NOWAIT:
+ case OMP_CLAUSE_ORDERED:
+ case OMP_CLAUSE_DEFAULT:
+ case OMP_CLAUSE_COPYIN:
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+ info->suppress_expansion = new_suppress;
+
+ return need_chain;
+}
+
+/* A subroutine of convert_local_reference. Create a local variable
+ in the parent function with DECL_VALUE_EXPR set to reference the
+ field in FRAME. This is used both for debug info and in OpenMP
+ lowering. */
+
+static tree
+get_local_debug_decl (struct nesting_info *info, tree decl, tree field)
+{
+ struct var_map_elt *elt, dummy;
+ tree x, new_decl;
+ void **slot;
+
+ dummy.old = decl;
+ slot = htab_find_slot (info->var_map, &dummy, INSERT);
+ elt = *slot;
+
+ if (elt)
+ return elt->new;
+
+ /* Make sure frame_decl gets created. */
+ (void) get_frame_type (info);
+ x = info->frame_decl;
+ x = build3 (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
+
+ new_decl = build_decl (VAR_DECL, DECL_NAME (decl), TREE_TYPE (decl));
+ DECL_CONTEXT (new_decl) = info->context;
+ DECL_SOURCE_LOCATION (new_decl) = DECL_SOURCE_LOCATION (decl);
+ DECL_ARTIFICIAL (new_decl) = DECL_ARTIFICIAL (decl);
+ DECL_IGNORED_P (new_decl) = DECL_IGNORED_P (decl);
+ TREE_THIS_VOLATILE (new_decl) = TREE_THIS_VOLATILE (decl);
+ TREE_SIDE_EFFECTS (new_decl) = TREE_SIDE_EFFECTS (decl);
+ TREE_READONLY (new_decl) = TREE_READONLY (decl);
+ TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (decl);
+ DECL_SEEN_IN_BIND_EXPR_P (new_decl) = 1;
+
+ SET_DECL_VALUE_EXPR (new_decl, x);
+ DECL_HAS_VALUE_EXPR_P (new_decl) = 1;
+
+ elt = ggc_alloc (sizeof (*elt));
+ elt->old = decl;
+ elt->new = new_decl;
+ *slot = elt;
+
+ TREE_CHAIN (new_decl) = info->debug_var_chain;
+ info->debug_var_chain = new_decl;
+
+ /* Do not emit debug info twice. */
+ DECL_IGNORED_P (decl) = 1;
+
+ return new_decl;
+}
+
/* Called via walk_function+walk_tree, rewrite all references to VAR
and PARM_DECLs that were referenced by inner nested functions.
The rewrite will be a structure reference to the local frame variable. */
+static bool convert_local_omp_clauses (tree *, struct walk_stmt_info *);
+
static tree
convert_local_reference (tree *tp, int *walk_subtrees, void *data)
{
- struct walk_stmt_info *wi = data;
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
struct nesting_info *info = wi->info;
tree t = *tp, field, x;
+ bool save_val_only;
+ tree save_local_var_chain;
+ bitmap save_suppress;
+ *walk_subtrees = 0;
switch (TREE_CODE (t))
{
case VAR_DECL:
break;
wi->changed = true;
- x = get_frame_field (info, info->context, field, &wi->tsi);
+ x = get_local_debug_decl (info, t, field);
+ if (!bitmap_bit_p (info->suppress_expansion, DECL_UID (t)))
+ x = get_frame_field (info, info->context, field, &wi->tsi);
+
if (wi->val_only)
- x = init_tmp_var (info, x, &wi->tsi);
+ {
+ if (wi->is_lhs)
+ x = save_tmp_var (info, x, &wi->tsi);
+ else
+ x = init_tmp_var (info, x, &wi->tsi);
+ }
+
*tp = x;
}
break;
case ADDR_EXPR:
- {
- bool save_val_only = wi->val_only;
+ save_val_only = wi->val_only;
+ wi->val_only = false;
+ wi->is_lhs = false;
+ wi->changed = false;
+ walk_tree (&TREE_OPERAND (t, 0), convert_local_reference, wi, NULL);
+ wi->val_only = save_val_only;
- wi->changed = false;
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), convert_local_reference, wi, NULL);
- wi->val_only = save_val_only;
+ /* If we converted anything ... */
+ if (wi->changed)
+ {
+ tree save_context;
- /* If we converted anything ... */
- if (wi->changed)
- {
- /* Then the frame decl is now addressable. */
- TREE_ADDRESSABLE (info->frame_decl) = 1;
+ /* Then the frame decl is now addressable. */
+ TREE_ADDRESSABLE (info->frame_decl) = 1;
- recompute_tree_invarant_for_addr_expr (t);
-
- /* If we are in a context where we only accept values, then
- compute the address into a temporary. */
- if (save_val_only)
- *tp = tsi_gimplify_val (wi->info, t, &wi->tsi);
- }
- }
+ save_context = current_function_decl;
+ current_function_decl = info->context;
+ recompute_tree_invariant_for_addr_expr (t);
+ current_function_decl = save_context;
+
+ /* If we are in a context where we only accept values, then
+ compute the address into a temporary. */
+ if (save_val_only)
+ *tp = tsi_gimplify_val (wi->info, t, &wi->tsi);
+ }
break;
case REALPART_EXPR:
/* Go down this entire nest and just look at the final prefix and
anything that describes the references. Otherwise, we lose track
of whether a NOP_EXPR or VIEW_CONVERT_EXPR needs a simple value. */
+ save_val_only = wi->val_only;
wi->val_only = true;
- for (; handled_component_p (t)
- || TREE_CODE (t) == REALPART_EXPR || TREE_CODE (t) == IMAGPART_EXPR;
- tp = &TREE_OPERAND (t, 0), t = *tp)
+ wi->is_lhs = false;
+ for (; handled_component_p (t); tp = &TREE_OPERAND (t, 0), t = *tp)
{
if (TREE_CODE (t) == COMPONENT_REF)
walk_tree (&TREE_OPERAND (t, 2), convert_local_reference, wi,
}
wi->val_only = false;
walk_tree (tp, convert_local_reference, wi, NULL);
+ wi->val_only = save_val_only;
+ break;
+
+ case OMP_PARALLEL:
+ save_suppress = info->suppress_expansion;
+ if (convert_local_omp_clauses (&OMP_PARALLEL_CLAUSES (t), wi))
+ {
+ tree c;
+ (void) get_frame_type (info);
+ c = build_omp_clause (OMP_CLAUSE_SHARED);
+ OMP_CLAUSE_DECL (c) = info->frame_decl;
+ OMP_CLAUSE_CHAIN (c) = OMP_PARALLEL_CLAUSES (t);
+ OMP_PARALLEL_CLAUSES (t) = c;
+ }
+
+ save_local_var_chain = info->new_local_var_chain;
+ info->new_local_var_chain = NULL;
+
+ walk_body (convert_local_reference, info, &OMP_PARALLEL_BODY (t));
+
+ if (info->new_local_var_chain)
+ declare_vars (info->new_local_var_chain, OMP_PARALLEL_BODY (t), false);
+ info->new_local_var_chain = save_local_var_chain;
+ info->suppress_expansion = save_suppress;
+ break;
+
+ case OMP_FOR:
+ case OMP_SECTIONS:
+ case OMP_SINGLE:
+ save_suppress = info->suppress_expansion;
+ convert_local_omp_clauses (&OMP_CLAUSES (t), wi);
+ walk_body (convert_local_reference, info, &OMP_BODY (t));
+ info->suppress_expansion = save_suppress;
+ break;
+
+ case OMP_SECTION:
+ case OMP_MASTER:
+ case OMP_ORDERED:
+ walk_body (convert_local_reference, info, &OMP_BODY (t));
break;
default:
{
*walk_subtrees = 1;
wi->val_only = true;
+ wi->is_lhs = false;
}
break;
}
return NULL_TREE;
}
+static bool
+convert_local_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
+{
+ struct nesting_info *info = wi->info;
+ bool need_frame = false;
+ tree clause, decl;
+ int dummy;
+ bitmap new_suppress;
+
+ new_suppress = BITMAP_GGC_ALLOC ();
+ bitmap_copy (new_suppress, info->suppress_expansion);
+
+ for (clause = *pclauses; clause ; clause = OMP_CLAUSE_CHAIN (clause))
+ {
+ switch (OMP_CLAUSE_CODE (clause))
+ {
+ case OMP_CLAUSE_PRIVATE:
+ case OMP_CLAUSE_FIRSTPRIVATE:
+ case OMP_CLAUSE_LASTPRIVATE:
+ case OMP_CLAUSE_REDUCTION:
+ case OMP_CLAUSE_COPYPRIVATE:
+ case OMP_CLAUSE_SHARED:
+ decl = OMP_CLAUSE_DECL (clause);
+ if (decl_function_context (decl) == info->context
+ && !use_pointer_in_frame (decl))
+ {
+ tree field = lookup_field_for_decl (info, decl, NO_INSERT);
+ if (field)
+ {
+ bitmap_set_bit (new_suppress, DECL_UID (decl));
+ OMP_CLAUSE_DECL (clause)
+ = get_local_debug_decl (info, decl, field);
+ need_frame = true;
+ }
+ }
+ break;
+
+ case OMP_CLAUSE_SCHEDULE:
+ if (OMP_CLAUSE_SCHEDULE_CHUNK_EXPR (clause) == NULL)
+ break;
+ /* FALLTHRU */
+ case OMP_CLAUSE_IF:
+ case OMP_CLAUSE_NUM_THREADS:
+ wi->val_only = true;
+ wi->is_lhs = false;
+ convert_local_reference (&OMP_CLAUSE_OPERAND (clause, 0), &dummy, wi);
+ break;
+
+ case OMP_CLAUSE_NOWAIT:
+ case OMP_CLAUSE_ORDERED:
+ case OMP_CLAUSE_DEFAULT:
+ case OMP_CLAUSE_COPYIN:
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+ info->suppress_expansion = new_suppress;
+
+ return need_frame;
+}
+
/* Called via walk_function+walk_tree, rewrite all GOTO_EXPRs that
reference labels from outer functions. The rewrite will be a
call to __builtin_nonlocal_goto. */
static tree
convert_nl_goto_reference (tree *tp, int *walk_subtrees, void *data)
{
- struct walk_stmt_info *wi = data;
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
struct nesting_info *info = wi->info, *i;
tree t = *tp, label, new_label, target_context, x, arg, field;
- struct var_map_elt *elt;
+ struct var_map_elt *elt, dummy;
void **slot;
*walk_subtrees = 0;
control transfer. This new label will be marked LABEL_NONLOCAL; this
mark will trigger proper behavior in the cfg, as well as cause the
(hairy target-specific) non-local goto receiver code to be generated
- when we expand rtl. */
- new_label = create_artificial_label ();
- DECL_NONLOCAL (new_label) = 1;
-
- /* Enter this association into var_map so that we can insert the new
- label into the IL during a second pass. */
- elt = xmalloc (sizeof (*elt));
- elt->old = label;
- elt->new = new_label;
- slot = htab_find_slot (i->var_map, elt, INSERT);
- *slot = elt;
+ when we expand rtl. Enter this association into var_map so that we
+ can insert the new label into the IL during a second pass. */
+ dummy.old = label;
+ slot = htab_find_slot (i->var_map, &dummy, INSERT);
+ elt = (struct var_map_elt *) *slot;
+ if (elt == NULL)
+ {
+ new_label = create_artificial_label ();
+ DECL_NONLOCAL (new_label) = 1;
+
+ elt = GGC_NEW (struct var_map_elt);
+ elt->old = label;
+ elt->new = new_label;
+ *slot = elt;
+ }
+ else
+ new_label = elt->new;
/* Build: __builtin_nl_goto(new_label, &chain->nl_goto_field). */
field = get_nl_goto_field (i);
x = get_frame_field (info, target_context, field, &wi->tsi);
- x = build_addr (x);
+ x = build_addr (x, target_context);
x = tsi_gimplify_val (info, x, &wi->tsi);
arg = tree_cons (NULL, x, NULL);
- x = build_addr (new_label);
+ x = build_addr (new_label, target_context);
arg = tree_cons (NULL, x, arg);
x = implicit_built_in_decls[BUILT_IN_NONLOCAL_GOTO];
x = build_function_call_expr (x, arg);
static tree
convert_nl_goto_receiver (tree *tp, int *walk_subtrees, void *data)
{
- struct walk_stmt_info *wi = data;
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
struct nesting_info *info = wi->info;
tree t = *tp, label, new_label, x;
struct var_map_elt *elt, dummy;
label = LABEL_EXPR_LABEL (t);
dummy.old = label;
- elt = htab_find (info->var_map, &dummy);
+ elt = (struct var_map_elt *) htab_find (info->var_map, &dummy);
if (!elt)
return NULL_TREE;
new_label = elt->new;
static tree
convert_tramp_reference (tree *tp, int *walk_subtrees, void *data)
{
- struct walk_stmt_info *wi = data;
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
struct nesting_info *info = wi->info, *i;
tree t = *tp, decl, target_context, x, arg;
/* Compute the address of the field holding the trampoline. */
x = get_frame_field (info, target_context, x, &wi->tsi);
- x = build_addr (x);
+ x = build_addr (x, target_context);
x = tsi_gimplify_val (info, x, &wi->tsi);
arg = tree_cons (NULL, x, NULL);
static tree
convert_call_expr (tree *tp, int *walk_subtrees, void *data)
{
- struct walk_stmt_info *wi = data;
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
struct nesting_info *info = wi->info;
tree t = *tp, decl, target_context;
+ char save_static_chain_added;
+ int i;
*walk_subtrees = 0;
switch (TREE_CODE (t))
break;
target_context = decl_function_context (decl);
if (target_context && !DECL_NO_STATIC_CHAIN (decl))
- TREE_OPERAND (t, 2)
- = get_static_chain (info, target_context, &wi->tsi);
+ {
+ TREE_OPERAND (t, 2)
+ = get_static_chain (info, target_context, &wi->tsi);
+ info->static_chain_added
+ |= (1 << (info->context != target_context));
+ }
break;
case RETURN_EXPR:
*walk_subtrees = 1;
break;
+ case OMP_PARALLEL:
+ save_static_chain_added = info->static_chain_added;
+ info->static_chain_added = 0;
+ walk_body (convert_call_expr, info, &OMP_PARALLEL_BODY (t));
+ for (i = 0; i < 2; i++)
+ {
+ tree c, decl;
+ if ((info->static_chain_added & (1 << i)) == 0)
+ continue;
+ decl = i ? get_chain_decl (info) : info->frame_decl;
+ /* Don't add CHAIN.* or FRAME.* twice. */
+ for (c = OMP_PARALLEL_CLAUSES (t); c; c = OMP_CLAUSE_CHAIN (c))
+ if ((OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FIRSTPRIVATE
+ || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_SHARED)
+ && OMP_CLAUSE_DECL (c) == decl)
+ break;
+ if (c == NULL)
+ {
+ c = build_omp_clause (OMP_CLAUSE_FIRSTPRIVATE);
+ OMP_CLAUSE_DECL (c) = decl;
+ OMP_CLAUSE_CHAIN (c) = OMP_PARALLEL_CLAUSES (t);
+ OMP_PARALLEL_CLAUSES (t) = c;
+ }
+ }
+ info->static_chain_added |= save_static_chain_added;
+ break;
+
+ case OMP_FOR:
+ case OMP_SECTIONS:
+ case OMP_SECTION:
+ case OMP_SINGLE:
+ case OMP_MASTER:
+ case OMP_ORDERED:
+ case OMP_CRITICAL:
+ walk_body (convert_call_expr, info, &OMP_BODY (t));
+ break;
+
default:
break;
}
tree stmt_list = NULL;
tree context = root->context;
struct function *sf;
- struct cgraph_node *node;
/* If we created a non-local frame type or decl, we need to lay them
out at this time. */
if (root->frame_type)
{
+ /* In some cases the frame type will trigger the -Wpadded warning.
+ This is not helpful; suppress it. */
+ int save_warn_padded = warn_padded;
+ warn_padded = 0;
layout_type (root->frame_type);
+ warn_padded = save_warn_padded;
layout_decl (root->frame_decl, 0);
}
continue;
if (use_pointer_in_frame (p))
- x = build_addr (p);
+ x = build_addr (p, context);
else
x = p;
- y = build (COMPONENT_REF, TREE_TYPE (field),
- root->frame_decl, field, NULL_TREE);
- x = build (MODIFY_EXPR, TREE_TYPE (field), y, x);
+ y = build3 (COMPONENT_REF, TREE_TYPE (field),
+ root->frame_decl, field, NULL_TREE);
+ x = build2 (MODIFY_EXPR, TREE_TYPE (field), y, x);
append_to_statement_list (x, &stmt_list);
}
}
from chain_decl. */
if (root->chain_field)
{
- tree x = build (COMPONENT_REF, TREE_TYPE (root->chain_field),
- root->frame_decl, root->chain_field, NULL_TREE);
- x = build (MODIFY_EXPR, TREE_TYPE (x), x, get_chain_decl (root));
+ tree x = build3 (COMPONENT_REF, TREE_TYPE (root->chain_field),
+ root->frame_decl, root->chain_field, NULL_TREE);
+ x = build2 (MODIFY_EXPR, TREE_TYPE (x), x, get_chain_decl (root));
append_to_statement_list (x, &stmt_list);
}
if (DECL_NO_STATIC_CHAIN (i->context))
x = null_pointer_node;
else
- x = build_addr (root->frame_decl);
+ x = build_addr (root->frame_decl, context);
arg = tree_cons (NULL, x, NULL);
- x = build_addr (i->context);
+ x = build_addr (i->context, context);
arg = tree_cons (NULL, x, arg);
- x = build (COMPONENT_REF, TREE_TYPE (field),
- root->frame_decl, field, NULL_TREE);
- x = build_addr (x);
+ x = build3 (COMPONENT_REF, TREE_TYPE (field),
+ root->frame_decl, field, NULL_TREE);
+ x = build_addr (x, context);
arg = tree_cons (NULL, x, arg);
x = implicit_built_in_decls[BUILT_IN_INIT_TRAMPOLINE];
/* Make sure all new local variables get inserted into the
proper BIND_EXPR. */
if (root->new_local_var_chain)
- declare_tmp_vars (root->new_local_var_chain,
- DECL_SAVED_TREE (root->context));
+ declare_vars (root->new_local_var_chain, DECL_SAVED_TREE (root->context),
+ false);
+ if (root->debug_var_chain)
+ declare_vars (root->debug_var_chain, DECL_SAVED_TREE (root->context),
+ true);
/* Dump the translated tree function. */
dump_function (TDI_nested, root->context);
- node = cgraph_node (root->context);
+}
+
+static void
+finalize_nesting_tree (struct nesting_info *root)
+{
+ do
+ {
+ if (root->inner)
+ finalize_nesting_tree (root->inner);
+ finalize_nesting_tree_1 (root);
+ root = root->next;
+ }
+ while (root);
+}
+
+/* Unnest the nodes and pass them to cgraph. */
+
+static void
+unnest_nesting_tree_1 (struct nesting_info *root)
+{
+ struct cgraph_node *node = cgraph_node (root->context);
/* For nested functions update the cgraph to reflect unnesting.
We also delay finalizing of these functions up to this point. */
}
static void
-finalize_nesting_tree (struct nesting_info *root)
+unnest_nesting_tree (struct nesting_info *root)
{
do
{
if (root->inner)
- finalize_nesting_tree (root->inner);
- finalize_nesting_tree_1 (root);
+ unnest_nesting_tree (root->inner);
+ unnest_nesting_tree_1 (root);
root = root->next;
}
while (root);
free_nesting_tree (root->inner);
htab_delete (root->var_map);
next = root->next;
- free (root);
+ ggc_free (root);
root = next;
}
while (root);
}
+static GTY(()) struct nesting_info *root;
+
/* Main entry point for this pass. Process FNDECL and all of its nested
subroutines and turn them into something less tightly bound. */
void
lower_nested_functions (tree fndecl)
{
- struct nesting_info *root;
struct cgraph_node *cgn;
/* If there are no nested functions, there's nothing to do. */
walk_all_functions (convert_nl_goto_receiver, root);
convert_all_function_calls (root);
finalize_nesting_tree (root);
+ unnest_nesting_tree (root);
free_nesting_tree (root);
+ root = NULL;
}
#include "gt-tree-nested.h"