| Index: gcc/gcc/tree-nested.c
|
| diff --git a/gcc/gcc/tree-nested.c b/gcc/gcc/tree-nested.c
|
| index 8f9fec58cf6bcf9261e30a6038ef1f956c87815c..a3aa6c4c384ac35d3fe9b1006dcb7cae49c7bbfa 100644
|
| --- a/gcc/gcc/tree-nested.c
|
| +++ b/gcc/gcc/tree-nested.c
|
| @@ -1,5 +1,6 @@
|
| /* Nested function decomposition for GIMPLE.
|
| - Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
|
| + Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
| + Free Software Foundation, Inc.
|
|
|
| This file is part of GCC.
|
|
|
| @@ -42,21 +43,21 @@
|
| nonlocal references. We want to do this sooner rather than later, in
|
| order to give us more freedom in emitting all of the functions in question.
|
|
|
| - Back in olden times, when gcc was young, we developed an insanely
|
| + Back in olden times, when gcc was young, we developed an insanely
|
| complicated scheme whereby variables which were referenced nonlocally
|
| were forced to live in the stack of the declaring function, and then
|
| the nested functions magically discovered where these variables were
|
| placed. In order for this scheme to function properly, it required
|
| - that the outer function be partially expanded, then we switch to
|
| + that the outer function be partially expanded, then we switch to
|
| compiling the inner function, and once done with those we switch back
|
| to compiling the outer function. Such delicate ordering requirements
|
| - makes it difficult to do whole translation unit optimizations
|
| + makes it difficult to do whole translation unit optimizations
|
| involving such functions.
|
|
|
| The implementation here is much more direct. Everything that can be
|
| referenced by an inner function is a member of an explicitly created
|
| structure herein called the "nonlocal frame struct". The incoming
|
| - static chain for a nested function is a pointer to this struct in
|
| + static chain for a nested function is a pointer to this struct in
|
| the parent. In this way, we settle on known offsets from a known
|
| base, and so are decoupled from the logic that places objects in the
|
| function's stack frame. More importantly, we don't have to wait for
|
| @@ -65,8 +66,8 @@
|
| allocated anywhere. Which means that the outer function is now
|
| inlinable.
|
|
|
| - Theory of operation here is very simple. Iterate over all the
|
| - statements in all the functions (depth first) several times,
|
| + Theory of operation here is very simple. Iterate over all the
|
| + statements in all the functions (depth first) several times,
|
| allocating structures and fields on demand. In general we want to
|
| examine inner functions first, so that we can avoid making changes
|
| to outer functions which are unnecessary.
|
| @@ -82,7 +83,7 @@ struct nesting_info
|
| struct nesting_info *outer;
|
| struct nesting_info *inner;
|
| struct nesting_info *next;
|
| -
|
| +
|
| struct pointer_map_t *field_map;
|
| struct pointer_map_t *var_map;
|
| bitmap suppress_expansion;
|
| @@ -102,6 +103,27 @@ struct nesting_info
|
| };
|
|
|
|
|
| +/* Iterate over the nesting tree, starting with ROOT, depth first. */
|
| +
|
| +static inline struct nesting_info *
|
| +iter_nestinfo_start (struct nesting_info *root)
|
| +{
|
| + while (root->inner)
|
| + root = root->inner;
|
| + return root;
|
| +}
|
| +
|
| +static inline struct nesting_info *
|
| +iter_nestinfo_next (struct nesting_info *node)
|
| +{
|
| + if (node->next)
|
| + return iter_nestinfo_start (node->next);
|
| + return node->outer;
|
| +}
|
| +
|
| +#define FOR_EACH_NEST_INFO(I, ROOT) \
|
| + for ((I) = iter_nestinfo_start (ROOT); (I); (I) = iter_nestinfo_next (I))
|
| +
|
| /* Obstack used for the bitmaps in the struct above. */
|
| static struct bitmap_obstack nesting_info_bitmap_obstack;
|
|
|
| @@ -301,6 +323,7 @@ static tree
|
| get_chain_decl (struct nesting_info *info)
|
| {
|
| tree decl = info->chain_decl;
|
| +
|
| if (!decl)
|
| {
|
| tree type;
|
| @@ -312,9 +335,10 @@ get_chain_decl (struct nesting_info *info)
|
| the construction of this variable is handled specially in
|
| expand_function_start and initialize_inlined_parameters.
|
| Note also that it's represented as a parameter. This is more
|
| - close to the truth, since the initial value does come from
|
| + close to the truth, since the initial value does come from
|
| the caller. */
|
| - decl = build_decl (PARM_DECL, create_tmp_var_name ("CHAIN"), type);
|
| + decl = build_decl (DECL_SOURCE_LOCATION (info->context),
|
| + PARM_DECL, create_tmp_var_name ("CHAIN"), type);
|
| DECL_ARTIFICIAL (decl) = 1;
|
| DECL_IGNORED_P (decl) = 1;
|
| TREE_USED (decl) = 1;
|
| @@ -326,6 +350,14 @@ get_chain_decl (struct nesting_info *info)
|
| TREE_READONLY (decl) = 1;
|
|
|
| info->chain_decl = decl;
|
| +
|
| + if (dump_file
|
| + && (dump_flags & TDF_DETAILS)
|
| + && !DECL_STATIC_CHAIN (info->context))
|
| + fprintf (dump_file, "Setting static-chain for %s\n",
|
| + lang_hooks.decl_printable_name (info->context, 2));
|
| +
|
| + DECL_STATIC_CHAIN (info->context) = 1;
|
| }
|
| return decl;
|
| }
|
| @@ -338,6 +370,7 @@ static tree
|
| get_chain_field (struct nesting_info *info)
|
| {
|
| tree field = info->chain_field;
|
| +
|
| if (!field)
|
| {
|
| tree type = build_pointer_type (get_frame_type (info->outer));
|
| @@ -351,6 +384,14 @@ get_chain_field (struct nesting_info *info)
|
| insert_field_into_struct (get_frame_type (info), field);
|
|
|
| info->chain_field = field;
|
| +
|
| + if (dump_file
|
| + && (dump_flags & TDF_DETAILS)
|
| + && !DECL_STATIC_CHAIN (info->context))
|
| + fprintf (dump_file, "Setting static-chain for %s\n",
|
| + lang_hooks.decl_printable_name (info->context, 2));
|
| +
|
| + DECL_STATIC_CHAIN (info->context) = 1;
|
| }
|
| return field;
|
| }
|
| @@ -372,7 +413,7 @@ init_tmp_var_with_call (struct nesting_info *info, gimple_stmt_iterator *gsi,
|
| return t;
|
| }
|
|
|
| -
|
| +
|
| /* Copy EXP into a temporary. Allocate the temporary in the context of
|
| INFO and insert the initialization statement before GSI. */
|
|
|
| @@ -427,7 +468,7 @@ save_tmp_var (struct nesting_info *info, tree exp, gimple_stmt_iterator *gsi)
|
| static GTY(()) tree trampoline_type;
|
|
|
| static tree
|
| -get_trampoline_type (void)
|
| +get_trampoline_type (struct nesting_info *info)
|
| {
|
| unsigned align, size;
|
| tree t;
|
| @@ -448,7 +489,8 @@ get_trampoline_type (void)
|
|
|
| t = build_index_type (build_int_cst (NULL_TREE, size - 1));
|
| t = build_array_type (char_type_node, t);
|
| - t = build_decl (FIELD_DECL, get_identifier ("__data"), t);
|
| + t = build_decl (DECL_SOURCE_LOCATION (info->context),
|
| + FIELD_DECL, get_identifier ("__data"), t);
|
| DECL_ALIGN (t) = align;
|
| DECL_USER_ALIGN (t) = 1;
|
|
|
| @@ -481,7 +523,7 @@ lookup_tramp_for_decl (struct nesting_info *info, tree decl,
|
| {
|
| tree field = make_node (FIELD_DECL);
|
| DECL_NAME (field) = DECL_NAME (decl);
|
| - TREE_TYPE (field) = get_trampoline_type ();
|
| + TREE_TYPE (field) = get_trampoline_type (info);
|
| TREE_ADDRESSABLE (field) = 1;
|
|
|
| insert_field_into_struct (get_frame_type (info), field);
|
| @@ -491,7 +533,7 @@ lookup_tramp_for_decl (struct nesting_info *info, tree decl,
|
| }
|
|
|
| return (tree) *slot;
|
| -}
|
| +}
|
|
|
| /* Build or return the field within the non-local frame state that holds
|
| the non-local goto "jmp_buf". The buffer itself is maintained by the
|
| @@ -615,19 +657,14 @@ walk_gimple_omp_for (gimple for_stmt,
|
| }
|
|
|
| /* Similarly for ROOT and all functions nested underneath, depth first. */
|
| -
|
| +
|
| static void
|
| walk_all_functions (walk_stmt_fn callback_stmt, walk_tree_fn callback_op,
|
| struct nesting_info *root)
|
| {
|
| - do
|
| - {
|
| - if (root->inner)
|
| - walk_all_functions (callback_stmt, callback_op, root->inner);
|
| - walk_function (callback_stmt, callback_op, root);
|
| - root = root->next;
|
| - }
|
| - while (root);
|
| + struct nesting_info *n;
|
| + FOR_EACH_NEST_INFO (n, root)
|
| + walk_function (callback_stmt, callback_op, n);
|
| }
|
|
|
|
|
| @@ -770,10 +807,11 @@ get_frame_field (struct nesting_info *info, tree target_context,
|
| return x;
|
| }
|
|
|
| +static void note_nonlocal_vla_type (struct nesting_info *info, tree type);
|
|
|
| /* A subroutine of convert_nonlocal_reference_op. 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
|
| + variable in the parent function. This is used both for debug info
|
| and in OpenMP lowering. */
|
|
|
| static tree
|
| @@ -817,9 +855,9 @@ get_nonlocal_debug_decl (struct nesting_info *info, tree 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));
|
| + new_decl = build_decl (DECL_SOURCE_LOCATION (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);
|
| @@ -827,6 +865,11 @@ get_nonlocal_debug_decl (struct nesting_info *info, tree decl)
|
| TREE_READONLY (new_decl) = TREE_READONLY (decl);
|
| TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (decl);
|
| DECL_SEEN_IN_BIND_EXPR_P (new_decl) = 1;
|
| + if ((TREE_CODE (decl) == PARM_DECL
|
| + || TREE_CODE (decl) == RESULT_DECL
|
| + || TREE_CODE (decl) == VAR_DECL)
|
| + && DECL_BY_REFERENCE (decl))
|
| + DECL_BY_REFERENCE (new_decl) = 1;
|
|
|
| SET_DECL_VALUE_EXPR (new_decl, x);
|
| DECL_HAS_VALUE_EXPR_P (new_decl) = 1;
|
| @@ -835,6 +878,11 @@ get_nonlocal_debug_decl (struct nesting_info *info, tree decl)
|
| TREE_CHAIN (new_decl) = info->debug_var_chain;
|
| info->debug_var_chain = new_decl;
|
|
|
| + if (!optimize
|
| + && info->context != target_context
|
| + && variably_modified_type_p (TREE_TYPE (decl), NULL))
|
| + note_nonlocal_vla_type (info, TREE_TYPE (decl));
|
| +
|
| return new_decl;
|
| }
|
|
|
| @@ -1040,7 +1088,8 @@ convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
|
| {
|
| bitmap_set_bit (new_suppress, DECL_UID (decl));
|
| OMP_CLAUSE_DECL (clause) = get_nonlocal_debug_decl (info, decl);
|
| - need_chain = true;
|
| + if (OMP_CLAUSE_CODE (clause) != OMP_CLAUSE_PRIVATE)
|
| + need_chain = true;
|
| }
|
| break;
|
|
|
| @@ -1106,6 +1155,60 @@ convert_nonlocal_omp_clauses (tree *pclauses, struct walk_stmt_info *wi)
|
| return need_chain;
|
| }
|
|
|
| +/* Create nonlocal debug decls for nonlocal VLA array bounds. */
|
| +
|
| +static void
|
| +note_nonlocal_vla_type (struct nesting_info *info, tree type)
|
| +{
|
| + while (POINTER_TYPE_P (type) && !TYPE_NAME (type))
|
| + type = TREE_TYPE (type);
|
| +
|
| + if (TYPE_NAME (type)
|
| + && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
|
| + && DECL_ORIGINAL_TYPE (TYPE_NAME (type)))
|
| + type = DECL_ORIGINAL_TYPE (TYPE_NAME (type));
|
| +
|
| + while (POINTER_TYPE_P (type)
|
| + || TREE_CODE (type) == VECTOR_TYPE
|
| + || TREE_CODE (type) == FUNCTION_TYPE
|
| + || TREE_CODE (type) == METHOD_TYPE)
|
| + type = TREE_TYPE (type);
|
| +
|
| + if (TREE_CODE (type) == ARRAY_TYPE)
|
| + {
|
| + tree domain, t;
|
| +
|
| + note_nonlocal_vla_type (info, TREE_TYPE (type));
|
| + domain = TYPE_DOMAIN (type);
|
| + if (domain)
|
| + {
|
| + t = TYPE_MIN_VALUE (domain);
|
| + if (t && (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL)
|
| + && decl_function_context (t) != info->context)
|
| + get_nonlocal_debug_decl (info, t);
|
| + t = TYPE_MAX_VALUE (domain);
|
| + if (t && (TREE_CODE (t) == VAR_DECL || TREE_CODE (t) == PARM_DECL)
|
| + && decl_function_context (t) != info->context)
|
| + get_nonlocal_debug_decl (info, t);
|
| + }
|
| + }
|
| +}
|
| +
|
| +/* Create nonlocal debug decls for nonlocal VLA array bounds for VLAs
|
| + in BLOCK. */
|
| +
|
| +static void
|
| +note_nonlocal_block_vlas (struct nesting_info *info, tree block)
|
| +{
|
| + tree var;
|
| +
|
| + for (var = BLOCK_VARS (block); var; var = TREE_CHAIN (var))
|
| + if (TREE_CODE (var) == VAR_DECL
|
| + && variably_modified_type_p (TREE_TYPE (var), NULL)
|
| + && DECL_HAS_VALUE_EXPR_P (var)
|
| + && decl_function_context (var) != info->context)
|
| + note_nonlocal_vla_type (info, TREE_TYPE (var));
|
| +}
|
|
|
| /* Callback for walk_gimple_stmt. Rewrite all references to VAR and
|
| PARM_DECLs that belong to outer functions. This handles statements
|
| @@ -1144,7 +1247,8 @@ convert_nonlocal_reference_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| {
|
| tree c, decl;
|
| decl = get_chain_decl (info);
|
| - c = build_omp_clause (OMP_CLAUSE_FIRSTPRIVATE);
|
| + c = build_omp_clause (gimple_location (stmt),
|
| + OMP_CLAUSE_FIRSTPRIVATE);
|
| OMP_CLAUSE_DECL (c) = decl;
|
| OMP_CLAUSE_CHAIN (c) = gimple_omp_taskreg_clauses (stmt);
|
| gimple_omp_taskreg_set_clauses (stmt, c);
|
| @@ -1197,6 +1301,19 @@ convert_nonlocal_reference_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| info, gimple_omp_body (stmt));
|
| break;
|
|
|
| + case GIMPLE_BIND:
|
| + if (!optimize && gimple_bind_block (stmt))
|
| + note_nonlocal_block_vlas (info, gimple_bind_block (stmt));
|
| +
|
| + *handled_ops_p = false;
|
| + return NULL_TREE;
|
| +
|
| + case GIMPLE_COND:
|
| + wi->val_only = true;
|
| + wi->is_lhs = false;
|
| + *handled_ops_p = false;
|
| + return NULL_TREE;
|
| +
|
| default:
|
| /* For every other statement that we are not interested in
|
| handling here, let the walker traverse the operands. */
|
| @@ -1230,9 +1347,9 @@ get_local_debug_decl (struct nesting_info *info, tree decl, tree field)
|
| 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));
|
| + new_decl = build_decl (DECL_SOURCE_LOCATION (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);
|
| @@ -1240,6 +1357,11 @@ get_local_debug_decl (struct nesting_info *info, tree decl, tree field)
|
| TREE_READONLY (new_decl) = TREE_READONLY (decl);
|
| TREE_ADDRESSABLE (new_decl) = TREE_ADDRESSABLE (decl);
|
| DECL_SEEN_IN_BIND_EXPR_P (new_decl) = 1;
|
| + if ((TREE_CODE (decl) == PARM_DECL
|
| + || TREE_CODE (decl) == RESULT_DECL
|
| + || TREE_CODE (decl) == VAR_DECL)
|
| + && DECL_BY_REFERENCE (decl))
|
| + DECL_BY_REFERENCE (new_decl) = 1;
|
|
|
| SET_DECL_VALUE_EXPR (new_decl, x);
|
| DECL_HAS_VALUE_EXPR_P (new_decl) = 1;
|
| @@ -1324,7 +1446,7 @@ convert_local_reference_op (tree *tp, int *walk_subtrees, void *data)
|
|
|
| /* Then the frame decl is now addressable. */
|
| TREE_ADDRESSABLE (info->frame_decl) = 1;
|
| -
|
| +
|
| save_context = current_function_decl;
|
| current_function_decl = info->context;
|
| recompute_tree_invariant_for_addr_expr (t);
|
| @@ -1539,7 +1661,8 @@ convert_local_reference_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| {
|
| tree c;
|
| (void) get_frame_type (info);
|
| - c = build_omp_clause (OMP_CLAUSE_SHARED);
|
| + c = build_omp_clause (gimple_location (stmt),
|
| + OMP_CLAUSE_SHARED);
|
| OMP_CLAUSE_DECL (c) = info->frame_decl;
|
| OMP_CLAUSE_CHAIN (c) = gimple_omp_taskreg_clauses (stmt);
|
| gimple_omp_taskreg_set_clauses (stmt, c);
|
| @@ -1591,6 +1714,12 @@ convert_local_reference_stmt (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| info, gimple_omp_body (stmt));
|
| break;
|
|
|
| + case GIMPLE_COND:
|
| + wi->val_only = true;
|
| + wi->is_lhs = false;
|
| + *handled_ops_p = false;
|
| + return NULL_TREE;
|
| +
|
| default:
|
| /* For every other statement that we are not interested in
|
| handling here, let the walker traverse the operands. */
|
| @@ -1651,13 +1780,13 @@ convert_nl_goto_reference (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| slot = pointer_map_insert (i->var_map, label);
|
| if (*slot == NULL)
|
| {
|
| - new_label = create_artificial_label ();
|
| + new_label = create_artificial_label (UNKNOWN_LOCATION);
|
| DECL_NONLOCAL (new_label) = 1;
|
| *slot = new_label;
|
| }
|
| else
|
| new_label = (tree) *slot;
|
| -
|
| +
|
| /* 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->gsi);
|
| @@ -1756,7 +1885,7 @@ convert_tramp_reference_op (tree *tp, int *walk_subtrees, void *data)
|
|
|
| /* If the nested function doesn't use a static chain, then
|
| it doesn't need a trampoline. */
|
| - if (DECL_NO_STATIC_CHAIN (decl))
|
| + if (!DECL_STATIC_CHAIN (decl))
|
| break;
|
|
|
| /* If we don't want a trampoline, then don't build one. */
|
| @@ -1850,11 +1979,13 @@ convert_gimple_call (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| switch (gimple_code (stmt))
|
| {
|
| case GIMPLE_CALL:
|
| + if (gimple_call_chain (stmt))
|
| + break;
|
| decl = gimple_call_fndecl (stmt);
|
| if (!decl)
|
| break;
|
| target_context = decl_function_context (decl);
|
| - if (target_context && !DECL_NO_STATIC_CHAIN (decl))
|
| + if (target_context && DECL_STATIC_CHAIN (decl))
|
| {
|
| gimple_call_set_chain (stmt, get_static_chain (info, target_context,
|
| &wi->gsi));
|
| @@ -1883,8 +2014,9 @@ convert_gimple_call (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| break;
|
| if (c == NULL)
|
| {
|
| - c = build_omp_clause (i ? OMP_CLAUSE_FIRSTPRIVATE
|
| - : OMP_CLAUSE_SHARED);
|
| + c = build_omp_clause (gimple_location (stmt),
|
| + i ? OMP_CLAUSE_FIRSTPRIVATE
|
| + : OMP_CLAUSE_SHARED);
|
| OMP_CLAUSE_DECL (c) = decl;
|
| OMP_CLAUSE_CHAIN (c) = gimple_omp_taskreg_clauses (stmt);
|
| gimple_omp_taskreg_set_clauses (stmt, c);
|
| @@ -1916,32 +2048,205 @@ convert_gimple_call (gimple_stmt_iterator *gsi, bool *handled_ops_p,
|
| return NULL_TREE;
|
| }
|
|
|
| -
|
| -/* Walk the nesting tree starting with ROOT, depth first. Convert all
|
| - trampolines and call expressions. On the way back up, determine if
|
| - a nested function actually uses its static chain; if not, remember that. */
|
| +/* Walk the nesting tree starting with ROOT. Convert all trampolines and
|
| + call expressions. At the same time, determine if a nested function
|
| + actually uses its static chain; if not, remember that. */
|
|
|
| static void
|
| convert_all_function_calls (struct nesting_info *root)
|
| {
|
| + struct nesting_info *n;
|
| + int iter_count;
|
| + bool any_changed;
|
| +
|
| + /* First, optimistically clear static_chain for all decls that haven't
|
| + used the static chain already for variable access. */
|
| + FOR_EACH_NEST_INFO (n, root)
|
| + {
|
| + tree decl = n->context;
|
| + if (!n->outer || (!n->chain_decl && !n->chain_field))
|
| + {
|
| + DECL_STATIC_CHAIN (decl) = 0;
|
| + if (dump_file && (dump_flags & TDF_DETAILS))
|
| + fprintf (dump_file, "Guessing no static-chain for %s\n",
|
| + lang_hooks.decl_printable_name (decl, 2));
|
| + }
|
| + else
|
| + DECL_STATIC_CHAIN (decl) = 1;
|
| + }
|
| +
|
| + /* Walk the functions and perform transformations. Note that these
|
| + transformations can induce new uses of the static chain, which in turn
|
| + require re-examining all users of the decl. */
|
| + /* ??? It would make sense to try to use the call graph to speed this up,
|
| + but the call graph hasn't really been built yet. Even if it did, we
|
| + would still need to iterate in this loop since address-of references
|
| + wouldn't show up in the callgraph anyway. */
|
| + iter_count = 0;
|
| do
|
| {
|
| - if (root->inner)
|
| - convert_all_function_calls (root->inner);
|
| + any_changed = false;
|
| + iter_count++;
|
|
|
| - walk_function (convert_tramp_reference_stmt, convert_tramp_reference_op,
|
| - root);
|
| - walk_function (convert_gimple_call, NULL, root);
|
| + if (dump_file && (dump_flags & TDF_DETAILS))
|
| + fputc ('\n', dump_file);
|
|
|
| - /* If the function does not use a static chain, then remember that. */
|
| - if (root->outer && !root->chain_decl && !root->chain_field)
|
| - DECL_NO_STATIC_CHAIN (root->context) = 1;
|
| - else
|
| - gcc_assert (!DECL_NO_STATIC_CHAIN (root->context));
|
| + FOR_EACH_NEST_INFO (n, root)
|
| + {
|
| + tree decl = n->context;
|
| + bool old_static_chain = DECL_STATIC_CHAIN (decl);
|
| +
|
| + walk_function (convert_tramp_reference_stmt,
|
| + convert_tramp_reference_op, n);
|
| + walk_function (convert_gimple_call, NULL, n);
|
|
|
| - root = root->next;
|
| + /* If a call to another function created the use of a chain
|
| + within this function, we'll have to continue iteration. */
|
| + if (!old_static_chain && DECL_STATIC_CHAIN (decl))
|
| + any_changed = true;
|
| + }
|
| }
|
| - while (root);
|
| + while (any_changed);
|
| +
|
| + if (dump_file && (dump_flags & TDF_DETAILS))
|
| + fprintf (dump_file, "convert_all_function_calls iterations: %d\n\n",
|
| + iter_count);
|
| +}
|
| +
|
| +struct nesting_copy_body_data
|
| +{
|
| + copy_body_data cb;
|
| + struct nesting_info *root;
|
| +};
|
| +
|
| +/* A helper subroutine for debug_var_chain type remapping. */
|
| +
|
| +static tree
|
| +nesting_copy_decl (tree decl, copy_body_data *id)
|
| +{
|
| + struct nesting_copy_body_data *nid = (struct nesting_copy_body_data *) id;
|
| + void **slot = pointer_map_contains (nid->root->var_map, decl);
|
| +
|
| + if (slot)
|
| + return (tree) *slot;
|
| +
|
| + if (TREE_CODE (decl) == TYPE_DECL && DECL_ORIGINAL_TYPE (decl))
|
| + {
|
| + tree new_decl = copy_decl_no_change (decl, id);
|
| + DECL_ORIGINAL_TYPE (new_decl)
|
| + = remap_type (DECL_ORIGINAL_TYPE (decl), id);
|
| + return new_decl;
|
| + }
|
| +
|
| + if (TREE_CODE (decl) == VAR_DECL
|
| + || TREE_CODE (decl) == PARM_DECL
|
| + || TREE_CODE (decl) == RESULT_DECL)
|
| + return decl;
|
| +
|
| + return copy_decl_no_change (decl, id);
|
| +}
|
| +
|
| +/* A helper function for remap_vla_decls. See if *TP contains
|
| + some remapped variables. */
|
| +
|
| +static tree
|
| +contains_remapped_vars (tree *tp, int *walk_subtrees, void *data)
|
| +{
|
| + struct nesting_info *root = (struct nesting_info *) data;
|
| + tree t = *tp;
|
| + void **slot;
|
| +
|
| + if (DECL_P (t))
|
| + {
|
| + *walk_subtrees = 0;
|
| + slot = pointer_map_contains (root->var_map, t);
|
| +
|
| + if (slot)
|
| + return (tree) *slot;
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +/* Remap VLA decls in BLOCK and subblocks if remapped variables are
|
| + involved. */
|
| +
|
| +static void
|
| +remap_vla_decls (tree block, struct nesting_info *root)
|
| +{
|
| + tree var, subblock, val, type;
|
| + struct nesting_copy_body_data id;
|
| +
|
| + for (subblock = BLOCK_SUBBLOCKS (block);
|
| + subblock;
|
| + subblock = BLOCK_CHAIN (subblock))
|
| + remap_vla_decls (subblock, root);
|
| +
|
| + for (var = BLOCK_VARS (block); var; var = TREE_CHAIN (var))
|
| + {
|
| + if (TREE_CODE (var) == VAR_DECL
|
| + && variably_modified_type_p (TREE_TYPE (var), NULL)
|
| + && DECL_HAS_VALUE_EXPR_P (var))
|
| + {
|
| + type = TREE_TYPE (var);
|
| + val = DECL_VALUE_EXPR (var);
|
| + if (walk_tree (&type, contains_remapped_vars, root, NULL) != NULL
|
| + || walk_tree (&val, contains_remapped_vars, root, NULL) != NULL)
|
| + break;
|
| + }
|
| + }
|
| + if (var == NULL_TREE)
|
| + return;
|
| +
|
| + memset (&id, 0, sizeof (id));
|
| + id.cb.copy_decl = nesting_copy_decl;
|
| + id.cb.decl_map = pointer_map_create ();
|
| + id.root = root;
|
| +
|
| + for (; var; var = TREE_CHAIN (var))
|
| + if (TREE_CODE (var) == VAR_DECL
|
| + && variably_modified_type_p (TREE_TYPE (var), NULL)
|
| + && DECL_HAS_VALUE_EXPR_P (var))
|
| + {
|
| + struct nesting_info *i;
|
| + tree newt, t, context;
|
| +
|
| + t = type = TREE_TYPE (var);
|
| + val = DECL_VALUE_EXPR (var);
|
| + if (walk_tree (&type, contains_remapped_vars, root, NULL) == NULL
|
| + && walk_tree (&val, contains_remapped_vars, root, NULL) == NULL)
|
| + continue;
|
| +
|
| + context = decl_function_context (var);
|
| + for (i = root; i; i = i->outer)
|
| + if (i->context == context)
|
| + break;
|
| +
|
| + if (i == NULL)
|
| + continue;
|
| +
|
| + id.cb.src_fn = i->context;
|
| + id.cb.dst_fn = i->context;
|
| + id.cb.src_cfun = DECL_STRUCT_FUNCTION (root->context);
|
| +
|
| + TREE_TYPE (var) = newt = remap_type (type, &id.cb);
|
| + while (POINTER_TYPE_P (newt) && !TYPE_NAME (newt))
|
| + {
|
| + newt = TREE_TYPE (newt);
|
| + t = TREE_TYPE (t);
|
| + }
|
| + if (TYPE_NAME (newt)
|
| + && TREE_CODE (TYPE_NAME (newt)) == TYPE_DECL
|
| + && DECL_ORIGINAL_TYPE (TYPE_NAME (newt))
|
| + && newt != t
|
| + && TYPE_NAME (newt) == TYPE_NAME (t))
|
| + TYPE_NAME (newt) = remap_decl (TYPE_NAME (newt), &id.cb);
|
| +
|
| + walk_tree (&val, copy_tree_body_r, &id.cb, NULL);
|
| + if (val != DECL_VALUE_EXPR (var))
|
| + SET_DECL_VALUE_EXPR (var, val);
|
| + }
|
| +
|
| + pointer_map_destroy (id.cb.decl_map);
|
| }
|
|
|
| /* Do "everything else" to clean up or complete state collected by the
|
| @@ -1966,13 +2271,29 @@ finalize_nesting_tree_1 (struct nesting_info *root)
|
| /* In some cases the frame type will trigger the -Wpadded warning.
|
| This is not helpful; suppress it. */
|
| int save_warn_padded = warn_padded;
|
| + tree *adjust;
|
| +
|
| warn_padded = 0;
|
| layout_type (root->frame_type);
|
| warn_padded = save_warn_padded;
|
| layout_decl (root->frame_decl, 0);
|
| +
|
| + /* Remove root->frame_decl from root->new_local_var_chain, so
|
| + that we can declare it also in the lexical blocks, which
|
| + helps ensure virtual regs that end up appearing in its RTL
|
| + expression get substituted in instantiate_virtual_regs(). */
|
| + for (adjust = &root->new_local_var_chain;
|
| + *adjust != root->frame_decl;
|
| + adjust = &TREE_CHAIN (*adjust))
|
| + gcc_assert (TREE_CHAIN (*adjust));
|
| + *adjust = TREE_CHAIN (*adjust);
|
| +
|
| + TREE_CHAIN (root->frame_decl) = NULL_TREE;
|
| + declare_vars (root->frame_decl,
|
| + gimple_seq_first_stmt (gimple_body (context)), true);
|
| }
|
|
|
| - /* If any parameters were referenced non-locally, then we need to
|
| + /* If any parameters were referenced non-locally, then we need to
|
| insert a copy. Likewise, if any variables were referenced by
|
| pointer, we need to initialize the address. */
|
| if (root->any_parm_remapped)
|
| @@ -2029,10 +2350,8 @@ finalize_nesting_tree_1 (struct nesting_info *root)
|
| if (!field)
|
| continue;
|
|
|
| - if (DECL_NO_STATIC_CHAIN (i->context))
|
| - arg3 = null_pointer_node;
|
| - else
|
| - arg3 = build_addr (root->frame_decl, context);
|
| + gcc_assert (DECL_STATIC_CHAIN (i->context));
|
| + arg3 = build_addr (root->frame_decl, context);
|
|
|
| arg2 = build_addr (i->context, context);
|
|
|
| @@ -2076,26 +2395,88 @@ finalize_nesting_tree_1 (struct nesting_info *root)
|
| declare_vars (root->new_local_var_chain,
|
| gimple_seq_first_stmt (gimple_body (root->context)),
|
| false);
|
| +
|
| if (root->debug_var_chain)
|
| - declare_vars (root->debug_var_chain,
|
| - gimple_seq_first_stmt (gimple_body (root->context)),
|
| - true);
|
| + {
|
| + tree debug_var;
|
| + gimple scope;
|
| +
|
| + remap_vla_decls (DECL_INITIAL (root->context), root);
|
| +
|
| + for (debug_var = root->debug_var_chain; debug_var;
|
| + debug_var = TREE_CHAIN (debug_var))
|
| + if (variably_modified_type_p (TREE_TYPE (debug_var), NULL))
|
| + break;
|
| +
|
| + /* If there are any debug decls with variable length types,
|
| + remap those types using other debug_var_chain variables. */
|
| + if (debug_var)
|
| + {
|
| + struct nesting_copy_body_data id;
|
| +
|
| + memset (&id, 0, sizeof (id));
|
| + id.cb.copy_decl = nesting_copy_decl;
|
| + id.cb.decl_map = pointer_map_create ();
|
| + id.root = root;
|
| +
|
| + for (; debug_var; debug_var = TREE_CHAIN (debug_var))
|
| + if (variably_modified_type_p (TREE_TYPE (debug_var), NULL))
|
| + {
|
| + tree type = TREE_TYPE (debug_var);
|
| + tree newt, t = type;
|
| + struct nesting_info *i;
|
| +
|
| + for (i = root; i; i = i->outer)
|
| + if (variably_modified_type_p (type, i->context))
|
| + break;
|
| +
|
| + if (i == NULL)
|
| + continue;
|
| +
|
| + id.cb.src_fn = i->context;
|
| + id.cb.dst_fn = i->context;
|
| + id.cb.src_cfun = DECL_STRUCT_FUNCTION (root->context);
|
| +
|
| + TREE_TYPE (debug_var) = newt = remap_type (type, &id.cb);
|
| + while (POINTER_TYPE_P (newt) && !TYPE_NAME (newt))
|
| + {
|
| + newt = TREE_TYPE (newt);
|
| + t = TREE_TYPE (t);
|
| + }
|
| + if (TYPE_NAME (newt)
|
| + && TREE_CODE (TYPE_NAME (newt)) == TYPE_DECL
|
| + && DECL_ORIGINAL_TYPE (TYPE_NAME (newt))
|
| + && newt != t
|
| + && TYPE_NAME (newt) == TYPE_NAME (t))
|
| + TYPE_NAME (newt) = remap_decl (TYPE_NAME (newt), &id.cb);
|
| + }
|
| +
|
| + pointer_map_destroy (id.cb.decl_map);
|
| + }
|
| +
|
| + scope = gimple_seq_first_stmt (gimple_body (root->context));
|
| + if (gimple_bind_block (scope))
|
| + declare_vars (root->debug_var_chain, scope, true);
|
| + else
|
| + BLOCK_VARS (DECL_INITIAL (root->context))
|
| + = chainon (BLOCK_VARS (DECL_INITIAL (root->context)),
|
| + root->debug_var_chain);
|
| + }
|
|
|
| /* Dump the translated tree function. */
|
| - dump_function (TDI_nested, root->context);
|
| + if (dump_file)
|
| + {
|
| + fputs ("\n\n", dump_file);
|
| + dump_function_to_file (root->context, dump_file, dump_flags);
|
| + }
|
| }
|
|
|
| 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);
|
| + struct nesting_info *n;
|
| + FOR_EACH_NEST_INFO (n, root)
|
| + finalize_nesting_tree_1 (n);
|
| }
|
|
|
| /* Unnest the nodes and pass them to cgraph. */
|
| @@ -2117,14 +2498,9 @@ unnest_nesting_tree_1 (struct nesting_info *root)
|
| static void
|
| unnest_nesting_tree (struct nesting_info *root)
|
| {
|
| - do
|
| - {
|
| - if (root->inner)
|
| - unnest_nesting_tree (root->inner);
|
| - unnest_nesting_tree_1 (root);
|
| - root = root->next;
|
| - }
|
| - while (root);
|
| + struct nesting_info *n;
|
| + FOR_EACH_NEST_INFO (n, root)
|
| + unnest_nesting_tree_1 (n);
|
| }
|
|
|
| /* Free the data structures allocated during this pass. */
|
| @@ -2132,18 +2508,29 @@ unnest_nesting_tree (struct nesting_info *root)
|
| static void
|
| free_nesting_tree (struct nesting_info *root)
|
| {
|
| - struct nesting_info *next;
|
| + struct nesting_info *node, *next;
|
| +
|
| + node = iter_nestinfo_start (root);
|
| do
|
| {
|
| - if (root->inner)
|
| - free_nesting_tree (root->inner);
|
| - pointer_map_destroy (root->var_map);
|
| - pointer_map_destroy (root->field_map);
|
| - next = root->next;
|
| - free (root);
|
| - root = next;
|
| + next = iter_nestinfo_next (node);
|
| + pointer_map_destroy (node->var_map);
|
| + pointer_map_destroy (node->field_map);
|
| + free (node);
|
| + node = next;
|
| }
|
| - while (root);
|
| + while (node);
|
| +}
|
| +
|
| +/* Gimplify a function and all its nested functions. */
|
| +static void
|
| +gimplify_all_functions (struct cgraph_node *root)
|
| +{
|
| + struct cgraph_node *iter;
|
| + if (!gimple_body (root->decl))
|
| + gimplify_function_tree (root->decl);
|
| + for (iter = root->nested; iter; iter = iter->next_nested)
|
| + gimplify_all_functions (iter);
|
| }
|
|
|
| /* Main entry point for this pass. Process FNDECL and all of its nested
|
| @@ -2160,8 +2547,16 @@ lower_nested_functions (tree fndecl)
|
| if (!cgn->nested)
|
| return;
|
|
|
| + gimplify_all_functions (cgn);
|
| +
|
| + dump_file = dump_begin (TDI_nested, &dump_flags);
|
| + if (dump_file)
|
| + fprintf (dump_file, "\n;; Function %s\n\n",
|
| + lang_hooks.decl_printable_name (fndecl, 2));
|
| +
|
| bitmap_obstack_initialize (&nesting_info_bitmap_obstack);
|
| root = create_nesting_tree (cgn);
|
| +
|
| walk_all_functions (convert_nonlocal_reference_stmt,
|
| convert_nonlocal_reference_op,
|
| root);
|
| @@ -2170,11 +2565,19 @@ lower_nested_functions (tree fndecl)
|
| root);
|
| walk_all_functions (convert_nl_goto_reference, NULL, root);
|
| walk_all_functions (convert_nl_goto_receiver, NULL, root);
|
| +
|
| convert_all_function_calls (root);
|
| finalize_nesting_tree (root);
|
| unnest_nesting_tree (root);
|
| +
|
| free_nesting_tree (root);
|
| bitmap_obstack_release (&nesting_info_bitmap_obstack);
|
| +
|
| + if (dump_file)
|
| + {
|
| + dump_end (TDI_nested, dump_file);
|
| + dump_file = NULL;
|
| + }
|
| }
|
|
|
| #include "gt-tree-nested.h"
|
|
|