/* Nested function decomposition for GIMPLE.
- Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
This file is part of GCC.
};
+/* 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;
get_chain_decl (struct nesting_info *info)
{
tree decl = info->chain_decl;
+
if (!decl)
{
tree type;
Note also that it's represented as a parameter. This is more
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;
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;
}
get_chain_field (struct nesting_info *info)
{
tree field = info->chain_field;
+
if (!field)
{
tree type = build_pointer_type (get_frame_type (info->outer));
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;
}
static GTY(()) tree trampoline_type;
static tree
-get_trampoline_type (void)
+get_trampoline_type (struct nesting_info *info)
{
unsigned align, size;
tree t;
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;
{
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);
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);
}
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
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);
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;
}
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
{
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);
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. */
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);
{
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);
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. */
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;
}
/* 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. */
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));
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);
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);
- root = root->next;
+ walk_function (convert_tramp_reference_stmt,
+ convert_tramp_reference_op, n);
+ walk_function (convert_gimple_call, NULL, n);
+
+ /* 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
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
various walking passes -- lay out the types and decls, generate code
to initialize the frame decl, store critical expressions in the
/* 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 (!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);
if (root->debug_var_chain)
{
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))
pointer_map_destroy (id.cb.decl_map);
}
- declare_vars (root->debug_var_chain,
- gimple_seq_first_stmt (gimple_body (root->context)),
- true);
+ 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. */
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. */
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
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);
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"