/* Nested function decomposition for trees.
- Copyright (C) 2004 Free Software Foundation, Inc.
+ Copyright (C) 2004, 2005 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))) var_map;
tree context;
tree new_local_var_chain;
tree frame_type;
/* If the type is of variable size or a type which must be created by the
frontend, something is wrong. Note that we explicitly allow
incomplete types here, since we create them ourselves here. */
- gcc_assert (TREE_ADDRESSABLE (type));
+ gcc_assert (!TREE_ADDRESSABLE (type));
gcc_assert (!TYPE_SIZE_UNIT (type)
|| TREE_CODE (TYPE_SIZE_UNIT (type)) == INTEGER_CST);
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. */
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;
}
insert_field_into_struct (get_frame_type (info), field);
- elt = xmalloc (sizeof (*elt));
+ elt = ggc_alloc (sizeof (*elt));
elt->old = decl;
elt->new = field;
*slot = elt;
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 = build (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;
insert_field_into_struct (get_frame_type (info), field);
- elt = xmalloc (sizeof (*elt));
+ elt = ggc_alloc (sizeof (*elt));
elt->old = decl;
elt->new = field;
*slot = elt;
tree_stmt_iterator tsi;
struct nesting_info *info;
bool val_only;
+ bool is_lhs;
+ bool changed;
};
/* A subroutine of walk_function. Iterate over all sub-statements of *TP. */
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:
}
while (root);
}
-
\f
+/* 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
+ done in the types of the PARM_DECLs of the nested function, meaning the
+ argument types of that function will disagree with the arguments in the
+ calls to that function. So we'd either have to make a copy of the nested
+ function corresponding to each time the enclosing function was inlined or
+ add a VIEW_CONVERT_EXPR to each such operand for each call to the nested
+ function. The former is not practical. The latter would still require
+ detecting this case to know when to add the conversions. So, for now at
+ least, we don't inline such an enclosing function.
+
+ We have to do that check recursively, so here return indicating whether
+ FNDECL has such a nested function. ORIG_FN is the function we were
+ trying to inline to use for checking whether any argument is variably
+ modified by anything in it.
+
+ It would be better to do this in tree-inline.c so that we could give
+ the appropriate warning for why a function can't be inlined, but that's
+ too late since the nesting structure has already been flattened and
+ adding a flag just to record this fact seems a waste of a flag. */
+
+static bool
+check_for_nested_with_variably_modified (tree fndecl, tree orig_fndecl)
+{
+ struct cgraph_node *cgn = cgraph_node (fndecl);
+ tree arg;
+
+ for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
+ {
+ for (arg = DECL_ARGUMENTS (cgn->decl); arg; arg = TREE_CHAIN (arg))
+ if (variably_modified_type_p (TREE_TYPE (arg), 0), orig_fndecl)
+ return true;
+
+ if (check_for_nested_with_variably_modified (cgn->decl, orig_fndecl))
+ return true;
+ }
+
+ return false;
+}
+
/* Construct our local datastructure describing the function nesting
tree rooted by CGN. */
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_calloc (1, sizeof (*info));
+ info->var_map = htab_create_ggc (7, var_map_hash, var_map_eq, ggc_free);
info->context = cgn->decl;
for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
info->inner = sub;
}
+ /* See discussion at check_for_nested_with_variably_modified for a
+ discussion of why this has to be here. */
+ if (check_for_nested_with_variably_modified (info->context, info->context))
+ DECL_UNINLINABLE (info->context) = true;
+
return info;
}
if (info->context == target_context)
{
- x = build_addr (info->frame_decl);
+ x = build_addr (info->frame_decl, target_context);
}
else
{
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 = 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;
case ADDR_EXPR:
{
bool save_val_only = wi->val_only;
- tree save_sub = TREE_OPERAND (t, 0);
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 (save_sub != TREE_OPERAND (t, 0))
+ 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. */
- TREE_INVARIANT (t) = 0;
+ save_context = current_function_decl;
+ current_function_decl = info->context;
+ recompute_tree_invarant_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,
break;
default:
- if (!DECL_P (t) && !TYPE_P (t))
+ if (!IS_TYPE_OR_DECL_P (t))
{
*walk_subtrees = 1;
wi->val_only = true;
+ wi->is_lhs = false;
}
break;
}
struct walk_stmt_info *wi = data;
struct nesting_info *info = wi->info;
tree t = *tp, field, x;
+ bool save_val_only;
+ *walk_subtrees = 0;
switch (TREE_CODE (t))
{
case VAR_DECL:
field = lookup_field_for_decl (info, t, NO_INSERT);
if (!field)
break;
+ wi->changed = true;
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;
- tree save_sub = TREE_OPERAND (t, 0);
-
- 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 (TREE_OPERAND (t, 0) != save_sub)
- {
- /* Then the frame decl is now addressable. */
- TREE_ADDRESSABLE (info->frame_decl) = 1;
+ 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;
- /* 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);
- }
- }
+ /* If we converted anything ... */
+ if (wi->changed)
+ {
+ tree save_context;
+
+ /* 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_invarant_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;
default:
- if (!DECL_P (t) && !TYPE_P (t))
+ if (!IS_TYPE_OR_DECL_P (t))
{
*walk_subtrees = 1;
wi->val_only = true;
+ wi->is_lhs = false;
}
break;
}
struct walk_stmt_info *wi = 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 = *slot;
+ if (elt == NULL)
+ {
+ new_label = create_artificial_label ();
+ DECL_NONLOCAL (new_label) = 1;
+
+ elt = ggc_alloc (sizeof (*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);
/* 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);
break;
default:
- if (!DECL_P (t) && !TYPE_P (t))
+ if (!IS_TYPE_OR_DECL_P (t))
*walk_subtrees = 1;
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;
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 = build_addr (x, context);
arg = tree_cons (NULL, x, arg);
x = implicit_built_in_decls[BUILT_IN_INIT_TRAMPOLINE];
/* Dump the translated tree function. */
dump_function (TDI_nested, root->context);
+ 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. */
+ if (node->origin)
+ {
+ cgraph_unnest_node (cgraph_node (root->context));
+ cgraph_finalize_function (root->context, true);
+ }
}
static void
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. */
convert_all_function_calls (root);
finalize_nesting_tree (root);
free_nesting_tree (root);
+ root = NULL;
}
#include "gt-tree-nested.h"