3849e25709
includes fixes for these 109 GCC PR's: 4520 13685 13983 17519 19505 20256 22209 22313 23454 24367 25211 25468 25519 25636 25636 26435 26504 26570 26719 26764 26881 26969 26983 26991 26991 26993 27075 27184 27226 27287 27287 27291 27334 27363 27428 27489 27490 27537 27558 27565 27566 27616 27639 27681 27697 27721 27724 27768 27793 27793 27795 27827 27878 27889 27893 28029 28075 28136 28148 28150 28162 28170 28187 28207 28207 28218 28221 28238 28243 28247 28257 28259 28267 28283 28286 28299 28386 28402 28403 28418 28473 28490 28493 28621 28634 28636 28649 28651 28677 28683 28726 28814 28825 28862 28900 28924 28946 28952 28960 28980 29006 29091 29119 29132 29154 29198 29230 29290 29323
1527 lines
44 KiB
C
1527 lines
44 KiB
C
/* Nested function decomposition for trees.
|
||
Copyright (C) 2004, 2005 Free Software Foundation, Inc.
|
||
|
||
This file is part of GCC.
|
||
|
||
GCC is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 2, or (at your option)
|
||
any later version.
|
||
|
||
GCC is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
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, 51 Franklin Street, Fifth Floor,
|
||
Boston, MA 02110-1301, USA. */
|
||
|
||
#include "config.h"
|
||
#include "system.h"
|
||
#include "coretypes.h"
|
||
#include "tm.h"
|
||
#include "tree.h"
|
||
#include "rtl.h"
|
||
#include "tm_p.h"
|
||
#include "function.h"
|
||
#include "tree-dump.h"
|
||
#include "tree-inline.h"
|
||
#include "tree-gimple.h"
|
||
#include "tree-iterator.h"
|
||
#include "tree-flow.h"
|
||
#include "cgraph.h"
|
||
#include "expr.h"
|
||
#include "langhooks.h"
|
||
#include "ggc.h"
|
||
|
||
|
||
/* The object of this pass is to lower the representation of a set of nested
|
||
functions in order to expose all of the gory details of the various
|
||
nonlocal references. We want to do this sooner rather than later, in
|
||
order to give us more freedom in emitting all of the functions in question.
|
||
|
||
Back in olden times, when gcc was young, we developed an insanely
|
||
complicated scheme whereby variables which were referenced nonlocally
|
||
were forced to live in the stack of the declaring function, and then
|
||
the nested functions magically discovered where these variables were
|
||
placed. In order for this scheme to function properly, it required
|
||
that the outer function be partially expanded, then we switch to
|
||
compiling the inner function, and once done with those we switch back
|
||
to compiling the outer function. Such delicate ordering requirements
|
||
makes it difficult to do whole translation unit optimizations
|
||
involving such functions.
|
||
|
||
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 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
|
||
function's stack frame. More importantly, we don't have to wait for
|
||
that to happen -- since the compilation of the inner function is no
|
||
longer tied to a real stack frame, the nonlocal frame struct can be
|
||
allocated anywhere. Which means that the outer function is now
|
||
inlinable.
|
||
|
||
Theory of operation here is very simple. Iterate over all the
|
||
statements in all the functions (depth first) several times,
|
||
allocating structures and fields on demand. In general we want to
|
||
examine inner functions first, so that we can avoid making changes
|
||
to outer functions which are unnecessary.
|
||
|
||
The order of the passes matters a bit, in that later passes will be
|
||
skipped if it is discovered that the functions don't actually interact
|
||
at all. That is, they're nested in the lexical sense but could have
|
||
been written as independent functions without change. */
|
||
|
||
|
||
struct var_map_elt GTY(())
|
||
{
|
||
tree old;
|
||
tree new;
|
||
};
|
||
|
||
struct nesting_info GTY ((chain_next ("%h.next")))
|
||
{
|
||
struct nesting_info *outer;
|
||
struct nesting_info *inner;
|
||
struct nesting_info *next;
|
||
|
||
htab_t GTY ((param_is (struct var_map_elt))) var_map;
|
||
tree context;
|
||
tree new_local_var_chain;
|
||
tree frame_type;
|
||
tree frame_decl;
|
||
tree chain_field;
|
||
tree chain_decl;
|
||
tree nl_goto_field;
|
||
|
||
bool any_parm_remapped;
|
||
bool any_tramp_created;
|
||
};
|
||
|
||
|
||
/* Hashing and equality functions for nesting_info->var_map. */
|
||
|
||
static hashval_t
|
||
var_map_hash (const void *x)
|
||
{
|
||
const struct var_map_elt *a = x;
|
||
return htab_hash_pointer (a->old);
|
||
}
|
||
|
||
static int
|
||
var_map_eq (const void *x, const void *y)
|
||
{
|
||
const struct var_map_elt *a = x;
|
||
const struct var_map_elt *b = y;
|
||
return a->old == b->old;
|
||
}
|
||
|
||
/* We're working in so many different function contexts simultaneously,
|
||
that create_tmp_var is dangerous. Prevent mishap. */
|
||
#define create_tmp_var cant_use_create_tmp_var_here_dummy
|
||
|
||
/* Like create_tmp_var, except record the variable for registration at
|
||
the given nesting level. */
|
||
|
||
static tree
|
||
create_tmp_var_for (struct nesting_info *info, tree type, const char *prefix)
|
||
{
|
||
tree tmp_var;
|
||
|
||
/* 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 (!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;
|
||
if (TREE_CODE (type) == COMPLEX_TYPE)
|
||
DECL_COMPLEX_GIMPLE_REG_P (tmp_var) = 1;
|
||
|
||
info->new_local_var_chain = tmp_var;
|
||
|
||
return tmp_var;
|
||
}
|
||
|
||
/* Take the address of EXP to be used within function CONTEXT.
|
||
Mark it for addressability as necessary. */
|
||
|
||
tree
|
||
build_addr (tree exp, tree context)
|
||
{
|
||
tree base = exp;
|
||
tree save_context;
|
||
tree retval;
|
||
|
||
while (handled_component_p (base))
|
||
base = TREE_OPERAND (base, 0);
|
||
|
||
if (DECL_P (base))
|
||
TREE_ADDRESSABLE (base) = 1;
|
||
|
||
/* 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. */
|
||
|
||
static void
|
||
insert_field_into_struct (tree type, tree field)
|
||
{
|
||
tree *p;
|
||
|
||
DECL_CONTEXT (field) = type;
|
||
|
||
for (p = &TYPE_FIELDS (type); *p ; p = &TREE_CHAIN (*p))
|
||
if (DECL_ALIGN (field) >= DECL_ALIGN (*p))
|
||
break;
|
||
|
||
TREE_CHAIN (field) = *p;
|
||
*p = field;
|
||
}
|
||
|
||
/* Build or return the RECORD_TYPE that describes the frame state that is
|
||
shared between INFO->CONTEXT and its nested functions. This record will
|
||
not be complete until finalize_nesting_tree; up until that point we'll
|
||
be adding fields as necessary.
|
||
|
||
We also build the DECL that represents this frame in the function. */
|
||
|
||
static tree
|
||
get_frame_type (struct nesting_info *info)
|
||
{
|
||
tree type = info->frame_type;
|
||
if (!type)
|
||
{
|
||
char *name;
|
||
|
||
type = make_node (RECORD_TYPE);
|
||
|
||
name = concat ("FRAME.",
|
||
IDENTIFIER_POINTER (DECL_NAME (info->context)),
|
||
NULL);
|
||
TYPE_NAME (type) = get_identifier (name);
|
||
free (name);
|
||
|
||
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;
|
||
}
|
||
|
||
/* Return true if DECL should be referenced by pointer in the non-local
|
||
frame structure. */
|
||
|
||
static bool
|
||
use_pointer_in_frame (tree decl)
|
||
{
|
||
if (TREE_CODE (decl) == PARM_DECL)
|
||
{
|
||
/* It's illegal to copy TREE_ADDRESSABLE, impossible to copy variable
|
||
sized decls, and inefficient to copy large aggregates. Don't bother
|
||
moving anything but scalar variables. */
|
||
return AGGREGATE_TYPE_P (TREE_TYPE (decl));
|
||
}
|
||
else
|
||
{
|
||
/* Variable sized types make things "interesting" in the frame. */
|
||
return DECL_SIZE (decl) == NULL || !TREE_CONSTANT (DECL_SIZE (decl));
|
||
}
|
||
}
|
||
|
||
/* Given DECL, a non-locally accessed variable, find or create a field
|
||
in the non-local frame structure for the given nesting context. */
|
||
|
||
static tree
|
||
lookup_field_for_decl (struct nesting_info *info, tree decl,
|
||
enum insert_option insert)
|
||
{
|
||
struct var_map_elt *elt, dummy;
|
||
void **slot;
|
||
tree field;
|
||
|
||
dummy.old = decl;
|
||
slot = htab_find_slot (info->var_map, &dummy, insert);
|
||
if (!slot)
|
||
{
|
||
gcc_assert (insert != INSERT);
|
||
return NULL;
|
||
}
|
||
elt = *slot;
|
||
|
||
if (!elt && insert == INSERT)
|
||
{
|
||
field = make_node (FIELD_DECL);
|
||
DECL_NAME (field) = DECL_NAME (decl);
|
||
|
||
if (use_pointer_in_frame (decl))
|
||
{
|
||
TREE_TYPE (field) = build_pointer_type (TREE_TYPE (decl));
|
||
DECL_ALIGN (field) = TYPE_ALIGN (TREE_TYPE (field));
|
||
DECL_NONADDRESSABLE_P (field) = 1;
|
||
}
|
||
else
|
||
{
|
||
TREE_TYPE (field) = TREE_TYPE (decl);
|
||
DECL_SOURCE_LOCATION (field) = DECL_SOURCE_LOCATION (decl);
|
||
DECL_ALIGN (field) = DECL_ALIGN (decl);
|
||
DECL_USER_ALIGN (field) = DECL_USER_ALIGN (decl);
|
||
TREE_ADDRESSABLE (field) = TREE_ADDRESSABLE (decl);
|
||
DECL_NONADDRESSABLE_P (field) = !TREE_ADDRESSABLE (decl);
|
||
TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (decl);
|
||
}
|
||
|
||
insert_field_into_struct (get_frame_type (info), field);
|
||
|
||
elt = ggc_alloc (sizeof (*elt));
|
||
elt->old = decl;
|
||
elt->new = field;
|
||
*slot = elt;
|
||
|
||
if (TREE_CODE (decl) == PARM_DECL)
|
||
info->any_parm_remapped = true;
|
||
}
|
||
else
|
||
field = elt ? elt->new : NULL;
|
||
|
||
return field;
|
||
}
|
||
|
||
/* Build or return the variable that holds the static chain within
|
||
INFO->CONTEXT. This variable may only be used within INFO->CONTEXT. */
|
||
|
||
static tree
|
||
get_chain_decl (struct nesting_info *info)
|
||
{
|
||
tree decl = info->chain_decl;
|
||
if (!decl)
|
||
{
|
||
tree type;
|
||
|
||
type = get_frame_type (info->outer);
|
||
type = build_pointer_type (type);
|
||
|
||
/* 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.
|
||
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_ARG_TYPE (decl) = type;
|
||
|
||
/* Tell tree-inline.c that we never write to this variable, so
|
||
it can copy-prop the replacement value immediately. */
|
||
TREE_READONLY (decl) = 1;
|
||
|
||
info->chain_decl = decl;
|
||
}
|
||
return decl;
|
||
}
|
||
|
||
/* Build or return the field within the non-local frame state that holds
|
||
the static chain for INFO->CONTEXT. This is the way to walk back up
|
||
multiple nesting levels. */
|
||
|
||
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));
|
||
|
||
field = make_node (FIELD_DECL);
|
||
DECL_NAME (field) = get_identifier ("__chain");
|
||
TREE_TYPE (field) = type;
|
||
DECL_ALIGN (field) = TYPE_ALIGN (type);
|
||
DECL_NONADDRESSABLE_P (field) = 1;
|
||
|
||
insert_field_into_struct (get_frame_type (info), field);
|
||
|
||
info->chain_field = field;
|
||
}
|
||
return field;
|
||
}
|
||
|
||
/* Copy EXP into a temporary. Allocate the temporary in the context of
|
||
INFO and insert the initialization statement before TSI. */
|
||
|
||
static tree
|
||
init_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), t, exp);
|
||
SET_EXPR_LOCUS (stmt, EXPR_LOCUS (tsi_stmt (*tsi)));
|
||
tsi_link_before (tsi, stmt, TSI_SAME_STMT);
|
||
|
||
return t;
|
||
}
|
||
|
||
/* Similarly, but only do so to force EXP to satisfy is_gimple_val. */
|
||
|
||
static tree
|
||
tsi_gimplify_val (struct nesting_info *info, tree exp, tree_stmt_iterator *tsi)
|
||
{
|
||
if (is_gimple_val (exp))
|
||
return exp;
|
||
else
|
||
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;
|
||
|
||
static tree
|
||
get_trampoline_type (void)
|
||
{
|
||
tree record, t;
|
||
unsigned align, size;
|
||
|
||
if (trampoline_type)
|
||
return trampoline_type;
|
||
|
||
align = TRAMPOLINE_ALIGNMENT;
|
||
size = TRAMPOLINE_SIZE;
|
||
|
||
/* If we won't be able to guarantee alignment simply via TYPE_ALIGN,
|
||
then allocate extra space so that we can do dynamic alignment. */
|
||
if (align > STACK_BOUNDARY)
|
||
{
|
||
size += ((align/BITS_PER_UNIT) - 1) & -(STACK_BOUNDARY/BITS_PER_UNIT);
|
||
align = STACK_BOUNDARY;
|
||
}
|
||
|
||
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;
|
||
DECL_USER_ALIGN (t) = 1;
|
||
|
||
record = make_node (RECORD_TYPE);
|
||
TYPE_NAME (record) = get_identifier ("__builtin_trampoline");
|
||
TYPE_FIELDS (record) = t;
|
||
layout_type (record);
|
||
|
||
return record;
|
||
}
|
||
|
||
/* Given DECL, a nested function, find or create a field in the non-local
|
||
frame structure for a trampoline for this function. */
|
||
|
||
static tree
|
||
lookup_tramp_for_decl (struct nesting_info *info, tree decl,
|
||
enum insert_option insert)
|
||
{
|
||
struct var_map_elt *elt, dummy;
|
||
void **slot;
|
||
tree field;
|
||
|
||
dummy.old = decl;
|
||
slot = htab_find_slot (info->var_map, &dummy, insert);
|
||
if (!slot)
|
||
{
|
||
gcc_assert (insert != INSERT);
|
||
return NULL;
|
||
}
|
||
elt = *slot;
|
||
|
||
if (!elt && insert == INSERT)
|
||
{
|
||
field = make_node (FIELD_DECL);
|
||
DECL_NAME (field) = DECL_NAME (decl);
|
||
TREE_TYPE (field) = get_trampoline_type ();
|
||
TREE_ADDRESSABLE (field) = 1;
|
||
|
||
insert_field_into_struct (get_frame_type (info), field);
|
||
|
||
elt = ggc_alloc (sizeof (*elt));
|
||
elt->old = decl;
|
||
elt->new = field;
|
||
*slot = elt;
|
||
|
||
info->any_tramp_created = true;
|
||
}
|
||
else
|
||
field = elt ? elt->new : NULL;
|
||
|
||
return field;
|
||
}
|
||
|
||
/* Build or return the field within the non-local frame state that holds
|
||
the non-local goto "jmp_buf". The buffer itself is maintained by the
|
||
rtl middle-end as dynamic stack space is allocated. */
|
||
|
||
static tree
|
||
get_nl_goto_field (struct nesting_info *info)
|
||
{
|
||
tree field = info->nl_goto_field;
|
||
if (!field)
|
||
{
|
||
unsigned size;
|
||
tree type;
|
||
|
||
/* 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 controlled by STACK_SAVEAREA_MODE;
|
||
not the best interface, but it'll do for now. */
|
||
if (Pmode == ptr_mode)
|
||
type = ptr_type_node;
|
||
else
|
||
type = lang_hooks.types.type_for_mode (Pmode, 1);
|
||
|
||
size = GET_MODE_SIZE (STACK_SAVEAREA_MODE (SAVE_NONLOCAL));
|
||
size = size / GET_MODE_SIZE (Pmode);
|
||
size = size + 1;
|
||
|
||
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_TYPE (field) = type;
|
||
DECL_ALIGN (field) = TYPE_ALIGN (type);
|
||
TREE_ADDRESSABLE (field) = 1;
|
||
|
||
insert_field_into_struct (get_frame_type (info), field);
|
||
|
||
info->nl_goto_field = field;
|
||
}
|
||
|
||
return field;
|
||
}
|
||
|
||
/* Convenience routines to walk all statements of a gimple function.
|
||
|
||
For each statement, we invoke CALLBACK via walk_tree. The passed
|
||
data is a walk_stmt_info structure. Of note here is a TSI that
|
||
points to the current statement being walked. The VAL_ONLY flag
|
||
that indicates whether the *TP being examined may be replaced
|
||
with something that matches is_gimple_val (if true) or something
|
||
slightly more complicated (if false). "Something" technically
|
||
means the common subset of is_gimple_lvalue and is_gimple_rhs,
|
||
but we never try to form anything more complicated than that, so
|
||
we don't bother checking. */
|
||
|
||
struct walk_stmt_info
|
||
{
|
||
walk_tree_fn callback;
|
||
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. */
|
||
|
||
static void
|
||
walk_stmts (struct walk_stmt_info *wi, tree *tp)
|
||
{
|
||
tree t = *tp;
|
||
if (!t)
|
||
return;
|
||
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case STATEMENT_LIST:
|
||
{
|
||
tree_stmt_iterator i;
|
||
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
|
||
{
|
||
wi->tsi = i;
|
||
walk_stmts (wi, tsi_stmt_ptr (i));
|
||
}
|
||
}
|
||
break;
|
||
|
||
case COND_EXPR:
|
||
walk_tree (&COND_EXPR_COND (t), wi->callback, wi, NULL);
|
||
walk_stmts (wi, &COND_EXPR_THEN (t));
|
||
walk_stmts (wi, &COND_EXPR_ELSE (t));
|
||
break;
|
||
case CATCH_EXPR:
|
||
walk_stmts (wi, &CATCH_BODY (t));
|
||
break;
|
||
case EH_FILTER_EXPR:
|
||
walk_stmts (wi, &EH_FILTER_FAILURE (t));
|
||
break;
|
||
case TRY_CATCH_EXPR:
|
||
case TRY_FINALLY_EXPR:
|
||
walk_stmts (wi, &TREE_OPERAND (t, 0));
|
||
walk_stmts (wi, &TREE_OPERAND (t, 1));
|
||
break;
|
||
case BIND_EXPR:
|
||
walk_stmts (wi, &BIND_EXPR_BODY (t));
|
||
break;
|
||
|
||
case RETURN_EXPR:
|
||
walk_stmts (wi, &TREE_OPERAND (t, 0));
|
||
break;
|
||
|
||
case MODIFY_EXPR:
|
||
/* 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:
|
||
wi->val_only = true;
|
||
walk_tree (tp, wi->callback, wi, NULL);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Invoke CALLBACK on all statements of INFO->CONTEXT. */
|
||
|
||
static void
|
||
walk_function (walk_tree_fn callback, struct nesting_info *info)
|
||
{
|
||
struct walk_stmt_info wi;
|
||
|
||
memset (&wi, 0, sizeof (wi));
|
||
wi.callback = callback;
|
||
wi.info = info;
|
||
wi.val_only = true;
|
||
|
||
walk_stmts (&wi, &DECL_SAVED_TREE (info->context));
|
||
}
|
||
|
||
/* Similarly for ROOT and all functions nested underneath, depth first. */
|
||
|
||
static void
|
||
walk_all_functions (walk_tree_fn callback, struct nesting_info *root)
|
||
{
|
||
do
|
||
{
|
||
if (root->inner)
|
||
walk_all_functions (callback, root->inner);
|
||
walk_function (callback, root);
|
||
root = root->next;
|
||
}
|
||
while (root);
|
||
}
|
||
|
||
/* 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 = 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)
|
||
{
|
||
struct nesting_info *sub = create_nesting_tree (cgn);
|
||
sub->outer = info;
|
||
sub->next = info->inner;
|
||
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;
|
||
}
|
||
|
||
/* Return an expression computing the static chain for TARGET_CONTEXT
|
||
from INFO->CONTEXT. Insert any necessary computations before TSI. */
|
||
|
||
static tree
|
||
get_static_chain (struct nesting_info *info, tree target_context,
|
||
tree_stmt_iterator *tsi)
|
||
{
|
||
struct nesting_info *i;
|
||
tree x;
|
||
|
||
if (info->context == target_context)
|
||
{
|
||
x = build_addr (info->frame_decl, target_context);
|
||
}
|
||
else
|
||
{
|
||
x = get_chain_decl (info);
|
||
|
||
for (i = info->outer; i->context != target_context; i = i->outer)
|
||
{
|
||
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, NULL_TREE);
|
||
x = init_tmp_var (info, x, tsi);
|
||
}
|
||
}
|
||
|
||
return x;
|
||
}
|
||
|
||
/* Return an expression referencing FIELD from TARGET_CONTEXT's non-local
|
||
frame as seen from INFO->CONTEXT. Insert any necessary computations
|
||
before TSI. */
|
||
|
||
static tree
|
||
get_frame_field (struct nesting_info *info, tree target_context,
|
||
tree field, tree_stmt_iterator *tsi)
|
||
{
|
||
struct nesting_info *i;
|
||
tree x;
|
||
|
||
if (info->context == target_context)
|
||
{
|
||
/* Make sure frame_decl gets created. */
|
||
(void) get_frame_type (info);
|
||
x = info->frame_decl;
|
||
}
|
||
else
|
||
{
|
||
x = get_chain_decl (info);
|
||
|
||
for (i = info->outer; i->context != target_context; i = i->outer)
|
||
{
|
||
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, 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, NULL_TREE);
|
||
return x;
|
||
}
|
||
|
||
/* Called via walk_function+walk_tree, rewrite all references to VAR
|
||
and PARM_DECLs that belong to outer functions.
|
||
|
||
The rewrite will involve some number of structure accesses back up
|
||
the static chain. E.g. for a variable FOO up one nesting level it'll
|
||
be CHAIN->FOO. For two levels it'll be CHAIN->__chain->FOO. Further
|
||
indirections apply to decls for which use_pointer_in_frame is true. */
|
||
|
||
static tree
|
||
convert_nonlocal_reference (tree *tp, int *walk_subtrees, void *data)
|
||
{
|
||
struct walk_stmt_info *wi = data;
|
||
struct nesting_info *info = wi->info;
|
||
tree t = *tp;
|
||
|
||
*walk_subtrees = 0;
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case VAR_DECL:
|
||
/* Non-automatic variables are never processed. */
|
||
if (TREE_STATIC (t) || DECL_EXTERNAL (t))
|
||
break;
|
||
/* FALLTHRU */
|
||
|
||
case PARM_DECL:
|
||
if (decl_function_context (t) != info->context)
|
||
{
|
||
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 = lookup_field_for_decl (i, t, INSERT);
|
||
x = get_frame_field (info, target_context, x, &wi->tsi);
|
||
if (use_pointer_in_frame (t))
|
||
{
|
||
x = init_tmp_var (info, x, &wi->tsi);
|
||
x = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (x)), x);
|
||
}
|
||
|
||
if (wi->val_only)
|
||
{
|
||
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 GOTO_EXPR:
|
||
/* Don't walk non-local gotos for now. */
|
||
if (TREE_CODE (GOTO_DESTINATION (t)) != LABEL_DECL)
|
||
{
|
||
*walk_subtrees = 1;
|
||
wi->val_only = true;
|
||
wi->is_lhs = false;
|
||
}
|
||
break;
|
||
|
||
case LABEL_DECL:
|
||
/* We're taking the address of a label from a parent function, but
|
||
this is not itself a non-local goto. Mark the label such that it
|
||
will not be deleted, much as we would with a label address in
|
||
static storage. */
|
||
if (decl_function_context (t) != info->context)
|
||
FORCED_LABEL (t) = 1;
|
||
break;
|
||
|
||
case ADDR_EXPR:
|
||
{
|
||
bool save_val_only = wi->val_only;
|
||
|
||
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 (wi->changed)
|
||
{
|
||
tree save_context;
|
||
|
||
/* If we changed anything, then TREE_INVARIANT is be wrong,
|
||
since we're no longer directly referencing a decl. */
|
||
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 = tsi_gimplify_val (wi->info, t, &wi->tsi);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case REALPART_EXPR:
|
||
case IMAGPART_EXPR:
|
||
case COMPONENT_REF:
|
||
case ARRAY_REF:
|
||
case ARRAY_RANGE_REF:
|
||
case BIT_FIELD_REF:
|
||
/* 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;
|
||
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 (!IS_TYPE_OR_DECL_P (t))
|
||
{
|
||
*walk_subtrees = 1;
|
||
wi->val_only = true;
|
||
wi->is_lhs = false;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return NULL_TREE;
|
||
}
|
||
|
||
/* Called via walk_function+walk_tree, rewrite all references to VAR
|
||
and PARM_DECLs that were referenced by inner nested functions.
|
||
The rewrite will be a structure reference to the local frame variable. */
|
||
|
||
static tree
|
||
convert_local_reference (tree *tp, int *walk_subtrees, void *data)
|
||
{
|
||
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:
|
||
/* Non-automatic variables are never processed. */
|
||
if (TREE_STATIC (t) || DECL_EXTERNAL (t))
|
||
break;
|
||
/* FALLTHRU */
|
||
|
||
case PARM_DECL:
|
||
if (decl_function_context (t) == info->context)
|
||
{
|
||
/* If we copied a pointer to the frame, then the original decl
|
||
is used unchanged in the parent function. */
|
||
if (use_pointer_in_frame (t))
|
||
break;
|
||
|
||
/* No need to transform anything if no child references the
|
||
variable. */
|
||
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)
|
||
{
|
||
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:
|
||
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 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:
|
||
case IMAGPART_EXPR:
|
||
case COMPONENT_REF:
|
||
case ARRAY_REF:
|
||
case ARRAY_RANGE_REF:
|
||
case BIT_FIELD_REF:
|
||
/* 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;
|
||
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);
|
||
wi->val_only = save_val_only;
|
||
break;
|
||
|
||
default:
|
||
if (!IS_TYPE_OR_DECL_P (t))
|
||
{
|
||
*walk_subtrees = 1;
|
||
wi->val_only = true;
|
||
wi->is_lhs = false;
|
||
}
|
||
break;
|
||
}
|
||
|
||
return NULL_TREE;
|
||
}
|
||
|
||
/* Called via walk_function+walk_tree, rewrite all GOTO_EXPRs that
|
||
reference labels from outer functions. The rewrite will be a
|
||
call to __builtin_nonlocal_goto. */
|
||
|
||
static tree
|
||
convert_nl_goto_reference (tree *tp, int *walk_subtrees, void *data)
|
||
{
|
||
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, dummy;
|
||
void **slot;
|
||
|
||
*walk_subtrees = 0;
|
||
if (TREE_CODE (t) != GOTO_EXPR)
|
||
return NULL_TREE;
|
||
label = GOTO_DESTINATION (t);
|
||
if (TREE_CODE (label) != LABEL_DECL)
|
||
return NULL_TREE;
|
||
target_context = decl_function_context (label);
|
||
if (target_context == info->context)
|
||
return NULL_TREE;
|
||
|
||
for (i = info->outer; target_context != i->context; i = i->outer)
|
||
continue;
|
||
|
||
/* 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 behavior in the cfg, as well as cause the
|
||
(hairy target-specific) non-local goto receiver code to be generated
|
||
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, target_context);
|
||
x = tsi_gimplify_val (info, x, &wi->tsi);
|
||
arg = tree_cons (NULL, x, NULL);
|
||
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);
|
||
|
||
SET_EXPR_LOCUS (x, EXPR_LOCUS (tsi_stmt (wi->tsi)));
|
||
*tsi_stmt_ptr (wi->tsi) = x;
|
||
|
||
return NULL_TREE;
|
||
}
|
||
|
||
/* Called via walk_function+walk_tree, rewrite all LABEL_EXPRs that
|
||
are referenced via nonlocal goto from a nested function. The rewrite
|
||
will involve installing a newly generated DECL_NONLOCAL label, and
|
||
(potentially) a branch around the rtl gunk that is assumed to be
|
||
attached to such a label. */
|
||
|
||
static tree
|
||
convert_nl_goto_receiver (tree *tp, int *walk_subtrees, void *data)
|
||
{
|
||
struct walk_stmt_info *wi = data;
|
||
struct nesting_info *info = wi->info;
|
||
tree t = *tp, label, new_label, x;
|
||
struct var_map_elt *elt, dummy;
|
||
tree_stmt_iterator tmp_tsi;
|
||
|
||
*walk_subtrees = 0;
|
||
if (TREE_CODE (t) != LABEL_EXPR)
|
||
return NULL_TREE;
|
||
label = LABEL_EXPR_LABEL (t);
|
||
|
||
dummy.old = label;
|
||
elt = htab_find (info->var_map, &dummy);
|
||
if (!elt)
|
||
return NULL_TREE;
|
||
new_label = elt->new;
|
||
|
||
/* If there's any possibility that the previous statement falls through,
|
||
then we must branch around the new non-local label. */
|
||
tmp_tsi = wi->tsi;
|
||
tsi_prev (&tmp_tsi);
|
||
if (tsi_end_p (tmp_tsi) || block_may_fallthru (tsi_stmt (tmp_tsi)))
|
||
{
|
||
x = build1 (GOTO_EXPR, void_type_node, label);
|
||
tsi_link_before (&wi->tsi, x, TSI_SAME_STMT);
|
||
}
|
||
x = build1 (LABEL_EXPR, void_type_node, new_label);
|
||
tsi_link_before (&wi->tsi, x, TSI_SAME_STMT);
|
||
|
||
return NULL_TREE;
|
||
}
|
||
|
||
/* Called via walk_function+walk_tree, rewrite all references to addresses
|
||
of nested functions that require the use of trampolines. The rewrite
|
||
will involve a reference a trampoline generated for the occasion. */
|
||
|
||
static tree
|
||
convert_tramp_reference (tree *tp, int *walk_subtrees, void *data)
|
||
{
|
||
struct walk_stmt_info *wi = data;
|
||
struct nesting_info *info = wi->info, *i;
|
||
tree t = *tp, decl, target_context, x, arg;
|
||
|
||
*walk_subtrees = 0;
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case ADDR_EXPR:
|
||
/* Build
|
||
T.1 = &CHAIN->tramp;
|
||
T.2 = __builtin_adjust_trampoline (T.1);
|
||
T.3 = (func_type)T.2;
|
||
*/
|
||
|
||
decl = TREE_OPERAND (t, 0);
|
||
if (TREE_CODE (decl) != FUNCTION_DECL)
|
||
break;
|
||
|
||
/* Only need to process nested functions. */
|
||
target_context = decl_function_context (decl);
|
||
if (!target_context)
|
||
break;
|
||
|
||
/* If the nested function doesn't use a static chain, then
|
||
it doesn't need a trampoline. */
|
||
if (DECL_NO_STATIC_CHAIN (decl))
|
||
break;
|
||
|
||
/* Lookup the immediate parent of the callee, as that's where
|
||
we need to insert the trampoline. */
|
||
for (i = info; i->context != target_context; i = i->outer)
|
||
continue;
|
||
x = lookup_tramp_for_decl (i, decl, INSERT);
|
||
|
||
/* Compute the address of the field holding the trampoline. */
|
||
x = get_frame_field (info, target_context, 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
|
||
computing extra alignment, but it can really be anything. */
|
||
x = implicit_built_in_decls[BUILT_IN_ADJUST_TRAMPOLINE];
|
||
x = build_function_call_expr (x, arg);
|
||
x = init_tmp_var (info, x, &wi->tsi);
|
||
|
||
/* Cast back to the proper function type. */
|
||
x = build1 (NOP_EXPR, TREE_TYPE (t), x);
|
||
x = init_tmp_var (info, x, &wi->tsi);
|
||
|
||
*tp = x;
|
||
break;
|
||
|
||
case CALL_EXPR:
|
||
/* Only walk call arguments, lest we generate trampolines for
|
||
direct calls. */
|
||
walk_tree (&TREE_OPERAND (t, 1), convert_tramp_reference, wi, NULL);
|
||
break;
|
||
|
||
default:
|
||
if (!IS_TYPE_OR_DECL_P (t))
|
||
*walk_subtrees = 1;
|
||
break;
|
||
}
|
||
|
||
return NULL_TREE;
|
||
}
|
||
|
||
/* Called via walk_function+walk_tree, rewrite all CALL_EXPRs that
|
||
reference nested functions to make sure that the static chain is
|
||
set up properly for the call. */
|
||
|
||
static tree
|
||
convert_call_expr (tree *tp, int *walk_subtrees, void *data)
|
||
{
|
||
struct walk_stmt_info *wi = data;
|
||
struct nesting_info *info = wi->info;
|
||
tree t = *tp, decl, target_context;
|
||
|
||
*walk_subtrees = 0;
|
||
switch (TREE_CODE (t))
|
||
{
|
||
case CALL_EXPR:
|
||
decl = get_callee_fndecl (t);
|
||
if (!decl)
|
||
break;
|
||
target_context = decl_function_context (decl);
|
||
if (target_context && !DECL_NO_STATIC_CHAIN (decl))
|
||
TREE_OPERAND (t, 2)
|
||
= get_static_chain (info, target_context, &wi->tsi);
|
||
break;
|
||
|
||
case RETURN_EXPR:
|
||
case MODIFY_EXPR:
|
||
case WITH_SIZE_EXPR:
|
||
/* Only return modify and with_size_expr may contain calls. */
|
||
*walk_subtrees = 1;
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
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. */
|
||
|
||
static void
|
||
convert_all_function_calls (struct nesting_info *root)
|
||
{
|
||
do
|
||
{
|
||
if (root->inner)
|
||
convert_all_function_calls (root->inner);
|
||
|
||
walk_function (convert_tramp_reference, root);
|
||
walk_function (convert_call_expr, root);
|
||
|
||
/* 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));
|
||
|
||
root = root->next;
|
||
}
|
||
while (root);
|
||
}
|
||
|
||
/* 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
|
||
struct function for rtl to find. */
|
||
|
||
static void
|
||
finalize_nesting_tree_1 (struct nesting_info *root)
|
||
{
|
||
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);
|
||
}
|
||
|
||
/* If any parameters were referenced non-locally, then we need to
|
||
insert a copy. Likewise, if any variables were referenced by
|
||
pointer, we need to initialize the address. */
|
||
if (root->any_parm_remapped)
|
||
{
|
||
tree p;
|
||
for (p = DECL_ARGUMENTS (context); p ; p = TREE_CHAIN (p))
|
||
{
|
||
tree field, x, y;
|
||
|
||
field = lookup_field_for_decl (root, p, NO_INSERT);
|
||
if (!field)
|
||
continue;
|
||
|
||
if (use_pointer_in_frame (p))
|
||
x = build_addr (p, context);
|
||
else
|
||
x = p;
|
||
|
||
y = build (COMPONENT_REF, TREE_TYPE (field),
|
||
root->frame_decl, field, NULL_TREE);
|
||
x = build (MODIFY_EXPR, TREE_TYPE (field), y, x);
|
||
append_to_statement_list (x, &stmt_list);
|
||
}
|
||
}
|
||
|
||
/* If a chain_field was created, then it needs to be initialized
|
||
from chain_decl. */
|
||
if (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);
|
||
}
|
||
|
||
/* If trampolines were created, then we need to initialize them. */
|
||
if (root->any_tramp_created)
|
||
{
|
||
struct nesting_info *i;
|
||
for (i = root->inner; i ; i = i->next)
|
||
{
|
||
tree arg, x, field;
|
||
|
||
field = lookup_tramp_for_decl (root, i->context, NO_INSERT);
|
||
if (!field)
|
||
continue;
|
||
|
||
if (DECL_NO_STATIC_CHAIN (i->context))
|
||
x = null_pointer_node;
|
||
else
|
||
x = build_addr (root->frame_decl, context);
|
||
arg = tree_cons (NULL, x, NULL);
|
||
|
||
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, context);
|
||
arg = tree_cons (NULL, x, arg);
|
||
|
||
x = implicit_built_in_decls[BUILT_IN_INIT_TRAMPOLINE];
|
||
x = build_function_call_expr (x, arg);
|
||
|
||
append_to_statement_list (x, &stmt_list);
|
||
}
|
||
}
|
||
|
||
/* If we created initialization statements, insert them. */
|
||
if (stmt_list)
|
||
{
|
||
annotate_all_with_locus (&stmt_list,
|
||
DECL_SOURCE_LOCATION (context));
|
||
append_to_statement_list (BIND_EXPR_BODY (DECL_SAVED_TREE (context)),
|
||
&stmt_list);
|
||
BIND_EXPR_BODY (DECL_SAVED_TREE (context)) = stmt_list;
|
||
}
|
||
|
||
/* If a chain_decl was created, then it needs to be registered with
|
||
struct function so that it gets initialized from the static chain
|
||
register at the beginning of the function. */
|
||
sf = DECL_STRUCT_FUNCTION (root->context);
|
||
sf->static_chain_decl = root->chain_decl;
|
||
|
||
/* Similarly for the non-local goto save area. */
|
||
if (root->nl_goto_field)
|
||
{
|
||
sf->nonlocal_goto_save_area
|
||
= get_frame_field (root, context, root->nl_goto_field, NULL);
|
||
sf->has_nonlocal_label = 1;
|
||
}
|
||
|
||
/* 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,
|
||
DECL_SAVED_TREE (root->context));
|
||
|
||
/* 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
|
||
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);
|
||
}
|
||
|
||
/* Free the data structures allocated during this pass. */
|
||
|
||
static void
|
||
free_nesting_tree (struct nesting_info *root)
|
||
{
|
||
struct nesting_info *next;
|
||
do
|
||
{
|
||
if (root->inner)
|
||
free_nesting_tree (root->inner);
|
||
htab_delete (root->var_map);
|
||
next = root->next;
|
||
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 cgraph_node *cgn;
|
||
|
||
/* If there are no nested functions, there's nothing to do. */
|
||
cgn = cgraph_node (fndecl);
|
||
if (!cgn->nested)
|
||
return;
|
||
|
||
root = create_nesting_tree (cgn);
|
||
walk_all_functions (convert_nonlocal_reference, root);
|
||
walk_all_functions (convert_local_reference, root);
|
||
walk_all_functions (convert_nl_goto_reference, root);
|
||
walk_all_functions (convert_nl_goto_receiver, root);
|
||
convert_all_function_calls (root);
|
||
finalize_nesting_tree (root);
|
||
free_nesting_tree (root);
|
||
root = NULL;
|
||
}
|
||
|
||
#include "gt-tree-nested.h"
|