/* 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 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_ADDRESSABLE (type)
- || (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;
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. */
-static tree
-build_addr (tree exp)
+tree
+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. */
slot = htab_find_slot (info->var_map, &dummy, insert);
if (!slot)
{
- if (insert == INSERT)
- abort ();
+ gcc_assert (insert != INSERT);
return NULL;
}
elt = *slot;
/* 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;
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 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;
}
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),
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;
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, 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)
{
+ 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 = 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 REALPART_EXPR:
case IMAGPART_EXPR:
case COMPONENT_REF:
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_local_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;
}
/* 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 = gimplify_val (info, x, &wi->tsi);
+ 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 = gimplify_val (info, x, &wi->tsi);
+ x = build_addr (x, target_context);
+ 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. */
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