/* Tree inlining.
- Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Contributed by Alexandre Oliva <aoliva@redhat.com>
This file is part of GCC.
#include "integrate.h"
#include "varray.h"
#include "hashtab.h"
-#include "pointer-set.h"
#include "splay-tree.h"
#include "langhooks.h"
#include "cgraph.h"
#include "intl.h"
#include "tree-mudflap.h"
+#include "tree-flow.h"
#include "function.h"
#include "diagnostic.h"
+#include "debug.h"
/* I'm not real happy about this, but we need to handle gimple and
non-gimple trees. */
/* Nonzero if we are currently within the cleanup for a
TARGET_EXPR. */
int in_target_cleanup_p;
- /* A list of the functions current function has inlined. */
- varray_type inlined_fns;
/* We use the same mechanism to build clones that we do to perform
inlining. However, there are a few places where we need to
distinguish between those two situations. This flag is true if
/* Prototypes. */
-/* The approximate number of instructions per statement. This number
- need not be particularly accurate; it is used only to make
- decisions about when a function is too big to inline. */
-#define INSNS_PER_STMT (10)
-
static tree copy_body_r (tree *, int *, void *);
static tree copy_body (inline_data *);
static tree expand_call_inline (tree *, int *, void *);
/* Make a copy of the variable or label. */
tree t = copy_decl_for_inlining (decl, fn, VARRAY_TREE (id->fns, 0));
+ /* Remember it, so that if we encounter this local entity again
+ we can reuse this copy. Do this early because remap_type may
+ need this decl for TYPE_STUB_DECL. */
+ insert_decl_map (id, decl, t);
+
/* Remap types, if necessary. */
TREE_TYPE (t) = remap_type (TREE_TYPE (t), id);
if (TREE_CODE (t) == TYPE_DECL)
}
#endif
- /* Remember it, so that if we encounter this local entity
- again we can reuse this copy. */
- insert_decl_map (id, decl, t);
return t;
}
TYPE_NEXT_VARIANT (new) = NULL;
}
+ if (TYPE_STUB_DECL (type))
+ TYPE_STUB_DECL (new) = remap_decl (TYPE_STUB_DECL (type), id);
+
/* Lazily create pointer and reference types. */
TYPE_POINTER_TO (new) = NULL;
TYPE_REFERENCE_TO (new) = NULL;
walk_tree (&TYPE_FIELDS (new), copy_body_r, id, NULL);
break;
- case FILE_TYPE:
- case SET_TYPE:
case OFFSET_TYPE:
default:
/* Shouldn't have been thought variable sized. */
/* Replace this variable with the copy. */
STRIP_TYPE_NOPS (new_decl);
*tp = new_decl;
+ *walk_subtrees = 0;
}
else if (TREE_CODE (*tp) == STATEMENT_LIST)
copy_statement_list (tp);
else if (TYPE_P (*tp))
*tp = remap_type (*tp, id);
+ /* If this is a constant, we have to copy the node iff the type will be
+ remapped. copy_tree_r will not copy a constant. */
+ else if (CONSTANT_CLASS_P (*tp))
+ {
+ tree new_type = remap_type (TREE_TYPE (*tp), id);
+
+ if (new_type == TREE_TYPE (*tp))
+ *walk_subtrees = 0;
+
+ else if (TREE_CODE (*tp) == INTEGER_CST)
+ *tp = build_int_cst_wide (new_type, TREE_INT_CST_LOW (*tp),
+ TREE_INT_CST_HIGH (*tp));
+ else
+ {
+ *tp = copy_node (*tp);
+ TREE_TYPE (*tp) = new_type;
+ }
+ }
+
/* Otherwise, just copy the node. Note that copy_tree_r already
knows not to copy VAR_DECLs, etc., so this is safe. */
else
return body;
}
+/* Return true if VALUE is an ADDR_EXPR of an automatic variable
+ defined in function FN, or of a data member thereof. */
+
+static bool
+self_inlining_addr_expr (tree value, tree fn)
+{
+ tree var;
+
+ if (TREE_CODE (value) != ADDR_EXPR)
+ return false;
+
+ var = get_base_address (TREE_OPERAND (value, 0));
+
+ return var && lang_hooks.tree_inlining.auto_var_in_fn_p (var, fn);
+}
+
static void
setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
tree *init_stmts, tree *vars, bool *gimplify_init_stmts_p)
It is not big deal to prohibit constant propagation here as
we will constant propagate in DOM1 pass anyway. */
if (is_gimple_min_invariant (value)
- && lang_hooks.types_compatible_p (TREE_TYPE (value), TREE_TYPE (p)))
+ && lang_hooks.types_compatible_p (TREE_TYPE (value), TREE_TYPE (p))
+ /* We have to be very careful about ADDR_EXPR. Make sure
+ the base variable isn't a local variable of the inlined
+ function, e.g., when doing recursive inlining, direct or
+ mutually-recursive or whatever, which is why we don't
+ just test whether fn == current_function_decl. */
+ && ! self_inlining_addr_expr (value, fn))
{
insert_decl_map (id, p, value);
return;
/* Initialize the static chain. */
p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl;
+ if (fn == current_function_decl)
+ p = DECL_STRUCT_FUNCTION (fn)->saved_static_chain_decl;
if (p)
{
/* No static chain? Seems like a bug in tree-nested.c. */
}
if (gimplify_init_stmts_p)
- gimplify_body (&init_stmts, current_function_decl);
+ gimplify_body (&init_stmts, current_function_decl, false);
declare_inline_vars (bind_expr, vars);
return init_stmts;
"it uses non-local goto");
return node;
+ case BUILT_IN_RETURN:
+ case BUILT_IN_APPLY_ARGS:
+ /* If a __builtin_apply_args caller would be inlined,
+ it would be saving arguments of the function it has
+ been inlined into. Similarly __builtin_return would
+ return from the function the inline has been inlined into. */
+ inline_forbidden_reason
+ = N_("%Jfunction %qF can never be inlined because "
+ "it uses __builtin_return or __builtin_apply_args");
+ return node;
+
default:
break;
}
UNION_TYPE nodes, then it goes into infinite recursion on a
structure containing a pointer to its own type. If it doesn't,
then the type node for S doesn't get adjusted properly when
- F is inlined, and we abort in find_function_data. */
+ F is inlined.
+
+ ??? This is likely no longer true, but it's too late in the 4.0
+ cycle to try to find out. This should be checked for 4.1. */
for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t))
if (variably_modified_type_p (TREE_TYPE (t), NULL))
{
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
sorry (inline_forbidden_reason, fn, fn);
else if (do_warning)
- warning (inline_forbidden_reason, fn, fn);
+ warning (0, inline_forbidden_reason, fn, fn);
inlinable = false;
}
return inlinable;
}
+/* Estimate the cost of a memory move. Use machine dependent
+ word size and take possible memcpy call into account. */
+
+int
+estimate_move_cost (tree type)
+{
+ HOST_WIDE_INT size;
+
+ size = int_size_in_bytes (type);
+
+ if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO)
+ /* Cost of a memcpy call, 3 arguments and the call. */
+ return 4;
+ else
+ return ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES);
+}
+
/* Used by estimate_num_insns. Estimate number of instructions seen
by given statement. */
case COMPONENT_REF:
case BIT_FIELD_REF:
case INDIRECT_REF:
+ case ALIGN_INDIRECT_REF:
+ case MISALIGNED_INDIRECT_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
case OBJ_TYPE_REF:
case SAVE_EXPR:
case ADDR_EXPR:
case COMPLEX_EXPR:
+ case RANGE_EXPR:
case CASE_LABEL_EXPR:
case SSA_NAME:
case CATCH_EXPR:
*walk_subtrees = 0;
return NULL;
- /* Recognize assignments of large structures and constructors of
- big arrays. */
+ /* Try to estimate the cost of assignments. We have three cases to
+ deal with:
+ 1) Simple assignments to registers;
+ 2) Stores to things that must live in memory. This includes
+ "normal" stores to scalars, but also assignments of large
+ structures, or constructors of big arrays;
+ 3) TARGET_EXPRs.
+
+ Let us look at the first two cases, assuming we have "a = b + C":
+ <modify_expr <var_decl "a"> <plus_expr <var_decl "b"> <constant C>>
+ If "a" is a GIMPLE register, the assignment to it is free on almost
+ any target, because "a" usually ends up in a real register. Hence
+ the only cost of this expression comes from the PLUS_EXPR, and we
+ can ignore the MODIFY_EXPR.
+ If "a" is not a GIMPLE register, the assignment to "a" will most
+ likely be a real store, so the cost of the MODIFY_EXPR is the cost
+ of moving something into "a", which we compute using the function
+ estimate_move_cost.
+
+ The third case deals with TARGET_EXPRs, for which the semantics are
+ that a temporary is assigned, unless the TARGET_EXPR itself is being
+ assigned to something else. In the latter case we do not need the
+ temporary. E.g. in <modify_expr <var_decl "a"> <target_expr>>, the
+ MODIFY_EXPR is free. */
case INIT_EXPR:
case MODIFY_EXPR:
- x = TREE_OPERAND (x, 0);
- /* FALLTHRU */
+ /* Is the right and side a TARGET_EXPR? */
+ if (TREE_CODE (TREE_OPERAND (x, 1)) == TARGET_EXPR)
+ break;
+ /* ... fall through ... */
+
case TARGET_EXPR:
+ x = TREE_OPERAND (x, 0);
+ /* Is this an assignments to a register? */
+ if (is_gimple_reg (x))
+ break;
+ /* Otherwise it's a store, so fall through to compute the move cost. */
+
case CONSTRUCTOR:
- {
- HOST_WIDE_INT size;
-
- size = int_size_in_bytes (TREE_TYPE (x));
-
- if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO)
- *count += 10;
- else
- *count += ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES);
- }
+ *count += estimate_move_cost (TREE_TYPE (x));
break;
- /* Assign cost of 1 to usual operations.
- ??? We may consider mapping RTL costs to this. */
+ /* Assign cost of 1 to usual operations.
+ ??? We may consider mapping RTL costs to this. */
case COND_EXPR:
case PLUS_EXPR:
case ASM_EXPR:
+ case REALIGN_LOAD_EXPR:
+
case RESX_EXPR:
*count += 1;
break;
case CALL_EXPR:
{
tree decl = get_callee_fndecl (x);
+ tree arg;
- if (decl && DECL_BUILT_IN (decl))
+ if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
switch (DECL_FUNCTION_CODE (decl))
{
case BUILT_IN_CONSTANT_P:
default:
break;
}
- *count += 10;
+
+ /* Our cost must be kept in sync with cgraph_estimate_size_after_inlining
+ that does use function declaration to figure out the arguments. */
+ if (!decl)
+ {
+ for (arg = TREE_OPERAND (x, 1); arg; arg = TREE_CHAIN (arg))
+ *count += estimate_move_cost (TREE_TYPE (TREE_VALUE (arg)));
+ }
+ else
+ {
+ for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg))
+ *count += estimate_move_cost (TREE_TYPE (arg));
+ }
+
+ *count += PARAM_VALUE (PARAM_INLINE_CALL_COST);
break;
}
default:
- /* Abort here se we know we don't miss any nodes. */
gcc_unreachable ();
}
return NULL;
tree expr;
tree stmt;
tree use_retvar;
- tree decl;
tree fn;
tree arg_inits;
tree *inlined_body;
if (TREE_CODE (*tp) == TARGET_EXPR)
{
#if 0
- int i, len = first_rtl_op (TARGET_EXPR);
+ int i, len = TREE_CODE_LENGTH (TARGET_EXPR);
/* We're walking our own subtrees. */
*walk_subtrees = 0;
&& strlen (reason)
&& !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn)))
{
- warning ("%Jinlining failed in call to %qF: %s", fn, fn, reason);
- warning ("called from here");
+ warning (0, "%Jinlining failed in call to %qF: %s", fn, fn, reason);
+ warning (0, "called from here");
}
goto egress;
}
recursing into it. */
VARRAY_PUSH_TREE (id->fns, fn);
- /* Record the function we are about to inline if optimize_function
- has not been called on it yet and we don't have it in the list. */
- if (! DECL_INLINED_FNS (fn))
- {
- int i;
-
- for (i = VARRAY_ACTIVE_SIZE (id->inlined_fns) - 1; i >= 0; i--)
- if (VARRAY_TREE (id->inlined_fns, i) == fn)
- break;
- if (i < 0)
- VARRAY_PUSH_TREE (id->inlined_fns, fn);
- }
-
/* Return statements in the function body will be replaced by jumps
to the RET_LABEL. */
id->ret_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
DECL_ARTIFICIAL (id->ret_label) = 1;
+ DECL_IGNORED_P (id->ret_label) = 1;
DECL_CONTEXT (id->ret_label) = VARRAY_TREE (id->fns, 0);
insert_decl_map (id, id->ret_label, id->ret_label);
/* Find the lhs to which the result of this call is assigned. */
modify_dest = tsi_stmt (id->tsi);
if (TREE_CODE (modify_dest) == MODIFY_EXPR)
- modify_dest = TREE_OPERAND (modify_dest, 0);
+ {
+ modify_dest = TREE_OPERAND (modify_dest, 0);
+
+ /* The function which we are inlining might not return a value,
+ in which case we should issue a warning that the function
+ does not return a value. In that case the optimizers will
+ see that the variable to which the value is assigned was not
+ initialized. We do not want to issue a warning about that
+ uninitialized variable. */
+ if (DECL_P (modify_dest))
+ TREE_NO_WARNING (modify_dest) = 1;
+ }
else
modify_dest = NULL;
/* Declare the return variable for the function. */
- decl = declare_return_variable (id, return_slot_addr,
- modify_dest, &use_retvar);
+ declare_return_variable (id, return_slot_addr,
+ modify_dest, &use_retvar);
/* After we've initialized the parameters, we insert the body of the
function itself. */
{
struct cgraph_node *old_node = id->current_node;
+ tree copy;
id->current_node = edge->callee;
- append_to_statement_list (copy_body (id), &BIND_EXPR_BODY (expr));
+ copy = copy_body (id);
+
+ /* If the function uses a return slot, then it may legitimately
+ fall through while still returning a value, so we have to skip
+ the warning here. */
+ if (warn_return_type
+ && !TREE_NO_WARNING (fn)
+ && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fn)))
+ && return_slot_addr == NULL_TREE
+ && block_may_fallthru (copy))
+ {
+ warning (0, "control may reach end of non-void function %qD being inlined",
+ fn);
+ TREE_NO_WARNING (fn) = 1;
+ }
+
+ append_to_statement_list (copy, &BIND_EXPR_BODY (expr));
id->current_node = old_node;
}
inlined_body = &BIND_EXPR_BODY (expr);
splay_tree_delete (id->decl_map);
id->decl_map = st;
- /* The new expression has side-effects if the old one did. */
- TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (t);
+ /* Although, from the semantic viewpoint, the new expression has
+ side-effects only if the old one did, it is not possible, from
+ the technical viewpoint, to evaluate the body of a function
+ multiple times without serious havoc. */
+ TREE_SIDE_EFFECTS (expr) = 1;
tsi_link_before (&id->tsi, expr, TSI_SAME_STMT);
The easiest solution is to simply recalculate TREE_SIDE_EFFECTS for
the toplevel expression. */
recalculate_side_effects (expr);
+
+ /* Output the inlining info for this abstract function, since it has been
+ inlined. If we don't do this now, we can lose the information about the
+ variables in the function when the blocks get blown away as soon as we
+ remove the cgraph node. */
+ (*debug_hooks->outlining_inline_function) (edge->callee->decl);
/* Update callgraph if needed. */
cgraph_remove_node (edge->callee);
{
inline_data id;
tree prev_fn;
- tree ifn;
/* There is no point in performing inlining if errors have already
occurred -- and we might crash if we try to inline invalid
prev_fn = lang_hooks.tree_inlining.add_pending_fn_decls (&id.fns, prev_fn);
- /* Create the list of functions this call will inline. */
- VARRAY_TREE_INIT (id.inlined_fns, 32, "inlined_fns");
-
/* Keep track of the low-water mark, i.e., the point where the first
real inlining is represented in ID.FNS. */
id.first_inlined_fn = VARRAY_ACTIVE_SIZE (id.fns);
/* Clean up. */
htab_delete (id.tree_pruner);
- ifn = make_tree_vec (VARRAY_ACTIVE_SIZE (id.inlined_fns));
- if (VARRAY_ACTIVE_SIZE (id.inlined_fns))
- memcpy (&TREE_VEC_ELT (ifn, 0), &VARRAY_TREE (id.inlined_fns, 0),
- VARRAY_ACTIVE_SIZE (id.inlined_fns) * sizeof (tree));
- DECL_INLINED_FNS (fn) = ifn;
#ifdef ENABLE_CHECKING
{
return body;
}
-#define WALK_SUBTREE(NODE) \
- do \
- { \
- result = walk_tree (&(NODE), func, data, pset); \
- if (result) \
- return result; \
- } \
- while (0)
-
-/* This is a subroutine of walk_tree that walks field of TYPE that are to
- be walked whenever a type is seen in the tree. Rest of operands and return
- value are as for walk_tree. */
-
-static tree
-walk_type_fields (tree type, walk_tree_fn func, void *data,
- struct pointer_set_t *pset)
-{
- tree result = NULL_TREE;
-
- switch (TREE_CODE (type))
- {
- case POINTER_TYPE:
- case REFERENCE_TYPE:
- /* We have to worry about mutually recursive pointers. These can't
- be written in C. They can in Ada. It's pathological, but
- there's an ACATS test (c38102a) that checks it. Deal with this
- by checking if we're pointing to another pointer, that one
- points to another pointer, that one does too, and we have no htab.
- If so, get a hash table. We check three levels deep to avoid
- the cost of the hash table if we don't need one. */
- if (POINTER_TYPE_P (TREE_TYPE (type))
- && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (type)))
- && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (TREE_TYPE (type))))
- && !pset)
- {
- result = walk_tree_without_duplicates (&TREE_TYPE (type),
- func, data);
- if (result)
- return result;
-
- break;
- }
-
- /* ... fall through ... */
-
- case COMPLEX_TYPE:
- WALK_SUBTREE (TREE_TYPE (type));
- break;
-
- case METHOD_TYPE:
- WALK_SUBTREE (TYPE_METHOD_BASETYPE (type));
-
- /* Fall through. */
-
- case FUNCTION_TYPE:
- WALK_SUBTREE (TREE_TYPE (type));
- {
- tree arg;
-
- /* We never want to walk into default arguments. */
- for (arg = TYPE_ARG_TYPES (type); arg; arg = TREE_CHAIN (arg))
- WALK_SUBTREE (TREE_VALUE (arg));
- }
- break;
-
- case ARRAY_TYPE:
- /* Don't follow this nodes's type if a pointer for fear that we'll
- have infinite recursion. Those types are uninteresting anyway. */
- if (!POINTER_TYPE_P (TREE_TYPE (type))
- && TREE_CODE (TREE_TYPE (type)) != OFFSET_TYPE)
- WALK_SUBTREE (TREE_TYPE (type));
- WALK_SUBTREE (TYPE_DOMAIN (type));
- break;
-
- case BOOLEAN_TYPE:
- case ENUMERAL_TYPE:
- case INTEGER_TYPE:
- case CHAR_TYPE:
- case REAL_TYPE:
- WALK_SUBTREE (TYPE_MIN_VALUE (type));
- WALK_SUBTREE (TYPE_MAX_VALUE (type));
- break;
-
- case OFFSET_TYPE:
- WALK_SUBTREE (TREE_TYPE (type));
- WALK_SUBTREE (TYPE_OFFSET_BASETYPE (type));
- break;
-
- default:
- break;
- }
-
- return NULL_TREE;
-}
-
-/* Apply FUNC to all the sub-trees of TP in a pre-order traversal. FUNC is
- called with the DATA and the address of each sub-tree. If FUNC returns a
- non-NULL value, the traversal is aborted, and the value returned by FUNC
- is returned. If PSET is non-NULL it is used to record the nodes visited,
- and to avoid visiting a node more than once. */
-
-tree
-walk_tree (tree *tp, walk_tree_fn func, void *data, struct pointer_set_t *pset)
-{
- enum tree_code code;
- int walk_subtrees;
- tree result;
-
-#define WALK_SUBTREE_TAIL(NODE) \
- do \
- { \
- tp = & (NODE); \
- goto tail_recurse; \
- } \
- while (0)
-
- tail_recurse:
- /* Skip empty subtrees. */
- if (!*tp)
- return NULL_TREE;
-
- /* Don't walk the same tree twice, if the user has requested
- that we avoid doing so. */
- if (pset && pointer_set_insert (pset, *tp))
- return NULL_TREE;
-
- /* Call the function. */
- walk_subtrees = 1;
- result = (*func) (tp, &walk_subtrees, data);
-
- /* If we found something, return it. */
- if (result)
- return result;
-
- code = TREE_CODE (*tp);
-
- /* Even if we didn't, FUNC may have decided that there was nothing
- interesting below this point in the tree. */
- if (!walk_subtrees)
- {
- if (code == TREE_LIST)
- /* But we still need to check our siblings. */
- WALK_SUBTREE_TAIL (TREE_CHAIN (*tp));
- else
- return NULL_TREE;
- }
-
- result = lang_hooks.tree_inlining.walk_subtrees (tp, &walk_subtrees, func,
- data, pset);
- if (result || ! walk_subtrees)
- return result;
-
- /* If this is a DECL_EXPR, walk into various fields of the type that it's
- defining. We only want to walk into these fields of a type in this
- case. Note that decls get walked as part of the processing of a
- BIND_EXPR.
-
- ??? Precisely which fields of types that we are supposed to walk in
- this case vs. the normal case aren't well defined. */
- if (code == DECL_EXPR
- && TREE_CODE (DECL_EXPR_DECL (*tp)) == TYPE_DECL
- && TREE_CODE (TREE_TYPE (DECL_EXPR_DECL (*tp))) != ERROR_MARK)
- {
- tree *type_p = &TREE_TYPE (DECL_EXPR_DECL (*tp));
-
- /* Call the function for the type. See if it returns anything or
- doesn't want us to continue. If we are to continue, walk both
- the normal fields and those for the declaration case. */
- result = (*func) (type_p, &walk_subtrees, data);
- if (result || !walk_subtrees)
- return NULL_TREE;
-
- result = walk_type_fields (*type_p, func, data, pset);
- if (result)
- return result;
-
- WALK_SUBTREE (TYPE_SIZE (*type_p));
- WALK_SUBTREE (TYPE_SIZE_UNIT (*type_p));
-
- /* If this is a record type, also walk the fields. */
- if (TREE_CODE (*type_p) == RECORD_TYPE
- || TREE_CODE (*type_p) == UNION_TYPE
- || TREE_CODE (*type_p) == QUAL_UNION_TYPE)
- {
- tree field;
-
- for (field = TYPE_FIELDS (*type_p); field;
- field = TREE_CHAIN (field))
- {
- /* We'd like to look at the type of the field, but we can easily
- get infinite recursion. So assume it's pointed to elsewhere
- in the tree. Also, ignore things that aren't fields. */
- if (TREE_CODE (field) != FIELD_DECL)
- continue;
-
- WALK_SUBTREE (DECL_FIELD_OFFSET (field));
- WALK_SUBTREE (DECL_SIZE (field));
- WALK_SUBTREE (DECL_SIZE_UNIT (field));
- if (TREE_CODE (*type_p) == QUAL_UNION_TYPE)
- WALK_SUBTREE (DECL_QUALIFIER (field));
- }
- }
- }
-
- else if (code != SAVE_EXPR
- && code != BIND_EXPR
- && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)))
- {
- int i, len;
-
- /* Walk over all the sub-trees of this operand. */
- len = first_rtl_op (code);
- /* TARGET_EXPRs are peculiar: operands 1 and 3 can be the same.
- But, we only want to walk once. */
- if (code == TARGET_EXPR
- && TREE_OPERAND (*tp, 3) == TREE_OPERAND (*tp, 1))
- --len;
-
- /* Go through the subtrees. We need to do this in forward order so
- that the scope of a FOR_EXPR is handled properly. */
-#ifdef DEBUG_WALK_TREE
- for (i = 0; i < len; ++i)
- WALK_SUBTREE (TREE_OPERAND (*tp, i));
-#else
- for (i = 0; i < len - 1; ++i)
- WALK_SUBTREE (TREE_OPERAND (*tp, i));
-
- if (len)
- {
- /* The common case is that we may tail recurse here. */
- if (code != BIND_EXPR
- && !TREE_CHAIN (*tp))
- WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, len - 1));
- else
- WALK_SUBTREE (TREE_OPERAND (*tp, len - 1));
- }
-#endif
- }
-
- /* If this is a type, walk the needed fields in the type. */
- else if (TYPE_P (*tp))
- {
- result = walk_type_fields (*tp, func, data, pset);
- if (result)
- return result;
- }
- else
- {
- /* Not one of the easy cases. We must explicitly go through the
- children. */
- switch (code)
- {
- case ERROR_MARK:
- case IDENTIFIER_NODE:
- case INTEGER_CST:
- case REAL_CST:
- case VECTOR_CST:
- case STRING_CST:
- case BLOCK:
- case PLACEHOLDER_EXPR:
- case SSA_NAME:
- case FIELD_DECL:
- case RESULT_DECL:
- /* None of thse have subtrees other than those already walked
- above. */
- break;
-
- case TREE_LIST:
- WALK_SUBTREE (TREE_VALUE (*tp));
- WALK_SUBTREE_TAIL (TREE_CHAIN (*tp));
- break;
-
- case TREE_VEC:
- {
- int len = TREE_VEC_LENGTH (*tp);
-
- if (len == 0)
- break;
-
- /* Walk all elements but the first. */
- while (--len)
- WALK_SUBTREE (TREE_VEC_ELT (*tp, len));
-
- /* Now walk the first one as a tail call. */
- WALK_SUBTREE_TAIL (TREE_VEC_ELT (*tp, 0));
- }
-
- case COMPLEX_CST:
- WALK_SUBTREE (TREE_REALPART (*tp));
- WALK_SUBTREE_TAIL (TREE_IMAGPART (*tp));
-
- case CONSTRUCTOR:
- WALK_SUBTREE_TAIL (CONSTRUCTOR_ELTS (*tp));
-
- case SAVE_EXPR:
- WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0));
-
- case BIND_EXPR:
- {
- tree decl;
- for (decl = BIND_EXPR_VARS (*tp); decl; decl = TREE_CHAIN (decl))
- {
- /* Walk the DECL_INITIAL and DECL_SIZE. We don't want to walk
- into declarations that are just mentioned, rather than
- declared; they don't really belong to this part of the tree.
- And, we can see cycles: the initializer for a declaration
- can refer to the declaration itself. */
- WALK_SUBTREE (DECL_INITIAL (decl));
- WALK_SUBTREE (DECL_SIZE (decl));
- WALK_SUBTREE (DECL_SIZE_UNIT (decl));
- }
- WALK_SUBTREE_TAIL (BIND_EXPR_BODY (*tp));
- }
-
- case STATEMENT_LIST:
- {
- tree_stmt_iterator i;
- for (i = tsi_start (*tp); !tsi_end_p (i); tsi_next (&i))
- WALK_SUBTREE (*tsi_stmt_ptr (i));
- }
- break;
-
- default:
- /* ??? This could be a language-defined node. We really should make
- a hook for it, but right now just ignore it. */
- break;
- }
- }
-
- /* We didn't find what we were looking for. */
- return NULL_TREE;
-
-#undef WALK_SUBTREE
-#undef WALK_SUBTREE_TAIL
-}
-
-/* Like walk_tree, but does not walk duplicate nodes more than once. */
-
-tree
-walk_tree_without_duplicates (tree *tp, walk_tree_fn func, void *data)
-{
- tree result;
- struct pointer_set_t *pset;
-
- pset = pointer_set_create ();
- result = walk_tree (tp, func, data, pset);
- pointer_set_destroy (pset);
- return result;
-}
-
/* Passed to walk_tree. Copies the node pointed to, if appropriate. */
tree