ad616de14c
* basic-block.h, c-common.c, c-cppbuiltin.c, c-lang.c, c-tree.h, cfgbuild.c, cgraph.c, cgraph.h, collect2.c, combine.c, config.gcc, coverage.h, cse.c, cselib.c, defaults.h, df.c, dwarf2asm.c, dwarf2out.c, explow.c, expr.c, flow.c, fold-const.c, gcse.c, ggc-page.c, gimple-low.c, gimplify.c, ifcvt.c, langhooks-def.h, lcm.c, optabs.h, output.h, postreload-gcse.c, postreload.c, recog.c, resource.c, rtl.def, rtlanal.c, sched-deps.c, sched-rgn.c, targhooks.h, toplev.c, tree-data-ref.c, tree-eh.c, tree-flow.h, tree-loop-linear.c, tree-mudflap.h, tree-nrv.c, tree-optimize.c, tree-outof-ssa.c, tree-pass.h, tree-scalar-evolution.c, tree-ssa-copy.c, tree-ssa-dce.c, tree-ssa-dse.c, tree-ssa-loop-ivopts.c, tree-ssa-operands.c, tree-ssa-pre.c, tree-ssa.c, tree-vectorizer.c, tree.def, unwind-dw2-fde-darwin.c, var-tracking.c: Update copyright. From-SVN: r93827
313 lines
9.5 KiB
C
313 lines
9.5 KiB
C
/* Const/copy propagation and SSA_NAME replacement support routines.
|
|
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, 59 Temple Place - Suite 330,
|
|
Boston, MA 02111-1307, USA. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "tm.h"
|
|
#include "tree.h"
|
|
#include "flags.h"
|
|
#include "rtl.h"
|
|
#include "tm_p.h"
|
|
#include "ggc.h"
|
|
#include "basic-block.h"
|
|
#include "output.h"
|
|
#include "errors.h"
|
|
#include "expr.h"
|
|
#include "function.h"
|
|
#include "diagnostic.h"
|
|
#include "timevar.h"
|
|
#include "tree-dump.h"
|
|
#include "tree-flow.h"
|
|
#include "tree-pass.h"
|
|
#include "langhooks.h"
|
|
|
|
/* This file provides a handful of interfaces for performing const/copy
|
|
propagation and simple expression replacement which keep variable
|
|
annotations up-to-date.
|
|
|
|
We require that for any copy operation where the RHS and LHS have
|
|
a non-null memory tag the memory tag be the same. It is OK
|
|
for one or both of the memory tags to be NULL.
|
|
|
|
We also require tracking if a variable is dereferenced in a load or
|
|
store operation.
|
|
|
|
We enforce these requirements by having all copy propagation and
|
|
replacements of one SSA_NAME with a different SSA_NAME to use the
|
|
APIs defined in this file. */
|
|
|
|
|
|
/* Return true if we may propagate ORIG into DEST, false otherwise. */
|
|
|
|
bool
|
|
may_propagate_copy (tree dest, tree orig)
|
|
{
|
|
tree type_d = TREE_TYPE (dest);
|
|
tree type_o = TREE_TYPE (orig);
|
|
|
|
/* Do not copy between types for which we *do* need a conversion. */
|
|
if (!tree_ssa_useless_type_conversion_1 (type_d, type_o))
|
|
return false;
|
|
|
|
/* FIXME. GIMPLE is allowing pointer assignments and comparisons of
|
|
pointers that have different alias sets. This means that these
|
|
pointers will have different memory tags associated to them.
|
|
|
|
If we allow copy propagation in these cases, statements de-referencing
|
|
the new pointer will now have a reference to a different memory tag
|
|
with potentially incorrect SSA information.
|
|
|
|
This was showing up in libjava/java/util/zip/ZipFile.java with code
|
|
like:
|
|
|
|
struct java.io.BufferedInputStream *T.660;
|
|
struct java.io.BufferedInputStream *T.647;
|
|
struct java.io.InputStream *is;
|
|
struct java.io.InputStream *is.662;
|
|
[ ... ]
|
|
T.660 = T.647;
|
|
is = T.660; <-- This ought to be type-casted
|
|
is.662 = is;
|
|
|
|
Also, f/name.c exposed a similar problem with a COND_EXPR predicate
|
|
that was causing DOM to generate and equivalence with two pointers of
|
|
alias-incompatible types:
|
|
|
|
struct _ffename_space *n;
|
|
struct _ffename *ns;
|
|
[ ... ]
|
|
if (n == ns)
|
|
goto lab;
|
|
...
|
|
lab:
|
|
return n;
|
|
|
|
I think that GIMPLE should emit the appropriate type-casts. For the
|
|
time being, blocking copy-propagation in these cases is the safe thing
|
|
to do. */
|
|
if (TREE_CODE (dest) == SSA_NAME && TREE_CODE (orig) == SSA_NAME
|
|
&& POINTER_TYPE_P (type_d) && POINTER_TYPE_P (type_o))
|
|
{
|
|
tree mt_dest = var_ann (SSA_NAME_VAR (dest))->type_mem_tag;
|
|
tree mt_orig = var_ann (SSA_NAME_VAR (orig))->type_mem_tag;
|
|
if (mt_dest && mt_orig && mt_dest != mt_orig)
|
|
return false;
|
|
else if (!lang_hooks.types_compatible_p (type_d, type_o))
|
|
return false;
|
|
else if (get_alias_set (TREE_TYPE (type_d)) !=
|
|
get_alias_set (TREE_TYPE (type_o)))
|
|
return false;
|
|
}
|
|
|
|
/* If the destination is a SSA_NAME for a virtual operand, then we have
|
|
some special cases to handle. */
|
|
if (TREE_CODE (dest) == SSA_NAME && !is_gimple_reg (dest))
|
|
{
|
|
/* If both operands are SSA_NAMEs referring to virtual operands, then
|
|
we can always propagate. */
|
|
if (TREE_CODE (orig) == SSA_NAME)
|
|
{
|
|
if (!is_gimple_reg (orig))
|
|
return true;
|
|
|
|
#ifdef ENABLE_CHECKING
|
|
/* If we have one real and one virtual operand, then something has
|
|
gone terribly wrong. */
|
|
gcc_assert (!is_gimple_reg (orig));
|
|
#endif
|
|
}
|
|
|
|
/* We have a "copy" from something like a constant into a virtual
|
|
operand. Reject these. */
|
|
return false;
|
|
}
|
|
|
|
/* If ORIG flows in from an abnormal edge, it cannot be propagated. */
|
|
if (TREE_CODE (orig) == SSA_NAME
|
|
&& SSA_NAME_OCCURS_IN_ABNORMAL_PHI (orig))
|
|
return false;
|
|
|
|
/* If DEST is an SSA_NAME that flows from an abnormal edge, then it
|
|
cannot be replaced. */
|
|
if (TREE_CODE (dest) == SSA_NAME
|
|
&& SSA_NAME_OCCURS_IN_ABNORMAL_PHI (dest))
|
|
return false;
|
|
|
|
/* Anything else is OK. */
|
|
return true;
|
|
}
|
|
|
|
/* Similarly, but we know that we're propagating into an ASM_EXPR. */
|
|
|
|
bool
|
|
may_propagate_copy_into_asm (tree dest)
|
|
{
|
|
/* Hard register operands of asms are special. Do not bypass. */
|
|
return !(TREE_CODE (dest) == SSA_NAME
|
|
&& TREE_CODE (SSA_NAME_VAR (dest)) == VAR_DECL
|
|
&& DECL_HARD_REGISTER (SSA_NAME_VAR (dest)));
|
|
}
|
|
|
|
|
|
/* Given two SSA_NAMEs pointers ORIG and NEW such that we are copy
|
|
propagating NEW into ORIG, consolidate aliasing information so that
|
|
they both share the same memory tags. */
|
|
|
|
static void
|
|
merge_alias_info (tree orig, tree new)
|
|
{
|
|
tree new_sym = SSA_NAME_VAR (new);
|
|
tree orig_sym = SSA_NAME_VAR (orig);
|
|
var_ann_t new_ann = var_ann (new_sym);
|
|
var_ann_t orig_ann = var_ann (orig_sym);
|
|
|
|
gcc_assert (POINTER_TYPE_P (TREE_TYPE (orig)));
|
|
gcc_assert (POINTER_TYPE_P (TREE_TYPE (new)));
|
|
|
|
#if defined ENABLE_CHECKING
|
|
gcc_assert (lang_hooks.types_compatible_p (TREE_TYPE (orig),
|
|
TREE_TYPE (new)));
|
|
|
|
/* If the pointed-to alias sets are different, these two pointers
|
|
would never have the same memory tag. In this case, NEW should
|
|
not have been propagated into ORIG. */
|
|
gcc_assert (get_alias_set (TREE_TYPE (TREE_TYPE (new_sym)))
|
|
== get_alias_set (TREE_TYPE (TREE_TYPE (orig_sym))));
|
|
#endif
|
|
|
|
/* Synchronize the type tags. If both pointers had a tag and they
|
|
are different, then something has gone wrong. */
|
|
if (new_ann->type_mem_tag == NULL_TREE)
|
|
new_ann->type_mem_tag = orig_ann->type_mem_tag;
|
|
else if (orig_ann->type_mem_tag == NULL_TREE)
|
|
orig_ann->type_mem_tag = new_ann->type_mem_tag;
|
|
else
|
|
gcc_assert (new_ann->type_mem_tag == orig_ann->type_mem_tag);
|
|
|
|
#if defined ENABLE_CHECKING
|
|
{
|
|
struct ptr_info_def *orig_ptr_info = SSA_NAME_PTR_INFO (orig);
|
|
struct ptr_info_def *new_ptr_info = SSA_NAME_PTR_INFO (new);
|
|
|
|
if (orig_ptr_info
|
|
&& new_ptr_info
|
|
&& orig_ptr_info->name_mem_tag
|
|
&& new_ptr_info->name_mem_tag
|
|
&& orig_ptr_info->pt_vars
|
|
&& new_ptr_info->pt_vars)
|
|
{
|
|
/* Note that pointer NEW may actually have a different set of
|
|
pointed-to variables. However, since NEW is being
|
|
copy-propagated into ORIG, it must always be true that the
|
|
pointed-to set for pointer NEW is the same, or a subset, of
|
|
the pointed-to set for pointer ORIG. If this isn't the case,
|
|
we shouldn't have been able to do the propagation of NEW into
|
|
ORIG. */
|
|
gcc_assert (bitmap_intersect_p (new_ptr_info->pt_vars,
|
|
orig_ptr_info->pt_vars));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Common code for propagate_value and replace_exp.
|
|
|
|
Replace use operand OP_P with VAL. FOR_PROPAGATION indicates if the
|
|
replacement is done to propagate a value or not. */
|
|
|
|
static void
|
|
replace_exp_1 (use_operand_p op_p, tree val,
|
|
bool for_propagation ATTRIBUTE_UNUSED)
|
|
{
|
|
tree op = USE_FROM_PTR (op_p);
|
|
|
|
#if defined ENABLE_CHECKING
|
|
gcc_assert (!(for_propagation
|
|
&& TREE_CODE (op) == SSA_NAME
|
|
&& TREE_CODE (val) == SSA_NAME
|
|
&& !may_propagate_copy (op, val)));
|
|
#endif
|
|
|
|
if (TREE_CODE (val) == SSA_NAME)
|
|
{
|
|
if (TREE_CODE (op) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (op)))
|
|
merge_alias_info (op, val);
|
|
SET_USE (op_p, val);
|
|
}
|
|
else
|
|
SET_USE (op_p, unsave_expr_now (val));
|
|
}
|
|
|
|
|
|
/* Propagate the value VAL (assumed to be a constant or another SSA_NAME)
|
|
into the operand pointed by OP_P.
|
|
|
|
Use this version for const/copy propagation as it will perform additional
|
|
checks to ensure validity of the const/copy propagation. */
|
|
|
|
void
|
|
propagate_value (use_operand_p op_p, tree val)
|
|
{
|
|
replace_exp_1 (op_p, val, true);
|
|
}
|
|
|
|
|
|
/* Propagate the value VAL (assumed to be a constant or another SSA_NAME)
|
|
into the tree pointed by OP_P.
|
|
|
|
Use this version for const/copy propagation when SSA operands are not
|
|
available. It will perform the additional checks to ensure validity of
|
|
the const/copy propagation, but will not update any operand information.
|
|
Be sure to mark the stmt as modified. */
|
|
|
|
void
|
|
propagate_tree_value (tree *op_p, tree val)
|
|
{
|
|
#if defined ENABLE_CHECKING
|
|
gcc_assert (!(TREE_CODE (val) == SSA_NAME
|
|
&& TREE_CODE (*op_p) == SSA_NAME
|
|
&& !may_propagate_copy (*op_p, val)));
|
|
#endif
|
|
|
|
if (TREE_CODE (val) == SSA_NAME)
|
|
{
|
|
if (TREE_CODE (*op_p) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (*op_p)))
|
|
merge_alias_info (*op_p, val);
|
|
*op_p = val;
|
|
}
|
|
else
|
|
*op_p = unsave_expr_now (val);
|
|
}
|
|
|
|
|
|
/* Replace *OP_P with value VAL (assumed to be a constant or another SSA_NAME).
|
|
|
|
Use this version when not const/copy propagating values. For example,
|
|
PRE uses this version when building expressions as they would appear
|
|
in specific blocks taking into account actions of PHI nodes. */
|
|
|
|
void
|
|
replace_exp (use_operand_p op_p, tree val)
|
|
{
|
|
replace_exp_1 (op_p, val, false);
|
|
}
|