Interprocedural constant propagation.

From-SVN: r102626
This commit is contained in:
Razya Ladelsky 2005-08-01 07:47:25 +00:00
parent 57fb53415e
commit 19734dd84e
4 changed files with 356 additions and 20 deletions

View File

@ -42,6 +42,7 @@ DEFTIMEVAR (TV_DUMP , "dump files")
DEFTIMEVAR (TV_CGRAPH , "callgraph construction")
DEFTIMEVAR (TV_CGRAPHOPT , "callgraph optimization")
DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp")
DEFTIMEVAR (TV_IPA_REFERENCE , "ipa reference")
DEFTIMEVAR (TV_IPA_PURE_CONST , "ipa pure const")
DEFTIMEVAR (TV_IPA_TYPE_ESCAPE , "ipa type escape")

View File

@ -49,7 +49,7 @@ Boston, MA 02110-1301, USA. */
#include "except.h"
#include "debug.h"
#include "pointer-set.h"
#include "integrate.h"
#include "ipa-prop.h"
/* I'm not real happy about this, but we need to handle gimple and
non-gimple trees. */
@ -127,12 +127,15 @@ typedef struct inline_data
bool cloning_p;
/* Similarly for saving function body. */
bool saving_p;
/* Versioning function is slightly different from inlining. */
bool versioning_p;
/* Callgraph node of function we are inlining into. */
struct cgraph_node *node;
/* Callgraph node of currently inlined function. */
struct cgraph_node *current_node;
/* Current BLOCK. */
tree block;
varray_type ipa_info;
/* Exception region the inlined call lie in. */
int eh_region;
/* Take region number in the function being copied, add this value and
@ -157,8 +160,8 @@ static void unsave_expr_1 (tree);
static tree unsave_r (tree *, int *, void *);
static void declare_inline_vars (tree, tree);
static void remap_save_expr (tree *, void *, int *);
static inline bool inlining_p (inline_data *id);
static bool replace_ref_tree (inline_data *, tree *);
static inline bool inlining_p (inline_data *);
static void add_lexical_block (tree current_block, tree new_block);
/* Insert a tree->tree mapping for ID. Despite the name suggests
@ -198,8 +201,8 @@ remap_decl (tree decl, inline_data *id)
{
/* Make a copy of the variable or label. */
tree t;
t = copy_decl_for_inlining (decl, fn, id->caller);
t = copy_decl_for_dup (decl, fn, id->caller, id->versioning_p);
/* 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. */
@ -607,7 +610,8 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
}
}
}
else if (TREE_CODE (*tp) == INDIRECT_REF)
else if (TREE_CODE (*tp) == INDIRECT_REF
&& !id->versioning_p)
{
/* Get rid of *& from inline substitutions that can happen when a
pointer argument is an ADDR_EXPR. */
@ -639,8 +643,8 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data)
/* Here is the "usual case". Copy this tree node, and then
tweak some special cases. */
copy_tree_r (tp, walk_subtrees, NULL);
copy_tree_r (tp, walk_subtrees, id->versioning_p ? data : NULL);
/* If EXPR has block defined, map it to newly constructed block.
When inlining we want EXPRs without block appear in the block
of function call. */
@ -749,10 +753,22 @@ copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale)
/* We're cloning or inlining this body; duplicate the
associate callgraph nodes. */
edge = cgraph_edge (id->current_node, orig_stmt);
if (!id->versioning_p)
{
edge = cgraph_edge (id->current_node, orig_stmt);
if (edge)
cgraph_clone_edge (edge, id->node, stmt,
REG_BR_PROB_BASE, 1, true);
}
}
if (id->versioning_p)
{
/* Update the call_expr on the edges from the new version
to its callees. */
struct cgraph_edge *edge;
edge = cgraph_edge (id->node, orig_stmt);
if (edge)
cgraph_clone_edge (edge, id->node, stmt,
REG_BR_PROB_BASE, 1, true);
edge->call_stmt = stmt;
}
}
/* If you think we can abort here, you are wrong.
@ -921,7 +937,7 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency,
and label_to_block_maps. Otherwise, we're duplicating a function
body for inlining; insert our new blocks and labels into the
existing varrays. */
saving_or_cloning = (id->saving_p || id->cloning_p);
saving_or_cloning = (id->saving_p || id->cloning_p || id->versioning_p);
if (saving_or_cloning)
{
new_cfun =
@ -1061,7 +1077,7 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn,
/* Make an equivalent VAR_DECL. Note that we must NOT remap the type
here since the type of this decl must be visible to the calling
function. */
var = copy_decl_for_inlining (p, fn, id->caller);
var = copy_decl_for_dup (p, fn, id->caller, /*versioning=*/false);
/* See if the frontend wants to pass this by invisible reference. If
so, our new VAR_DECL will have REFERENCE_TYPE, and we need to
@ -1259,7 +1275,7 @@ declare_return_variable (inline_data *id, tree return_slot_addr,
gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) == INTEGER_CST);
var = copy_decl_for_inlining (result, callee, caller);
var = copy_decl_for_dup (result, callee, caller, /*versioning=*/false);
DECL_SEEN_IN_BIND_EXPR_P (var) = 1;
DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list
@ -2365,6 +2381,7 @@ tree
copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
{
enum tree_code code = TREE_CODE (*tp);
inline_data *id = (inline_data *) data;
/* We make copies of most nodes. */
if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))
@ -2377,6 +2394,11 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
tree chain = TREE_CHAIN (*tp);
tree new;
if (id && id->versioning_p && replace_ref_tree (id, tp))
{
*walk_subtrees = 0;
return NULL_TREE;
}
/* Copy the node. */
new = copy_node (*tp);
@ -2479,8 +2501,8 @@ mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
/* Copy the decl and remember the copy. */
insert_decl_map (id, decl,
copy_decl_for_inlining (decl, DECL_CONTEXT (decl),
DECL_CONTEXT (decl)));
copy_decl_for_dup (decl, DECL_CONTEXT (decl),
DECL_CONTEXT (decl), /*versioning=*/false));
}
return NULL_TREE;
@ -2614,9 +2636,314 @@ declare_inline_vars (tree block, tree vars)
BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars);
}
/* Returns true if we're inlining. */
static inline bool
inlining_p (inline_data *id)
/* Copy NODE (which must be a DECL). The DECL originally was in the FROM_FN,
but now it will be in the TO_FN. VERSIONING means that this function
is used by the versioning utility (not inlining or cloning). */
tree
copy_decl_for_dup (tree decl, tree from_fn, tree to_fn, bool versioning)
{
return (!id->saving_p && !id->cloning_p);
tree copy;
gcc_assert (DECL_P (decl));
/* Copy the declaration. */
if (!versioning
&& (TREE_CODE (decl) == PARM_DECL
|| TREE_CODE (decl) == RESULT_DECL))
{
tree type = TREE_TYPE (decl);
/* For a parameter or result, we must make an equivalent VAR_DECL,
not a new PARM_DECL. */
copy = build_decl (VAR_DECL, DECL_NAME (decl), type);
TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
TREE_READONLY (copy) = TREE_READONLY (decl);
TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
DECL_COMPLEX_GIMPLE_REG_P (copy) = DECL_COMPLEX_GIMPLE_REG_P (decl);
}
else
{
copy = copy_node (decl);
/* The COPY is not abstract; it will be generated in TO_FN. */
DECL_ABSTRACT (copy) = 0;
lang_hooks.dup_lang_specific_decl (copy);
/* TREE_ADDRESSABLE isn't used to indicate that a label's
address has been taken; it's for internal bookkeeping in
expand_goto_internal. */
if (TREE_CODE (copy) == LABEL_DECL)
{
TREE_ADDRESSABLE (copy) = 0;
LABEL_DECL_UID (copy) = -1;
}
}
/* Don't generate debug information for the copy if we wouldn't have
generated it for the copy either. */
DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl);
DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl);
/* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what
declaration inspired this copy. */
DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);
/* The new variable/label has no RTL, yet. */
if (!TREE_STATIC (copy) && !DECL_EXTERNAL (copy))
SET_DECL_RTL (copy, NULL_RTX);
/* These args would always appear unused, if not for this. */
TREE_USED (copy) = 1;
/* Set the context for the new declaration. */
if (!DECL_CONTEXT (decl))
/* Globals stay global. */
;
else if (DECL_CONTEXT (decl) != from_fn)
/* Things that weren't in the scope of the function we're inlining
from aren't in the scope we're inlining to, either. */
;
else if (TREE_STATIC (decl))
/* Function-scoped static variables should stay in the original
function. */
;
else
/* Ordinary automatic local variables are now in the scope of the
new function. */
DECL_CONTEXT (copy) = to_fn;
return copy;
}
/* Return a copy of the function's argument tree. */
static tree
copy_arguments_for_versioning (tree orig_parm, inline_data * id)
{
tree *arg_copy, *parg;
arg_copy = &orig_parm;
for (parg = arg_copy; *parg; parg = &TREE_CHAIN (*parg))
{
tree new = remap_decl (*parg, id);
lang_hooks.dup_lang_specific_decl (new);
TREE_CHAIN (new) = TREE_CHAIN (*parg);
*parg = new;
}
return orig_parm;
}
/* Return a copy of the function's static chain. */
static tree
copy_static_chain (tree static_chain, inline_data * id)
{
tree *chain_copy, *pvar;
chain_copy = &static_chain;
for (pvar = chain_copy; *pvar; pvar = &TREE_CHAIN (*pvar))
{
tree new = remap_decl (*pvar, id);
lang_hooks.dup_lang_specific_decl (new);
TREE_CHAIN (new) = TREE_CHAIN (*pvar);
*pvar = new;
}
return static_chain;
}
/* Return true if the function is allowed to be versioned.
This is a guard for the versioning functionality. */
bool
tree_versionable_function_p (tree fndecl)
{
if (fndecl == NULL_TREE)
return false;
/* ??? There are cases where a function is
uninlinable but can be versioned. */
if (!tree_inlinable_function_p (fndecl))
return false;
return true;
}
/* Create a copy of a function's tree.
OLD_DECL and NEW_DECL are FUNCTION_DECL tree nodes
of the original function and the new copied function
respectively. In case we want to replace a DECL
tree with another tree while duplicating the function's
body, TREE_MAP represents the mapping between these
trees. */
void
tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map)
{
struct cgraph_node *old_version_node;
struct cgraph_node *new_version_node;
inline_data id;
tree p, new_fndecl;
unsigned i;
struct ipa_replace_map *replace_info;
basic_block old_entry_block;
tree t_step;
gcc_assert (TREE_CODE (old_decl) == FUNCTION_DECL
&& TREE_CODE (new_decl) == FUNCTION_DECL);
DECL_POSSIBLY_INLINED (old_decl) = 1;
old_version_node = cgraph_node (old_decl);
new_version_node = cgraph_node (new_decl);
allocate_struct_function (new_decl);
/* Cfun points to the new allocated function struct at this point. */
cfun->function_end_locus = DECL_SOURCE_LOCATION (new_decl);
DECL_ARTIFICIAL (new_decl) = 1;
DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl);
/* Generate a new name for the new version. */
DECL_NAME (new_decl) =
create_tmp_var_name (NULL);
/* Create a new SYMBOL_REF rtx for the new name. */
if (DECL_RTL (old_decl) != NULL)
{
SET_DECL_RTL (new_decl, copy_rtx (DECL_RTL (old_decl)));
XEXP (DECL_RTL (new_decl), 0) =
gen_rtx_SYMBOL_REF (GET_MODE (XEXP (DECL_RTL (old_decl), 0)),
IDENTIFIER_POINTER (DECL_NAME (new_decl)));
}
/* Prepare the data structures for the tree copy. */
memset (&id, 0, sizeof (id));
/* The new version. */
id.node = new_version_node;
/* The old version. */
id.current_node = cgraph_node (old_decl);
id.versioning_p = true;
id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL);
id.caller = new_decl;
id.callee = old_decl;
id.callee_cfun = DECL_STRUCT_FUNCTION (old_decl);
current_function_decl = new_decl;
/* Copy the function's static chain. */
p = DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl;
if (p)
DECL_STRUCT_FUNCTION (new_decl)->static_chain_decl =
copy_static_chain (DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl,
&id);
/* Copy the function's arguments. */
if (DECL_ARGUMENTS (old_decl) != NULL_TREE)
DECL_ARGUMENTS (new_decl) =
copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id);
/* If there's a tree_map, prepare for substitution. */
if (tree_map)
for (i = 0; i < VARRAY_ACTIVE_SIZE (tree_map); i++)
{
replace_info = VARRAY_GENERIC_PTR (tree_map, i);
if (replace_info->replace_p && !replace_info->ref_p)
insert_decl_map (&id, replace_info->old_tree,
replace_info->new_tree);
else if (replace_info->replace_p && replace_info->ref_p)
id.ipa_info = tree_map;
}
DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.callee), &id);
/* Renumber the lexical scoping (non-code) blocks consecutively. */
number_blocks (id.caller);
if (DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list != NULL_TREE)
/* Add local vars. */
for (t_step = DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list;
t_step; t_step = TREE_CHAIN (t_step))
{
tree var = TREE_VALUE (t_step);
if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var))
cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
cfun->unexpanded_var_list);
else
cfun->unexpanded_var_list =
tree_cons (NULL_TREE, remap_decl (var, &id),
cfun->unexpanded_var_list);
}
/* Copy the Function's body. */
old_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION
(DECL_STRUCT_FUNCTION (old_decl));
new_fndecl = copy_body (&id,
old_entry_block->count,
old_entry_block->frequency, NULL, NULL);
DECL_SAVED_TREE (new_decl) = DECL_SAVED_TREE (new_fndecl);
DECL_STRUCT_FUNCTION (new_decl)->cfg =
DECL_STRUCT_FUNCTION (new_fndecl)->cfg;
DECL_STRUCT_FUNCTION (new_decl)->eh = DECL_STRUCT_FUNCTION (new_fndecl)->eh;
DECL_STRUCT_FUNCTION (new_decl)->ib_boundaries_block =
DECL_STRUCT_FUNCTION (new_fndecl)->ib_boundaries_block;
DECL_STRUCT_FUNCTION (new_decl)->last_label_uid =
DECL_STRUCT_FUNCTION (new_fndecl)->last_label_uid;
if (DECL_RESULT (old_decl) != NULL_TREE)
{
tree *res_decl = &DECL_RESULT (old_decl);
DECL_RESULT (new_decl) = remap_decl (*res_decl, &id);
lang_hooks.dup_lang_specific_decl (DECL_RESULT (new_decl));
}
current_function_decl = NULL;
/* Renumber the lexical scoping (non-code) blocks consecutively. */
number_blocks (new_decl);
/* Clean up. */
splay_tree_delete (id.decl_map);
fold_cond_expr_cond ();
return;
}
/* Replace an INDIRECT_REF tree of a given DECL tree with a new
given tree.
ID->ipa_info keeps the old tree and the new tree.
TP points to the INDIRECT REF tree. Return true if
the trees were replaced. */
static bool
replace_ref_tree (inline_data * id, tree * tp)
{
bool replaced = false;
tree new;
if (id->ipa_info && VARRAY_ACTIVE_SIZE (id->ipa_info) > 0)
{
unsigned i;
for (i = 0; i < VARRAY_ACTIVE_SIZE (id->ipa_info); i++)
{
struct ipa_replace_map *replace_info;
replace_info = VARRAY_GENERIC_PTR (id->ipa_info, i);
if (replace_info->replace_p && replace_info->ref_p)
{
tree old_tree = replace_info->old_tree;
tree new_tree = replace_info->new_tree;
if (TREE_CODE (*tp) == INDIRECT_REF
&& TREE_OPERAND (*tp, 0) == old_tree)
{
new = copy_node (new_tree);
*tp = new;
replaced = true;
}
}
}
}
return replaced;
}
/* Return true if we are inlining. */
static inline bool
inlining_p (inline_data * id)
{
return (!id->saving_p && !id->cloning_p && !id->versioning_p);
}

View File

@ -22,6 +22,7 @@ Boston, MA 02110-1301, USA. */
#ifndef GCC_TREE_INLINE_H
#define GCC_TREE_INLINE_H
#include "varray.h"
/* Function prototypes. */
void optimize_inline_calls (tree);
@ -33,6 +34,12 @@ int estimate_move_cost (tree type);
void push_cfun (struct function *new_cfun);
void pop_cfun (void);
int estimate_num_insns (tree expr);
bool tree_versionable_function_p (tree);
void tree_function_versioning (tree, tree, varray_type);
/* Copy a declaration when one function is substituted inline into
another. It is used also for versioning. */
extern tree copy_decl_for_dup (tree, tree, tree, bool);
/* 0 if we should not perform inlining.
1 if we should expand functions calls inline at the tree level.

View File

@ -284,6 +284,7 @@ extern struct tree_opt_pass pass_rebuild_cgraph_edges;
extern struct tree_opt_pass pass_eliminate_useless_stores;
/* IPA Passes */
extern struct tree_opt_pass pass_ipa_cp;
extern struct tree_opt_pass pass_ipa_inline;
extern struct tree_opt_pass pass_early_ipa_inline;
extern struct tree_opt_pass pass_ipa_reference;