summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog22
-rw-r--r--gcc/tree-cfg.c19
-rw-r--r--gcc/tree-nested.c212
3 files changed, 198 insertions, 55 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index eaff6d9e752..8d6ae7df4d1 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,25 @@
+2009-09-16 Richard Henderson <rth@redhat.com>
+
+ PR target/41246
+ * tree-cfg.c (verify_gimple_call): Validate that gimple_call_chain
+ is set only if DECL_NO_STATIC_CHAIN is unset.
+ * tree-nested.c (iter_nestinfo_start, iter_nestinfo_next): New.
+ (FOR_EACH_NEST_INFO): New.
+ (walk_all_functions): Use it.
+ (finalize_nesting_tree): Likewise.
+ (unnest_nesting_tree): Likewise.
+ (free_nesting_tree): Use iter_nestinfo_start, iter_nestinfo_next.
+ (get_chain_decl, get_chain_field): Reset DECL_NO_STATIC_CHAIN.
+ (convert_gimple_call): Early out if gimple_call_chain already set.
+ (convert_all_function_calls): Iterate until no new functions
+ require a static chain.
+ (finalize_nesting_tree_1): Assert DECL_NO_STATIC_CHAIN is unset
+ when building a trampoline. Use dump_function_to_file instead
+ of dump_function.
+ (lower_nested_functions): Open dump_file. Validate that decls
+ that have DECL_NO_STATIC_CHAIN from the front end don't have that
+ bit reset by this pass.
+
2009-09-16 Michael Matz <matz@suse.de>
PR fortran/41212
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index f596c75fe27..ef5f32284d4 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -3573,6 +3573,25 @@ verify_gimple_call (gimple stmt)
return true;
}
+ /* If there is a static chain argument, this should not be an indirect
+ call, and the decl should not have DECL_NO_STATIC_CHAIN set. */
+ if (gimple_call_chain (stmt))
+ {
+ if (TREE_CODE (fn) != ADDR_EXPR
+ || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
+ {
+ error ("static chain in indirect gimple call");
+ return true;
+ }
+ fn = TREE_OPERAND (fn, 0);
+
+ if (DECL_NO_STATIC_CHAIN (fn))
+ {
+ error ("static chain with function that doesn't use one");
+ return true;
+ }
+ }
+
/* ??? The C frontend passes unpromoted arguments in case it
didn't see a function declaration before the call. So for now
leave the call arguments unverified. Once we gimplify
diff --git a/gcc/tree-nested.c b/gcc/tree-nested.c
index 7c55c8adc23..bcf971143cf 100644
--- a/gcc/tree-nested.c
+++ b/gcc/tree-nested.c
@@ -1,5 +1,6 @@
/* Nested function decomposition for GIMPLE.
- Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
This file is part of GCC.
@@ -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;
@@ -327,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_NO_STATIC_CHAIN (info->context))
+ fprintf (dump_file, "Resetting no-static-chain for %s\n",
+ lang_hooks.decl_printable_name (info->context, 2));
+
+ DECL_NO_STATIC_CHAIN (info->context) = 0;
}
return decl;
}
@@ -339,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));
@@ -352,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_NO_STATIC_CHAIN (info->context))
+ fprintf (dump_file, "Resetting no-static-chain for %s\n",
+ lang_hooks.decl_printable_name (info->context, 2));
+
+ DECL_NO_STATIC_CHAIN (info->context) = 0;
}
return field;
}
@@ -622,14 +662,9 @@ 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);
}
@@ -1931,6 +1966,8 @@ 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;
@@ -1998,32 +2035,71 @@ 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 set no_static_chain for all decls that haven't
+ used the static chain already for variable access. Notice that we
+ do this pre-order, because we want inner functions to be processed
+ first in the LIFO worklist. */
+ FOR_EACH_NEST_INFO (n, root)
+ {
+ tree decl = n->context;
+ if (n->outer && !n->chain_decl && !n->chain_field)
+ {
+ DECL_NO_STATIC_CHAIN (decl) = 1;
+ 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
+ gcc_assert (!DECL_NO_STATIC_CHAIN (decl));
+ }
+
+ /* 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_no_static_chain = DECL_NO_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_no_static_chain && !DECL_NO_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
@@ -2263,10 +2339,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_NO_STATIC_CHAIN (i->context));
+ arg3 = build_addr (root->frame_decl, context);
arg2 = build_addr (i->context, context);
@@ -2379,20 +2453,19 @@ finalize_nesting_tree_1 (struct nesting_info *root)
}
/* 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. */
@@ -2414,14 +2487,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. */
@@ -2429,18 +2497,18 @@ 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. */
@@ -2462,6 +2530,10 @@ lower_nested_functions (tree fndecl)
{
struct cgraph_node *cgn;
struct nesting_info *root;
+#ifdef ENABLE_CHECKING
+ struct nesting_info *n;
+ bitmap orig_decl_no_static_chain;
+#endif
/* If there are no nested functions, there's nothing to do. */
cgn = cgraph_node (fndecl);
@@ -2470,8 +2542,23 @@ lower_nested_functions (tree fndecl)
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);
+
+#ifdef ENABLE_CHECKING
+ /* The C++ and Ada front ends set DECL_NO_STATIC_CHAIN in various
+ instances where they expect no static chain needed. */
+ orig_decl_no_static_chain = BITMAP_ALLOC (&nesting_info_bitmap_obstack);
+ FOR_EACH_NEST_INFO (n, root)
+ if (DECL_NO_STATIC_CHAIN (n->context))
+ bitmap_set_bit (orig_decl_no_static_chain, DECL_UID (n->context));
+#endif
+
walk_all_functions (convert_nonlocal_reference_stmt,
convert_nonlocal_reference_op,
root);
@@ -2480,11 +2567,26 @@ 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);
+
+#ifdef ENABLE_CHECKING
+ /* Validate the original settings of DECL_NO_STATIC_CHAIN. */
+ FOR_EACH_NEST_INFO (n, root)
+ if (bitmap_bit_p (orig_decl_no_static_chain, DECL_UID (n->context)))
+ gcc_assert (DECL_NO_STATIC_CHAIN (n->context));
+#endif
+
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"