/* 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.
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 incomming
+ structure herein called the "nonlocal frame struct". The incoming
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
{
tree tmp_var;
-#if defined ENABLE_CHECKING
- /* If the type is an array or a type which must be created by the
+ /* 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. */
- if (TREE_CODE (type) == ARRAY_TYPE || TREE_ADDRESSABLE (type))
- abort ();
- if (TYPE_SIZE_UNIT (type)
- && TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST)
- abort ();
-#endif
+ gcc_assert (!TREE_ADDRESSABLE (type));
+ gcc_assert (!TYPE_SIZE_UNIT (type)
+ || TREE_CODE (TYPE_SIZE_UNIT (type)) == INTEGER_CST);
tmp_var = create_tmp_var_raw (type, prefix);
DECL_CONTEXT (tmp_var) = info->context;
TREE_CHAIN (tmp_var) = info->new_local_var_chain;
+ DECL_SEEN_IN_BIND_EXPR_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. */
-static tree
+tree
build_addr (tree exp)
{
tree base = exp;
- while (TREE_CODE (base) == COMPONENT_REF || TREE_CODE (base) == ARRAY_REF)
+
+ while (handled_component_p (base))
base = TREE_OPERAND (base, 0);
+
if (DECL_P (base))
TREE_ADDRESSABLE (base) = 1;
slot = htab_find_slot (info->var_map, &dummy, insert);
if (!slot)
{
- if (insert == INSERT)
- abort ();
+ gcc_assert (insert != INSERT);
return NULL;
}
elt = *slot;
/* Note that this variable is *not* entered into any BIND_EXPR;
the construction of this variable is handled specially in
- expand_function_start and initialize_inlined_parameters. */
- decl = create_tmp_var_raw (type, "CHAIN");
+ 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
+ the caller. */
+ decl = build_decl (PARM_DECL, create_tmp_var_name ("CHAIN"), type);
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+ TREE_USED (decl) = 1;
DECL_CONTEXT (decl) = info->context;
- decl->decl.seen_in_bind_expr = 1;
-
- /* The initialization of CHAIN is not visible to the tree-ssa
- analyzers and optimizers. Thus we do not want to issue
- warnings for CHAIN. */
- TREE_NO_WARNING (decl) = 1;
+ DECL_ARG_TYPE (decl) = type;
/* Tell tree-inline.c that we never write to this variable, so
it can copy-prop the replacement value immediately. */
/* Similarly, but only do so to force EXP to satisfy is_gimple_val. */
static tree
-gimplify_val (struct nesting_info *info, tree exp, tree_stmt_iterator *tsi)
+tsi_gimplify_val (struct nesting_info *info, tree exp, tree_stmt_iterator *tsi)
{
if (is_gimple_val (exp))
return exp;
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;
align = STACK_BOUNDARY;
}
- t = build_index_type (build_int_2 (size - 1, 0));
+ 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);
DECL_ALIGN (t) = align;
slot = htab_find_slot (info->var_map, &dummy, insert);
if (!slot)
{
- if (insert == INSERT)
- abort ();
+ gcc_assert (insert != INSERT);
return NULL;
}
elt = *slot;
/* For __builtin_nonlocal_goto, we need N words. The first is the
frame pointer, the rest is for the target's stack pointer save
- area. The number of words is controled by STACK_SAVEAREA_MODE;
+ area. The number of words is controlled by STACK_SAVEAREA_MODE;
not the best interface, but it'll do for now. */
if (Pmode == ptr_mode)
type = ptr_type_node;
size = size / GET_MODE_SIZE (Pmode);
size = size + 1;
- type = build_array_type (type, build_index_type (build_int_2 (size, 0)));
+ type = build_array_type
+ (type, build_index_type (build_int_cst (NULL_TREE, size)));
field = make_node (FIELD_DECL);
DECL_NAME (field) = get_identifier ("__nl_goto_buf");
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 CATCH_EXPR:
walk_stmts (wi, &CATCH_BODY (t));
+ break;
case EH_FILTER_EXPR:
walk_stmts (wi, &EH_FILTER_FAILURE (t));
break;
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. */
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;
}
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);
+ x = build (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);
+ x = build (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);
+ x = build (COMPONENT_REF, TREE_TYPE (field), x, field, NULL_TREE);
return x;
}
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)
{
/* If we changed anything, then TREE_INVARIANT is be wrong,
since we're no longer directly referencing a decl. */
- TREE_INVARIANT (t) = 0;
+ recompute_tree_invarant_for_addr_expr (t);
/* If the callback converted the address argument in a context
where we only accept variables (and min_invariant, presumably),
then compute the address into a temporary. */
if (save_val_only)
- *tp = gimplify_val (wi->info, t, &wi->tsi);
+ *tp = tsi_gimplify_val (wi->info, t, &wi->tsi);
}
}
break;
- case COMPONENT_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), convert_nonlocal_reference, wi, NULL);
- wi->val_only = true;
- break;
-
+ case COMPONENT_REF:
case ARRAY_REF:
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), convert_nonlocal_reference, wi, NULL);
- wi->val_only = true;
- walk_tree (&TREE_OPERAND (t, 1), convert_nonlocal_reference, wi, NULL);
- break;
-
+ case ARRAY_RANGE_REF:
case BIT_FIELD_REF:
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), convert_nonlocal_reference, wi, NULL);
+ /* 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. */
wi->val_only = true;
- walk_tree (&TREE_OPERAND (t, 1), convert_nonlocal_reference, wi, NULL);
- walk_tree (&TREE_OPERAND (t, 2), convert_nonlocal_reference, wi, NULL);
+ 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,
+ NULL);
+ else if (TREE_CODE (t) == ARRAY_REF
+ || TREE_CODE (t) == ARRAY_RANGE_REF)
+ {
+ walk_tree (&TREE_OPERAND (t, 1), convert_nonlocal_reference, wi,
+ NULL);
+ walk_tree (&TREE_OPERAND (t, 2), convert_nonlocal_reference, wi,
+ NULL);
+ walk_tree (&TREE_OPERAND (t, 3), convert_nonlocal_reference, wi,
+ NULL);
+ }
+ else if (TREE_CODE (t) == BIT_FIELD_REF)
+ {
+ walk_tree (&TREE_OPERAND (t, 1), convert_nonlocal_reference, wi,
+ NULL);
+ walk_tree (&TREE_OPERAND (t, 2), convert_nonlocal_reference, wi,
+ NULL);
+ }
+ }
+ wi->val_only = false;
+ walk_tree (tp, convert_nonlocal_reference, wi, NULL);
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, y;
+ tree t = *tp, field, x;
switch (TREE_CODE (t))
{
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;
+ 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 converted anything ... */
- if (TREE_OPERAND (t, 0) != save_sub)
+ if (wi->changed)
{
/* 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 = gimplify_val (wi->info, t, &wi->tsi);
+ *tp = tsi_gimplify_val (wi->info, t, &wi->tsi);
}
}
break;
- case CALL_EXPR:
- *walk_subtrees = 1;
-
- /* Ready for some fun? We need to recognize
- __builtin_stack_alloc (&x, n)
- and insert
- FRAME.x = &x
- after that. X should have use_pointer_in_frame set. We can't
- do this any earlier, since we can't meaningfully evaluate &x. */
-
- x = get_callee_fndecl (t);
- if (!x || DECL_BUILT_IN_CLASS (x) != BUILT_IN_NORMAL)
- break;
- if (DECL_FUNCTION_CODE (x) != BUILT_IN_STACK_ALLOC)
- break;
- t = TREE_VALUE (TREE_OPERAND (t, 1));
- if (TREE_CODE (t) != ADDR_EXPR)
- abort ();
- t = TREE_OPERAND (t, 0);
- if (TREE_CODE (t) != VAR_DECL)
- abort ();
- field = lookup_field_for_decl (info, t, NO_INSERT);
- if (!field)
- break;
- if (!use_pointer_in_frame (t))
- abort ();
-
- x = build_addr (t);
- y = get_frame_field (info, info->context, field, &wi->tsi);
- x = build (MODIFY_EXPR, void_type_node, y, x);
- SET_EXPR_LOCUS (x, EXPR_LOCUS (tsi_stmt (wi->tsi)));
- tsi_link_after (&wi->tsi, x, TSI_SAME_STMT);
- break;
-
- case COMPONENT_REF:
case REALPART_EXPR:
case IMAGPART_EXPR:
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), convert_local_reference, wi, NULL);
- wi->val_only = true;
- break;
-
+ case COMPONENT_REF:
case ARRAY_REF:
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), convert_local_reference, wi, NULL);
- wi->val_only = true;
- walk_tree (&TREE_OPERAND (t, 1), convert_local_reference, wi, NULL);
- break;
-
+ case ARRAY_RANGE_REF:
case BIT_FIELD_REF:
- wi->val_only = false;
- walk_tree (&TREE_OPERAND (t, 0), convert_local_reference, wi, NULL);
+ /* 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. */
wi->val_only = true;
- walk_tree (&TREE_OPERAND (t, 1), convert_local_reference, wi, NULL);
- walk_tree (&TREE_OPERAND (t, 2), convert_local_reference, wi, NULL);
+ 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,
+ NULL);
+ else if (TREE_CODE (t) == ARRAY_REF
+ || TREE_CODE (t) == ARRAY_RANGE_REF)
+ {
+ walk_tree (&TREE_OPERAND (t, 1), convert_local_reference, wi,
+ NULL);
+ walk_tree (&TREE_OPERAND (t, 2), convert_local_reference, wi,
+ NULL);
+ walk_tree (&TREE_OPERAND (t, 3), convert_local_reference, wi,
+ NULL);
+ }
+ else if (TREE_CODE (t) == BIT_FIELD_REF)
+ {
+ walk_tree (&TREE_OPERAND (t, 1), convert_local_reference, wi,
+ NULL);
+ walk_tree (&TREE_OPERAND (t, 2), convert_local_reference, wi,
+ NULL);
+ }
+ }
+ wi->val_only = false;
+ walk_tree (tp, convert_local_reference, wi, NULL);
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;
}
/* The original user label may also be use for a normal goto, therefore
we must create a new label that will actually receive the abnormal
control transfer. This new label will be marked LABEL_NONLOCAL; this
- mark will trigger proper behaviour in the cfg, as well as cause the
+ 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 ();
field = get_nl_goto_field (i);
x = get_frame_field (info, target_context, field, &wi->tsi);
x = build_addr (x);
- x = gimplify_val (info, x, &wi->tsi);
+ x = tsi_gimplify_val (info, x, &wi->tsi);
arg = tree_cons (NULL, x, NULL);
x = build_addr (new_label);
arg = tree_cons (NULL, 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 = gimplify_val (info, x, &wi->tsi);
+ x = tsi_gimplify_val (info, x, &wi->tsi);
arg = tree_cons (NULL, x, NULL);
/* Do machine-specific ugliness. Normally this will involve
break;
default:
- if (!DECL_P (t) && !TYPE_P (t))
+ if (!IS_TYPE_OR_DECL_P (t))
*walk_subtrees = 1;
break;
}
case RETURN_EXPR:
case MODIFY_EXPR:
- /* Only return and modify may contain calls. */
+ case WITH_SIZE_EXPR:
+ /* Only return modify and with_size_expr may contain calls. */
*walk_subtrees = 1;
break;
if (root->outer && !root->chain_decl && !root->chain_field)
DECL_NO_STATIC_CHAIN (root->context) = 1;
else
- {
-#ifdef ENABLE_CHECKING
- if (DECL_NO_STATIC_CHAIN (root->context))
- abort ();
-#endif
- }
+ gcc_assert (!DECL_NO_STATIC_CHAIN (root->context));
root = root->next;
}
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. */
x = p;
y = build (COMPONENT_REF, TREE_TYPE (field),
- root->frame_decl, field);
+ root->frame_decl, field, NULL_TREE);
x = build (MODIFY_EXPR, TREE_TYPE (field), y, x);
append_to_statement_list (x, &stmt_list);
}
from chain_decl. */
if (root->chain_field)
{
- tree x;
- x = build (COMPONENT_REF, TREE_TYPE (root->chain_field),
- root->frame_decl, 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));
append_to_statement_list (x, &stmt_list);
}
arg = tree_cons (NULL, x, arg);
x = build (COMPONENT_REF, TREE_TYPE (field),
- root->frame_decl, field);
+ root->frame_decl, field, NULL_TREE);
x = build_addr (x);
arg = tree_cons (NULL, x, arg);
sf->has_nonlocal_label = 1;
}
- /* Make sure all new local variables get insertted into the
+ /* 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,
/* 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