/* Interprocedural analyses.
- Copyright (C) 2005-2015 Free Software Foundation, Inc.
+ Copyright (C) 2005-2017 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "alias.h"
#include "backend.h"
+#include "rtl.h"
#include "tree.h"
#include "gimple.h"
-#include "rtl.h"
+#include "alloc-pool.h"
+#include "tree-pass.h"
#include "ssa.h"
-#include "options.h"
+#include "tree-streamer.h"
+#include "cgraph.h"
+#include "diagnostic.h"
#include "fold-const.h"
-#include "internal-fn.h"
#include "gimple-fold.h"
#include "tree-eh.h"
-#include "flags.h"
-#include "insn-config.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
#include "calls.h"
-#include "emit-rtl.h"
-#include "varasm.h"
-#include "stmt.h"
-#include "expr.h"
#include "stor-layout.h"
#include "print-tree.h"
#include "gimplify.h"
#include "gimple-iterator.h"
#include "gimplify-me.h"
#include "gimple-walk.h"
-#include "langhooks.h"
-#include "target.h"
-#include "cgraph.h"
-#include "alloc-pool.h"
#include "symbol-summary.h"
#include "ipa-prop.h"
#include "tree-cfg.h"
-#include "tree-into-ssa.h"
#include "tree-dfa.h"
-#include "tree-pass.h"
#include "tree-inline.h"
#include "ipa-inline.h"
-#include "diagnostic.h"
#include "gimple-pretty-print.h"
-#include "lto-streamer.h"
-#include "data-streamer.h"
-#include "tree-streamer.h"
#include "params.h"
#include "ipa-utils.h"
#include "dbgcnt.h"
#include "domwalk.h"
#include "builtins.h"
-/* Intermediate information that we get from alias analysis about a particular
- parameter in a particular basic_block. When a parameter or the memory it
- references is marked modified, we use that information in all dominatd
- blocks without cosulting alias analysis oracle. */
-
-struct param_aa_status
-{
- /* Set when this structure contains meaningful information. If not, the
- structure describing a dominating BB should be used instead. */
- bool valid;
-
- /* Whether we have seen something which might have modified the data in
- question. PARM is for the parameter itself, REF is for data it points to
- but using the alias type of individual accesses and PT is the same thing
- but for computing aggregate pass-through functions using a very inclusive
- ao_ref. */
- bool parm_modified, ref_modified, pt_modified;
-};
-
-/* Information related to a given BB that used only when looking at function
- body. */
-
-struct ipa_bb_info
-{
- /* Call graph edges going out of this BB. */
- vec<cgraph_edge *> cg_edges;
- /* Alias analysis statuses of each formal parameter at this bb. */
- vec<param_aa_status> param_aa_statuses;
-};
-
-/* Structure with global information that is only used when looking at function
- body. */
-
-struct func_body_info
-{
- /* The node that is being analyzed. */
- cgraph_node *node;
-
- /* Its info. */
- struct ipa_node_params *info;
-
- /* Information about individual BBs. */
- vec<ipa_bb_info> bb_infos;
-
- /* Number of parameters. */
- int param_count;
-
- /* Number of statements already walked by when analyzing this function. */
- unsigned int aa_walked;
-};
-
/* Function summary where the parameter infos are actually stored. */
ipa_node_params_t *ipa_node_params_sum = NULL;
/* Vector of IPA-CP transformation data for each clone. */
/* Allocation pool for reference descriptions. */
-static pool_allocator<ipa_cst_ref_desc> ipa_refdesc_pool
- ("IPA-PROP ref descriptions", 32);
+static object_allocator<ipa_cst_ref_desc> ipa_refdesc_pool
+ ("IPA-PROP ref descriptions");
/* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
with NODE should prevent us from analyzing it for the purposes of IPA-CP. */
count = descriptors.length ();
for (i = 0; i < count; i++)
- if (descriptors[i].decl == ptree)
+ if (descriptors[i].decl_or_type == ptree)
return i;
return -1;
param_num = 0;
for (parm = fnargs; parm; parm = DECL_CHAIN (parm))
{
- descriptors[param_num].decl = parm;
+ descriptors[param_num].decl_or_type = parm;
descriptors[param_num].move_cost = estimate_move_cost (TREE_TYPE (parm),
true);
param_num++;
ipa_dump_param (FILE *file, struct ipa_node_params *info, int i)
{
fprintf (file, "param #%i", i);
- if (info->descriptors[i].decl)
+ if (info->descriptors[i].decl_or_type)
{
fprintf (file, " ");
- print_generic_expr (file, info->descriptors[i].decl, 0);
+ print_generic_expr (file, info->descriptors[i].decl_or_type, 0);
}
}
ctx->dump (dump_file);
}
- if (jump_func->alignment.known)
+ if (jump_func->bits.known)
{
- fprintf (f, " Alignment: %u, misalignment: %u\n",
- jump_func->alignment.align,
- jump_func->alignment.misalign);
+ fprintf (f, " value: "); print_hex (jump_func->bits.value, f);
+ fprintf (f, ", mask: "); print_hex (jump_func->bits.mask, f);
+ fprintf (f, "\n");
}
else
- fprintf (f, " Unknown alignment\n");
+ fprintf (f, " Unknown bits\n");
+
+ if (jump_func->vr_known)
+ {
+ fprintf (f, " VR ");
+ fprintf (f, "%s[",
+ (jump_func->m_vr.type == VR_ANTI_RANGE) ? "~" : "");
+ print_decs (jump_func->m_vr.min, f);
+ fprintf (f, ", ");
+ print_decs (jump_func->m_vr.max, f);
+ fprintf (f, "]\n");
+ }
+ else
+ fprintf (f, " Unknown VR\n");
}
}
ipa_set_jf_unknown (struct ipa_jump_func *jfunc)
{
jfunc->type = IPA_JF_UNKNOWN;
- jfunc->alignment.known = false;
+ jfunc->bits.known = false;
+ jfunc->vr_known = false;
}
/* Set JFUNC to be a copy of another jmp (to be used by jump function
ipa_set_jf_constant (struct ipa_jump_func *jfunc, tree constant,
struct cgraph_edge *cs)
{
- constant = unshare_expr (constant);
- if (constant && EXPR_P (constant))
- SET_EXPR_LOCATION (constant, UNKNOWN_LOCATION);
jfunc->type = IPA_JF_CONST;
jfunc->value.constant.value = unshare_expr_without_location (constant);
jfunc->value.pass_through.agg_preserved = agg_preserved;
}
+/* Set JFUNC to be an unary pass through jump function. */
+
+static void
+ipa_set_jf_unary_pass_through (struct ipa_jump_func *jfunc, int formal_id,
+ enum tree_code operation)
+{
+ jfunc->type = IPA_JF_PASS_THROUGH;
+ jfunc->value.pass_through.operand = NULL_TREE;
+ jfunc->value.pass_through.formal_id = formal_id;
+ jfunc->value.pass_through.operation = operation;
+ jfunc->value.pass_through.agg_preserved = false;
+}
/* Set JFUNC to be an arithmetic pass through jump function. */
static void
of this function body. */
static struct ipa_bb_info *
-ipa_get_bb_info (struct func_body_info *fbi, basic_block bb)
+ipa_get_bb_info (struct ipa_func_body_info *fbi, basic_block bb)
{
gcc_checking_assert (fbi);
return &fbi->bb_infos[bb->index];
*/
static bool
-stmt_may_be_vtbl_ptr_store (gimple stmt)
+stmt_may_be_vtbl_ptr_store (gimple *stmt)
{
if (is_gimple_call (stmt))
return false;
static bool
check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data)
{
- gimple stmt = SSA_NAME_DEF_STMT (vdef);
+ gimple *stmt = SSA_NAME_DEF_STMT (vdef);
struct prop_type_change_info *tci = (struct prop_type_change_info *) data;
if (stmt_may_be_vtbl_ptr_store (stmt))
type of the THIS pointer. */
static bool
-param_type_may_change_p (tree function, tree arg, gimple call)
+param_type_may_change_p (tree function, tree arg, gimple *call)
{
/* Pure functions can not do any changes on the dynamic type;
that require writting to memory. */
should really just start giving up. */
static bool
-aa_overwalked (struct func_body_info *fbi)
+aa_overwalked (struct ipa_func_body_info *fbi)
{
gcc_checking_assert (fbi);
return fbi->aa_walked > (unsigned) PARAM_VALUE (PARAM_IPA_MAX_AA_STEPS);
/* Find the nearest valid aa status for parameter specified by INDEX that
dominates BB. */
-static struct param_aa_status *
-find_dominating_aa_status (struct func_body_info *fbi, basic_block bb,
+static struct ipa_param_aa_status *
+find_dominating_aa_status (struct ipa_func_body_info *fbi, basic_block bb,
int index)
{
while (true)
structures and/or intialize the result with a dominating description as
necessary. */
-static struct param_aa_status *
-parm_bb_aa_status_for_bb (struct func_body_info *fbi, basic_block bb,
+static struct ipa_param_aa_status *
+parm_bb_aa_status_for_bb (struct ipa_func_body_info *fbi, basic_block bb,
int index)
{
gcc_checking_assert (fbi);
struct ipa_bb_info *bi = ipa_get_bb_info (fbi, bb);
if (bi->param_aa_statuses.is_empty ())
bi->param_aa_statuses.safe_grow_cleared (fbi->param_count);
- struct param_aa_status *paa = &bi->param_aa_statuses[index];
+ struct ipa_param_aa_status *paa = &bi->param_aa_statuses[index];
if (!paa->valid)
{
gcc_checking_assert (!paa->parm_modified
&& !paa->ref_modified
&& !paa->pt_modified);
- struct param_aa_status *dom_paa;
+ struct ipa_param_aa_status *dom_paa;
dom_paa = find_dominating_aa_status (fbi, bb, index);
if (dom_paa)
*paa = *dom_paa;
gathered but do not survive the summary building stage. */
static bool
-parm_preserved_before_stmt_p (struct func_body_info *fbi, int index,
- gimple stmt, tree parm_load)
+parm_preserved_before_stmt_p (struct ipa_func_body_info *fbi, int index,
+ gimple *stmt, tree parm_load)
{
- struct param_aa_status *paa;
+ struct ipa_param_aa_status *paa;
bool modified = false;
ao_ref refd;
+ tree base = get_base_address (parm_load);
+ gcc_assert (TREE_CODE (base) == PARM_DECL);
+ if (TREE_READONLY (base))
+ return true;
+
/* FIXME: FBI can be NULL if we are being called from outside
ipa_node_analysis or ipcp_transform_function, which currently happens
during inlining analysis. It would be great to extend fbi's lifetime and
return !modified;
}
-/* If STMT is an assignment that loads a value from an parameter declaration,
- return the index of the parameter in ipa_node_params which has not been
- modified. Otherwise return -1. */
+/* Main worker for load_from_unmodified_param and load_from_param.
+ If STMT is an assignment that loads a value from an parameter declaration,
+ return the index of the parameter in ipa_node_params. Otherwise return -1. */
static int
-load_from_unmodified_param (struct func_body_info *fbi,
- vec<ipa_param_descriptor> descriptors,
- gimple stmt)
+load_from_param_1 (struct ipa_func_body_info *fbi,
+ vec<ipa_param_descriptor> descriptors,
+ gimple *stmt)
{
int index;
tree op1;
- if (!gimple_assign_single_p (stmt))
- return -1;
-
+ gcc_checking_assert (is_gimple_assign (stmt));
op1 = gimple_assign_rhs1 (stmt);
if (TREE_CODE (op1) != PARM_DECL)
return -1;
return index;
}
+/* If STMT is an assignment that loads a value from an parameter declaration,
+ return the index of the parameter in ipa_node_params which has not been
+ modified. Otherwise return -1. */
+
+static int
+load_from_unmodified_param (struct ipa_func_body_info *fbi,
+ vec<ipa_param_descriptor> descriptors,
+ gimple *stmt)
+{
+ if (!gimple_assign_single_p (stmt))
+ return -1;
+
+ return load_from_param_1 (fbi, descriptors, stmt);
+}
+
+/* If STMT is an assignment that loads a value from an parameter declaration,
+ return the index of the parameter in ipa_node_params. Otherwise return -1. */
+
+static int
+load_from_param (struct ipa_func_body_info *fbi,
+ vec<ipa_param_descriptor> descriptors,
+ gimple *stmt)
+{
+ if (!is_gimple_assign (stmt))
+ return -1;
+
+ enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
+ if ((get_gimple_rhs_class (rhs_code) != GIMPLE_SINGLE_RHS)
+ && (get_gimple_rhs_class (rhs_code) != GIMPLE_UNARY_RHS))
+ return -1;
+
+ return load_from_param_1 (fbi, descriptors, stmt);
+}
+
/* Return true if memory reference REF (which must be a load through parameter
with INDEX) loads data that are known to be unmodified in this function
before reaching statement STMT. */
static bool
-parm_ref_data_preserved_p (struct func_body_info *fbi,
- int index, gimple stmt, tree ref)
+parm_ref_data_preserved_p (struct ipa_func_body_info *fbi,
+ int index, gimple *stmt, tree ref)
{
- struct param_aa_status *paa;
+ struct ipa_param_aa_status *paa;
bool modified = false;
ao_ref refd;
CALL into which it is passed. FBI describes the function body. */
static bool
-parm_ref_data_pass_through_p (struct func_body_info *fbi, int index,
- gimple call, tree parm)
+parm_ref_data_pass_through_p (struct ipa_func_body_info *fbi, int index,
+ gimple *call, tree parm)
{
bool modified = false;
ao_ref refd;
|| aa_overwalked (fbi))
return false;
- struct param_aa_status *paa = parm_bb_aa_status_for_bb (fbi, gimple_bb (call),
- index);
+ struct ipa_param_aa_status *paa = parm_bb_aa_status_for_bb (fbi,
+ gimple_bb (call),
+ index);
if (paa->pt_modified)
return false;
return !modified;
}
-/* Return true if we can prove that OP is a memory reference loading unmodified
- data from an aggregate passed as a parameter and if the aggregate is passed
- by reference, that the alias type of the load corresponds to the type of the
- formal parameter (so that we can rely on this type for TBAA in callers).
+/* Return true if we can prove that OP is a memory reference loading
+ data from an aggregate passed as a parameter.
+
+ The function works in two modes. If GUARANTEED_UNMODIFIED is NULL, it return
+ false if it cannot prove that the value has not been modified before the
+ load in STMT. If GUARANTEED_UNMODIFIED is not NULL, it will return true even
+ if it cannot prove the value has not been modified, in that case it will
+ store false to *GUARANTEED_UNMODIFIED, otherwise it will store true there.
+
INFO and PARMS_AINFO describe parameters of the current function (but the
latter can be NULL), STMT is the load statement. If function returns true,
*INDEX_P, *OFFSET_P and *BY_REF is filled with the parameter index, offset
within the aggregate and whether it is a load from a value passed by
reference respectively. */
-static bool
-ipa_load_from_parm_agg_1 (struct func_body_info *fbi,
- vec<ipa_param_descriptor> descriptors,
- gimple stmt, tree op, int *index_p,
- HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p,
- bool *by_ref_p)
+bool
+ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
+ vec<ipa_param_descriptor> descriptors,
+ gimple *stmt, tree op, int *index_p,
+ HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p,
+ bool *by_ref_p, bool *guaranteed_unmodified)
{
int index;
HOST_WIDE_INT size, max_size;
- tree base = get_ref_base_and_extent (op, offset_p, &size, &max_size);
+ bool reverse;
+ tree base
+ = get_ref_base_and_extent (op, offset_p, &size, &max_size, &reverse);
if (max_size == -1 || max_size != size || *offset_p < 0)
return false;
*by_ref_p = false;
if (size_p)
*size_p = size;
+ if (guaranteed_unmodified)
+ *guaranteed_unmodified = true;
return true;
}
return false;
gdp = &p;
*/
- gimple def = SSA_NAME_DEF_STMT (TREE_OPERAND (base, 0));
+ gimple *def = SSA_NAME_DEF_STMT (TREE_OPERAND (base, 0));
index = load_from_unmodified_param (fbi, descriptors, def);
}
- if (index >= 0
- && parm_ref_data_preserved_p (fbi, index, stmt, op))
+ if (index >= 0)
{
+ bool data_preserved = parm_ref_data_preserved_p (fbi, index, stmt, op);
+ if (!data_preserved && !guaranteed_unmodified)
+ return false;
+
*index_p = index;
*by_ref_p = true;
if (size_p)
*size_p = size;
+ if (guaranteed_unmodified)
+ *guaranteed_unmodified = data_preserved;
return true;
}
return false;
}
-/* Just like the previous function, just without the param_analysis_info
- pointer, for users outside of this file. */
-
-bool
-ipa_load_from_parm_agg (struct ipa_node_params *info, gimple stmt,
- tree op, int *index_p, HOST_WIDE_INT *offset_p,
- bool *by_ref_p)
-{
- return ipa_load_from_parm_agg_1 (NULL, info->descriptors, stmt, op, index_p,
- offset_p, NULL, by_ref_p);
-}
-
/* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
of an assignment statement STMT, try to determine whether we are actually
handling any of the following cases and construct an appropriate jump
only needed for intraprocedural analysis. */
static void
-compute_complex_assign_jump_func (struct func_body_info *fbi,
+compute_complex_assign_jump_func (struct ipa_func_body_info *fbi,
struct ipa_node_params *info,
struct ipa_jump_func *jfunc,
- gcall *call, gimple stmt, tree name,
+ gcall *call, gimple *stmt, tree name,
tree param_type)
{
HOST_WIDE_INT offset, size, max_size;
tree op1, tc_ssa, base, ssa;
+ bool reverse;
int index;
+ gimple *stmt2 = stmt;
op1 = gimple_assign_rhs1 (stmt);
if (SSA_NAME_IS_DEFAULT_DEF (op1))
index = ipa_get_param_decl_index (info, SSA_NAME_VAR (op1));
else
- index = load_from_unmodified_param (fbi, info->descriptors,
- SSA_NAME_DEF_STMT (op1));
+ {
+ index = load_from_param (fbi, info->descriptors,
+ SSA_NAME_DEF_STMT (op1));
+ stmt2 = SSA_NAME_DEF_STMT (op1);
+ }
tc_ssa = op1;
}
else
{
- index = load_from_unmodified_param (fbi, info->descriptors, stmt);
+ index = load_from_param (fbi, info->descriptors, stmt);
tc_ssa = gimple_assign_lhs (stmt);
}
if (index >= 0)
{
- tree op2 = gimple_assign_rhs2 (stmt);
-
- if (op2)
- {
- if (!is_gimple_ip_invariant (op2)
- || (TREE_CODE_CLASS (gimple_expr_code (stmt)) != tcc_comparison
- && !useless_type_conversion_p (TREE_TYPE (name),
- TREE_TYPE (op1))))
- return;
-
- ipa_set_jf_arith_pass_through (jfunc, index, op2,
- gimple_assign_rhs_code (stmt));
- }
- else if (gimple_assign_single_p (stmt))
+ switch (gimple_assign_rhs_class (stmt))
{
- bool agg_p = parm_ref_data_pass_through_p (fbi, index, call, tc_ssa);
- ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
+ case GIMPLE_BINARY_RHS:
+ {
+ tree op2 = gimple_assign_rhs2 (stmt);
+ if (!is_gimple_ip_invariant (op2)
+ || ((TREE_CODE_CLASS (gimple_assign_rhs_code (stmt))
+ != tcc_comparison)
+ && !useless_type_conversion_p (TREE_TYPE (name),
+ TREE_TYPE (op1))))
+ return;
+
+ ipa_set_jf_arith_pass_through (jfunc, index, op2,
+ gimple_assign_rhs_code (stmt));
+ break;
+ }
+ case GIMPLE_SINGLE_RHS:
+ {
+ bool agg_p = parm_ref_data_pass_through_p (fbi, index, call,
+ tc_ssa);
+ ipa_set_jf_simple_pass_through (jfunc, index, agg_p);
+ break;
+ }
+ case GIMPLE_UNARY_RHS:
+ if (is_gimple_assign (stmt2)
+ && gimple_assign_rhs_class (stmt2) == GIMPLE_UNARY_RHS
+ && ! CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt2)))
+ ipa_set_jf_unary_pass_through (jfunc, index,
+ gimple_assign_rhs_code (stmt2));
+ default:;
}
return;
}
op1 = TREE_OPERAND (op1, 0);
if (TREE_CODE (TREE_TYPE (op1)) != RECORD_TYPE)
return;
- base = get_ref_base_and_extent (op1, &offset, &size, &max_size);
+ base = get_ref_base_and_extent (op1, &offset, &size, &max_size, &reverse);
if (TREE_CODE (base) != MEM_REF
/* If this is a varying address, punt. */
|| max_size == -1
RHS stripped off the ADDR_EXPR is stored into *OBJ_P. */
static tree
-get_ancestor_addr_info (gimple assign, tree *obj_p, HOST_WIDE_INT *offset)
+get_ancestor_addr_info (gimple *assign, tree *obj_p, HOST_WIDE_INT *offset)
{
HOST_WIDE_INT size, max_size;
tree expr, parm, obj;
+ bool reverse;
if (!gimple_assign_single_p (assign))
return NULL_TREE;
return NULL_TREE;
expr = TREE_OPERAND (expr, 0);
obj = expr;
- expr = get_ref_base_and_extent (expr, offset, &size, &max_size);
+ expr = get_ref_base_and_extent (expr, offset, &size, &max_size, &reverse);
if (TREE_CODE (expr) != MEM_REF
/* If this is a varying address, punt. */
return D.1879_6; */
static void
-compute_complex_ancestor_jump_func (struct func_body_info *fbi,
+compute_complex_ancestor_jump_func (struct ipa_func_body_info *fbi,
struct ipa_node_params *info,
struct ipa_jump_func *jfunc,
gcall *call, gphi *phi)
{
HOST_WIDE_INT offset;
- gimple assign, cond;
+ gimple *assign, *cond;
basic_block phi_bb, assign_bb, cond_bb;
tree tmp, parm, expr, obj;
int index, i;
{
while (TREE_CODE (rhs) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (rhs))
{
- gimple def_stmt = SSA_NAME_DEF_STMT (rhs);
+ gimple *def_stmt = SSA_NAME_DEF_STMT (rhs);
if (gimple_assign_single_p (def_stmt))
rhs = gimple_assign_rhs1 (def_stmt);
bool check_ref, by_ref;
ao_ref r;
+ if (PARAM_VALUE (PARAM_IPA_MAX_AGG_ITEMS) == 0)
+ return;
+
/* The function operates in three stages. First, we prepare check_ref, r,
arg_base and arg_offset based on what is actually passed as an actual
argument. */
else if (TREE_CODE (arg) == ADDR_EXPR)
{
HOST_WIDE_INT arg_max_size;
+ bool reverse;
arg = TREE_OPERAND (arg, 0);
arg_base = get_ref_base_and_extent (arg, &arg_offset, &arg_size,
- &arg_max_size);
+ &arg_max_size, &reverse);
if (arg_max_size == -1
|| arg_max_size != arg_size
|| arg_offset < 0)
else
{
HOST_WIDE_INT arg_max_size;
+ bool reverse;
gcc_checking_assert (AGGREGATE_TYPE_P (TREE_TYPE (arg)));
by_ref = false;
check_ref = false;
arg_base = get_ref_base_and_extent (arg, &arg_offset, &arg_size,
- &arg_max_size);
+ &arg_max_size, &reverse);
if (arg_max_size == -1
|| arg_max_size != arg_size
|| arg_offset < 0)
for (; !gsi_end_p (gsi); gsi_prev (&gsi))
{
struct ipa_known_agg_contents_list *n, **p;
- gimple stmt = gsi_stmt (gsi);
+ gimple *stmt = gsi_stmt (gsi);
HOST_WIDE_INT lhs_offset, lhs_size, lhs_max_size;
tree lhs, rhs, lhs_base;
+ bool reverse;
if (!stmt_may_clobber_ref_p_1 (stmt, &r))
continue;
break;
lhs_base = get_ref_base_and_extent (lhs, &lhs_offset, &lhs_size,
- &lhs_max_size);
+ &lhs_max_size, &reverse);
if (lhs_max_size == -1
|| lhs_max_size != lhs_size)
break;
}
}
-static tree
+/* Return the Ith param type of callee associated with call graph
+ edge E. */
+
+tree
ipa_get_callee_param_type (struct cgraph_edge *e, int i)
{
int n;
to this callsite. */
static void
-ipa_compute_jump_functions_for_edge (struct func_body_info *fbi,
+ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi,
struct cgraph_edge *cs)
{
struct ipa_node_params *info = IPA_NODE_REF (cs->caller);
useful_context = true;
}
- if (POINTER_TYPE_P (TREE_TYPE(arg)))
+ if (POINTER_TYPE_P (TREE_TYPE (arg)))
{
- unsigned HOST_WIDE_INT hwi_bitpos;
- unsigned align;
+ bool addr_nonzero = false;
+ bool strict_overflow = false;
+
+ if (TREE_CODE (arg) == SSA_NAME
+ && param_type
+ && get_ptr_nonnull (arg))
+ addr_nonzero = true;
+ else if (tree_single_nonzero_warnv_p (arg, &strict_overflow))
+ addr_nonzero = true;
+
+ if (addr_nonzero)
+ {
+ jfunc->vr_known = true;
+ jfunc->m_vr.type = VR_ANTI_RANGE;
+ jfunc->m_vr.min = build_int_cst (TREE_TYPE (arg), 0);
+ jfunc->m_vr.max = build_int_cst (TREE_TYPE (arg), 0);
+ jfunc->m_vr.equiv = NULL;
+ }
+ else
+ gcc_assert (!jfunc->vr_known);
+ }
+ else
+ {
+ wide_int min, max;
+ value_range_type type;
+ if (TREE_CODE (arg) == SSA_NAME
+ && param_type
+ && (type = get_range_info (arg, &min, &max))
+ && (type == VR_RANGE || type == VR_ANTI_RANGE))
+ {
+ value_range vr;
+
+ vr.type = type;
+ vr.min = wide_int_to_tree (TREE_TYPE (arg), min);
+ vr.max = wide_int_to_tree (TREE_TYPE (arg), max);
+ vr.equiv = NULL;
+ extract_range_from_unary_expr (&jfunc->m_vr,
+ NOP_EXPR,
+ param_type,
+ &vr, TREE_TYPE (arg));
+ if (jfunc->m_vr.type == VR_RANGE
+ || jfunc->m_vr.type == VR_ANTI_RANGE)
+ jfunc->vr_known = true;
+ else
+ jfunc->vr_known = false;
+ }
+ else
+ gcc_assert (!jfunc->vr_known);
+ }
- if (get_pointer_alignment_1 (arg, &align, &hwi_bitpos)
- && align % BITS_PER_UNIT == 0
- && hwi_bitpos % BITS_PER_UNIT == 0)
+ if (INTEGRAL_TYPE_P (TREE_TYPE (arg))
+ && (TREE_CODE (arg) == SSA_NAME || TREE_CODE (arg) == INTEGER_CST))
+ {
+ jfunc->bits.known = true;
+
+ if (TREE_CODE (arg) == SSA_NAME)
{
- jfunc->alignment.known = true;
- jfunc->alignment.align = align / BITS_PER_UNIT;
- jfunc->alignment.misalign = hwi_bitpos / BITS_PER_UNIT;
+ jfunc->bits.value = 0;
+ jfunc->bits.mask = widest_int::from (get_nonzero_bits (arg),
+ TYPE_SIGN (TREE_TYPE (arg)));
}
else
- gcc_assert (!jfunc->alignment.known);
+ {
+ jfunc->bits.value = wi::to_widest (arg);
+ jfunc->bits.mask = 0;
+ }
+ }
+ else if (POINTER_TYPE_P (TREE_TYPE (arg)))
+ {
+ unsigned HOST_WIDE_INT bitpos;
+ unsigned align;
+
+ jfunc->bits.known = true;
+ get_pointer_alignment_1 (arg, &align, &bitpos);
+ jfunc->bits.mask = wi::mask<widest_int>(TYPE_PRECISION (TREE_TYPE (arg)), false)
+ .and_not (align / BITS_PER_UNIT - 1);
+ jfunc->bits.value = bitpos / BITS_PER_UNIT;
}
else
- gcc_assert (!jfunc->alignment.known);
+ gcc_assert (!jfunc->bits.known);
- if (is_gimple_ip_invariant (arg))
+ if (is_gimple_ip_invariant (arg)
+ || (VAR_P (arg)
+ && is_global_var (arg)
+ && TREE_READONLY (arg)))
ipa_set_jf_constant (jfunc, arg, cs);
else if (!is_gimple_reg_type (TREE_TYPE (arg))
&& TREE_CODE (arg) == PARM_DECL)
}
else
{
- gimple stmt = SSA_NAME_DEF_STMT (arg);
+ gimple *stmt = SSA_NAME_DEF_STMT (arg);
if (is_gimple_assign (stmt))
compute_complex_assign_jump_func (fbi, info, jfunc,
call, stmt, arg, param_type);
from BB. */
static void
-ipa_compute_jump_functions_for_bb (struct func_body_info *fbi, basic_block bb)
+ipa_compute_jump_functions_for_bb (struct ipa_func_body_info *fbi, basic_block bb)
{
struct ipa_bb_info *bi = ipa_get_bb_info (fbi, bb);
int i;
field rather than the pfn. */
static tree
-ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta,
+ipa_get_stmt_member_ptr_load_param (gimple *stmt, bool use_delta,
HOST_WIDE_INT *offset_p)
{
tree rhs, rec, ref_field, ref_offset, fld, ptr_field, delta_field;
cs->indirect_info->param_index = param_index;
cs->indirect_info->agg_contents = 0;
cs->indirect_info->member_ptr = 0;
+ cs->indirect_info->guaranteed_unmodified = 0;
return cs;
}
passed by value or reference. */
static void
-ipa_analyze_indirect_call_uses (struct func_body_info *fbi, gcall *call,
+ipa_analyze_indirect_call_uses (struct ipa_func_body_info *fbi, gcall *call,
tree target)
{
struct ipa_node_params *info = fbi->info;
}
int index;
- gimple def = SSA_NAME_DEF_STMT (target);
+ gimple *def = SSA_NAME_DEF_STMT (target);
+ bool guaranteed_unmodified;
if (gimple_assign_single_p (def)
- && ipa_load_from_parm_agg_1 (fbi, info->descriptors, def,
- gimple_assign_rhs1 (def), &index, &offset,
- NULL, &by_ref))
+ && ipa_load_from_parm_agg (fbi, info->descriptors, def,
+ gimple_assign_rhs1 (def), &index, &offset,
+ NULL, &by_ref, &guaranteed_unmodified))
{
struct cgraph_edge *cs = ipa_note_param_call (fbi->node, index, call);
cs->indirect_info->offset = offset;
cs->indirect_info->agg_contents = 1;
cs->indirect_info->by_ref = by_ref;
+ cs->indirect_info->guaranteed_unmodified = guaranteed_unmodified;
return;
}
tree n2 = PHI_ARG_DEF (def, 1);
if (!ipa_is_ssa_with_stmt_def (n1) || !ipa_is_ssa_with_stmt_def (n2))
return;
- gimple d1 = SSA_NAME_DEF_STMT (n1);
- gimple d2 = SSA_NAME_DEF_STMT (n2);
+ gimple *d1 = SSA_NAME_DEF_STMT (n1);
+ gimple *d2 = SSA_NAME_DEF_STMT (n2);
tree rec;
basic_block bb, virt_bb;
/* Third, let's see that the branching is done depending on the least
significant bit of the pfn. */
- gimple branch = last_stmt (bb);
+ gimple *branch = last_stmt (bb);
if (!branch || gimple_code (branch) != GIMPLE_COND)
return;
cs->indirect_info->offset = offset;
cs->indirect_info->agg_contents = 1;
cs->indirect_info->member_ptr = 1;
+ cs->indirect_info->guaranteed_unmodified = 1;
}
return;
statement. */
static void
-ipa_analyze_virtual_call_uses (struct func_body_info *fbi,
+ipa_analyze_virtual_call_uses (struct ipa_func_body_info *fbi,
gcall *call, tree target)
{
tree obj = OBJ_TYPE_REF_OBJECT (target);
else
{
struct ipa_jump_func jfunc;
- gimple stmt = SSA_NAME_DEF_STMT (obj);
+ gimple *stmt = SSA_NAME_DEF_STMT (obj);
tree expr;
expr = get_ancestor_addr_info (stmt, &obj, &anc_offset);
containing intermediate information about each formal parameter. */
static void
-ipa_analyze_call_uses (struct func_body_info *fbi, gcall *call)
+ipa_analyze_call_uses (struct ipa_func_body_info *fbi, gcall *call)
{
tree target = gimple_call_fn (call);
formal parameters are called. */
static void
-ipa_analyze_stmt_uses (struct func_body_info *fbi, gimple stmt)
+ipa_analyze_stmt_uses (struct ipa_func_body_info *fbi, gimple *stmt)
{
if (is_gimple_call (stmt))
ipa_analyze_call_uses (fbi, as_a <gcall *> (stmt));
passed in DATA. */
static bool
-visit_ref_for_mod_analysis (gimple, tree op, tree, void *data)
+visit_ref_for_mod_analysis (gimple *, tree op, tree, void *data)
{
struct ipa_node_params *info = (struct ipa_node_params *) data;
the function being analyzed. */
static void
-ipa_analyze_params_uses_in_bb (struct func_body_info *fbi, basic_block bb)
+ipa_analyze_params_uses_in_bb (struct ipa_func_body_info *fbi, basic_block bb)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
- gimple stmt = gsi_stmt (gsi);
+ gimple *stmt = gsi_stmt (gsi);
if (is_gimple_debug (stmt))
continue;
class analysis_dom_walker : public dom_walker
{
public:
- analysis_dom_walker (struct func_body_info *fbi)
+ analysis_dom_walker (struct ipa_func_body_info *fbi)
: dom_walker (CDI_DOMINATORS), m_fbi (fbi) {}
- virtual void before_dom_children (basic_block);
+ virtual edge before_dom_children (basic_block);
private:
- struct func_body_info *m_fbi;
+ struct ipa_func_body_info *m_fbi;
};
-void
+edge
analysis_dom_walker::before_dom_children (basic_block bb)
{
ipa_analyze_params_uses_in_bb (m_fbi, bb);
ipa_compute_jump_functions_for_bb (m_fbi, bb);
+ return NULL;
+}
+
+/* Release body info FBI. */
+
+void
+ipa_release_body_info (struct ipa_func_body_info *fbi)
+{
+ int i;
+ struct ipa_bb_info *bi;
+
+ FOR_EACH_VEC_ELT (fbi->bb_infos, i, bi)
+ free_ipa_bb_info (bi);
+ fbi->bb_infos.release ();
}
/* Initialize the array describing properties of formal parameters
void
ipa_analyze_node (struct cgraph_node *node)
{
- struct func_body_info fbi;
+ struct ipa_func_body_info fbi;
struct ipa_node_params *info;
ipa_check_create_node_params ();
analysis_dom_walker (&fbi).walk (ENTRY_BLOCK_PTR_FOR_FN (cfun));
- int i;
- struct ipa_bb_info *bi;
- FOR_EACH_VEC_ELT (fbi.bb_infos, i, bi)
- free_ipa_bb_info (bi);
- fbi.bb_infos.release ();
+ ipa_release_body_info (&fbi);
free_dominance_info (CDI_DOMINATORS);
pop_cfun ();
}
ctx.offset_by (dst->value.ancestor.offset);
if (!ctx.useless_p ())
{
- vec_safe_grow_cleared (args->polymorphic_call_contexts,
- count);
- dst_ctx = ipa_get_ith_polymorhic_call_context (args, i);
+ if (!dst_ctx)
+ {
+ vec_safe_grow_cleared (args->polymorphic_call_contexts,
+ count);
+ dst_ctx = ipa_get_ith_polymorhic_call_context (args, i);
+ }
+
+ dst_ctx->combine_with (ctx);
}
- dst_ctx->combine_with (ctx);
}
if (src->agg.items
dst->value.ancestor.agg_preserved &=
src->value.pass_through.agg_preserved;
}
+ else if (src->type == IPA_JF_PASS_THROUGH
+ && TREE_CODE_CLASS (src->value.pass_through.operation) == tcc_unary)
+ {
+ dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
+ dst->value.ancestor.agg_preserved = false;
+ }
else if (src->type == IPA_JF_ANCESTOR)
{
dst->value.ancestor.formal_id = src->value.ancestor.formal_id;
&& ipa_get_jf_pass_through_agg_preserved (src);
ipa_set_jf_simple_pass_through (dst, formal_id, agg_p);
}
+ else if (TREE_CODE_CLASS (operation) == tcc_unary)
+ ipa_set_jf_unary_pass_through (dst, formal_id, operation);
else
{
tree operand = ipa_get_jf_pass_through_operand (src);
return ie;
}
-/* Retrieve value from aggregate jump function AGG for the given OFFSET or
- return NULL if there is not any. BY_REF specifies whether the value has to
- be passed by reference or by value. */
+/* Attempt to locate an interprocedural constant at a given REQ_OFFSET in
+ CONSTRUCTOR and return it. Return NULL if the search fails for some
+ reason. */
+
+static tree
+find_constructor_constant_at_offset (tree constructor, HOST_WIDE_INT req_offset)
+{
+ tree type = TREE_TYPE (constructor);
+ if (TREE_CODE (type) != ARRAY_TYPE
+ && TREE_CODE (type) != RECORD_TYPE)
+ return NULL;
+
+ unsigned ix;
+ tree index, val;
+ FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, val)
+ {
+ HOST_WIDE_INT elt_offset;
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ offset_int off;
+ tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (type));
+ gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
+
+ if (index)
+ {
+ off = wi::to_offset (index);
+ if (TYPE_DOMAIN (type) && TYPE_MIN_VALUE (TYPE_DOMAIN (type)))
+ {
+ tree low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
+ gcc_assert (TREE_CODE (unit_size) == INTEGER_CST);
+ off = wi::sext (off - wi::to_offset (low_bound),
+ TYPE_PRECISION (TREE_TYPE (index)));
+ }
+ off *= wi::to_offset (unit_size);
+ }
+ else
+ off = wi::to_offset (unit_size) * ix;
+
+ off = wi::lshift (off, LOG2_BITS_PER_UNIT);
+ if (!wi::fits_shwi_p (off) || wi::neg_p (off))
+ continue;
+ elt_offset = off.to_shwi ();
+ }
+ else if (TREE_CODE (type) == RECORD_TYPE)
+ {
+ gcc_checking_assert (index && TREE_CODE (index) == FIELD_DECL);
+ if (DECL_BIT_FIELD (index))
+ continue;
+ elt_offset = int_bit_position (index);
+ }
+ else
+ gcc_unreachable ();
+
+ if (elt_offset > req_offset)
+ return NULL;
+
+ if (TREE_CODE (val) == CONSTRUCTOR)
+ return find_constructor_constant_at_offset (val,
+ req_offset - elt_offset);
+
+ if (elt_offset == req_offset
+ && is_gimple_reg_type (TREE_TYPE (val))
+ && is_gimple_ip_invariant (val))
+ return val;
+ }
+ return NULL;
+}
+
+/* Check whether SCALAR could be used to look up an aggregate interprocedural
+ invariant from a static constructor and if so, return it. Otherwise return
+ NULL. */
+
+static tree
+ipa_find_agg_cst_from_init (tree scalar, HOST_WIDE_INT offset, bool by_ref)
+{
+ if (by_ref)
+ {
+ if (TREE_CODE (scalar) != ADDR_EXPR)
+ return NULL;
+ scalar = TREE_OPERAND (scalar, 0);
+ }
+
+ if (!VAR_P (scalar)
+ || !is_global_var (scalar)
+ || !TREE_READONLY (scalar)
+ || !DECL_INITIAL (scalar)
+ || TREE_CODE (DECL_INITIAL (scalar)) != CONSTRUCTOR)
+ return NULL;
+
+ return find_constructor_constant_at_offset (DECL_INITIAL (scalar), offset);
+}
+
+/* Retrieve value from aggregate jump function AGG or static initializer of
+ SCALAR (which can be NULL) for the given OFFSET or return NULL if there is
+ none. BY_REF specifies whether the value has to be passed by reference or
+ by value. If FROM_GLOBAL_CONSTANT is non-NULL, then the boolean it points
+ to is set to true if the value comes from an initializer of a constant. */
tree
-ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg,
- HOST_WIDE_INT offset, bool by_ref)
+ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
+ HOST_WIDE_INT offset, bool by_ref,
+ bool *from_global_constant)
{
struct ipa_agg_jf_item *item;
int i;
- if (by_ref != agg->by_ref)
+ if (scalar)
+ {
+ tree res = ipa_find_agg_cst_from_init (scalar, offset, by_ref);
+ if (res)
+ {
+ if (from_global_constant)
+ *from_global_constant = true;
+ return res;
+ }
+ }
+
+ if (!agg
+ || by_ref != agg->by_ref)
return NULL;
FOR_EACH_VEC_SAFE_ELT (agg->items, i, item)
/* Currently we do not have clobber values, return NULL for them once
we do. */
gcc_checking_assert (is_gimple_ip_invariant (item->value));
+ if (from_global_constant)
+ *from_global_constant = false;
return item->value;
}
return NULL;
struct cgraph_edge *cs;
tree target;
bool agg_contents = ie->indirect_info->agg_contents;
-
- if (ie->indirect_info->agg_contents)
- target = ipa_find_agg_cst_for_param (&jfunc->agg,
- ie->indirect_info->offset,
- ie->indirect_info->by_ref);
+ tree scalar = ipa_value_from_jfunc (new_root_info, jfunc);
+ if (agg_contents)
+ {
+ bool from_global_constant;
+ target = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
+ ie->indirect_info->offset,
+ ie->indirect_info->by_ref,
+ &from_global_constant);
+ if (target
+ && !from_global_constant
+ && !ie->indirect_info->guaranteed_unmodified)
+ return NULL;
+ }
else
- target = ipa_value_from_jfunc (new_root_info, jfunc);
+ target = scalar;
if (!target)
return NULL;
cs = ipa_make_edge_direct_to_target (ie, target);
{
tree vtable;
unsigned HOST_WIDE_INT offset;
- tree t = ipa_find_agg_cst_for_param (&jfunc->agg,
+ tree scalar = (jfunc->type == IPA_JF_CONST) ? ipa_get_jf_constant (jfunc)
+ : NULL;
+ tree t = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
ie->indirect_info->offset,
true);
if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
{
+ bool can_refer;
t = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
- vtable, offset);
- if (t)
+ vtable, offset, &can_refer);
+ if (can_refer)
{
- if ((TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE
- && DECL_FUNCTION_CODE (t) == BUILT_IN_UNREACHABLE)
+ if (!t
+ || (TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE
+ && DECL_FUNCTION_CODE (t) == BUILT_IN_UNREACHABLE)
|| !possible_polymorphic_call_target_p
(ie, cgraph_node::get (t)))
{
/* Do not speculate builtin_unreachable, it is stupid! */
if (!ie->indirect_info->vptr_changed)
target = ipa_impossible_devirt_target (ie, target);
+ else
+ target = NULL;
}
else
{
free (lattices);
/* Lattice values and their sources are deallocated with their alocation
pool. */
+ known_csts.release ();
known_contexts.release ();
lattices = NULL;
new_info->analysis_done = old_info->analysis_done;
new_info->node_enqueued = old_info->node_enqueued;
+ new_info->versionable = old_info->versionable;
old_av = ipa_get_agg_replacements_for_node (src);
if (old_av)
ipcp_transformation_summary *src_trans = ipcp_get_transformation_summary (src);
- if (src_trans && vec_safe_length (src_trans->alignments) > 0)
+ if (src_trans)
{
ipcp_grow_transformations_if_necessary ();
src_trans = ipcp_get_transformation_summary (src);
- const vec<ipa_alignment, va_gc> *src_alignments = src_trans->alignments;
- vec<ipa_alignment, va_gc> *&dst_alignments
- = ipcp_get_transformation_summary (dst)->alignments;
- vec_safe_reserve_exact (dst_alignments, src_alignments->length ());
- for (unsigned i = 0; i < src_alignments->length (); ++i)
- dst_alignments->quick_push ((*src_alignments)[i]);
+ const vec<ipa_vr, va_gc> *src_vr = src_trans->m_vr;
+ vec<ipa_vr, va_gc> *&dst_vr
+ = ipcp_get_transformation_summary (dst)->m_vr;
+ if (vec_safe_length (src_trans->m_vr) > 0)
+ {
+ vec_safe_reserve_exact (dst_vr, src_vr->length ());
+ for (unsigned i = 0; i < src_vr->length (); ++i)
+ dst_vr->quick_push ((*src_vr)[i]);
+ }
+ }
+
+ if (src_trans && vec_safe_length (src_trans->bits) > 0)
+ {
+ ipcp_grow_transformations_if_necessary ();
+ src_trans = ipcp_get_transformation_summary (src);
+ const vec<ipa_bits, va_gc> *src_bits = src_trans->bits;
+ vec<ipa_bits, va_gc> *&dst_bits
+ = ipcp_get_transformation_summary (dst)->bits;
+ vec_safe_reserve_exact (dst_bits, src_bits->length ());
+ for (unsigned i = 0; i < src_bits->length (); ++i)
+ dst_bits->quick_push ((*src_bits)[i]);
}
}
if (is_gimple_reg_type (ptype))
{
unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ptype));
- if (TYPE_ALIGN (ptype) < malign)
+ if (TYPE_ALIGN (ptype) != malign)
ptype = build_aligned_type (ptype, malign);
}
}
* BITS_PER_UNIT);
misalign = misalign & (align - 1);
if (misalign != 0)
- align = (misalign & -misalign);
+ align = least_bit_hwi (misalign);
if (align < TYPE_ALIGN (type))
type = build_aligned_type (type, align);
base = force_gimple_operand_gsi (&gsi, base,
true, NULL, true, GSI_SAME_STMT);
expr = fold_build2_loc (loc, MEM_REF, type, base, off);
+ REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse;
/* If expr is not a valid gimple call argument emit
a load into a temporary. */
if (is_gimple_reg_type (TREE_TYPE (expr)))
{
- gimple tem = gimple_build_assign (NULL_TREE, expr);
+ gimple *tem = gimple_build_assign (NULL_TREE, expr);
if (gimple_in_ssa_p (cfun))
{
gimple_set_vuse (tem, gimple_vuse (stmt));
else
{
expr = fold_build2_loc (loc, MEM_REF, adj->type, base, off);
+ REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse;
expr = build_fold_addr_expr (expr);
expr = force_gimple_operand_gsi (&gsi, expr,
true, NULL, true, GSI_SAME_STMT);
{
unsigned int ix;
tree ddecl = NULL_TREE, origin = DECL_ORIGIN (adj->base), arg;
- gimple def_temp;
+ gimple *def_temp;
arg = gimple_call_arg (stmt, adj->base_index);
if (!useless_type_conversion_p (TREE_TYPE (origin), TREE_TYPE (arg)))
ddecl = make_node (DEBUG_EXPR_DECL);
DECL_ARTIFICIAL (ddecl) = 1;
TREE_TYPE (ddecl) = TREE_TYPE (origin);
- DECL_MODE (ddecl) = DECL_MODE (origin);
+ SET_DECL_MODE (ddecl, DECL_MODE (origin));
vec_safe_push (*debug_args, origin);
vec_safe_push (*debug_args, ddecl);
tree src;
if (cand->by_ref)
- src = build_simple_mem_ref (cand->new_decl);
+ {
+ src = build_simple_mem_ref (cand->new_decl);
+ REF_REVERSE_STORAGE_ORDER (src) = cand->reverse;
+ }
else
src = cand->new_decl;
}
HOST_WIDE_INT offset, size, max_size;
- tree base = get_ref_base_and_extent (**expr, &offset, &size, &max_size);
+ bool reverse;
+ tree base
+ = get_ref_base_and_extent (**expr, &offset, &size, &max_size, &reverse);
if (!base || size == -1 || max_size == -1)
return NULL;
bp_pack_value (&bp, jump_func->value.pass_through.agg_preserved, 1);
streamer_write_bitpack (&bp);
}
+ else if (TREE_CODE_CLASS (jump_func->value.pass_through.operation)
+ == tcc_unary)
+ streamer_write_uhwi (ob, jump_func->value.pass_through.formal_id);
else
{
stream_write_tree (ob, jump_func->value.pass_through.operand, true);
}
bp = bitpack_create (ob->main_stream);
- bp_pack_value (&bp, jump_func->alignment.known, 1);
+ bp_pack_value (&bp, jump_func->bits.known, 1);
streamer_write_bitpack (&bp);
- if (jump_func->alignment.known)
+ if (jump_func->bits.known)
{
- streamer_write_uhwi (ob, jump_func->alignment.align);
- streamer_write_uhwi (ob, jump_func->alignment.misalign);
+ streamer_write_widest_int (ob, jump_func->bits.value);
+ streamer_write_widest_int (ob, jump_func->bits.mask);
+ }
+ bp_pack_value (&bp, jump_func->vr_known, 1);
+ streamer_write_bitpack (&bp);
+ if (jump_func->vr_known)
+ {
+ streamer_write_enum (ob->main_stream, value_rang_type,
+ VR_LAST, jump_func->m_vr.type);
+ stream_write_tree (ob, jump_func->m_vr.min, true);
+ stream_write_tree (ob, jump_func->m_vr.max, true);
}
}
bool agg_preserved = bp_unpack_value (&bp, 1);
ipa_set_jf_simple_pass_through (jump_func, formal_id, agg_preserved);
}
+ else if (TREE_CODE_CLASS (operation) == tcc_unary)
+ {
+ int formal_id = streamer_read_uhwi (ib);
+ ipa_set_jf_unary_pass_through (jump_func, formal_id, operation);
+ }
else
{
tree operand = stream_read_tree (ib, data_in);
}
struct bitpack_d bp = streamer_read_bitpack (ib);
- bool alignment_known = bp_unpack_value (&bp, 1);
- if (alignment_known)
+ bool bits_known = bp_unpack_value (&bp, 1);
+ if (bits_known)
+ {
+ jump_func->bits.known = true;
+ jump_func->bits.value = streamer_read_widest_int (ib);
+ jump_func->bits.mask = streamer_read_widest_int (ib);
+ }
+ else
+ jump_func->bits.known = false;
+
+ struct bitpack_d vr_bp = streamer_read_bitpack (ib);
+ bool vr_known = bp_unpack_value (&vr_bp, 1);
+ if (vr_known)
{
- jump_func->alignment.known = true;
- jump_func->alignment.align = streamer_read_uhwi (ib);
- jump_func->alignment.misalign = streamer_read_uhwi (ib);
+ jump_func->vr_known = true;
+ jump_func->m_vr.type = streamer_read_enum (ib,
+ value_range_type,
+ VR_LAST);
+ jump_func->m_vr.min = stream_read_tree (ib, data_in);
+ jump_func->m_vr.max = stream_read_tree (ib, data_in);
}
else
- jump_func->alignment.known = false;
+ jump_func->vr_known = false;
}
/* Stream out parts of cgraph_indirect_call_info corresponding to CS that are
bp_pack_value (&bp, ii->agg_contents, 1);
bp_pack_value (&bp, ii->member_ptr, 1);
bp_pack_value (&bp, ii->by_ref, 1);
+ bp_pack_value (&bp, ii->guaranteed_unmodified, 1);
bp_pack_value (&bp, ii->vptr_changed, 1);
streamer_write_bitpack (&bp);
if (ii->agg_contents || ii->polymorphic)
ii->agg_contents = bp_unpack_value (&bp, 1);
ii->member_ptr = bp_unpack_value (&bp, 1);
ii->by_ref = bp_unpack_value (&bp, 1);
+ ii->guaranteed_unmodified = bp_unpack_value (&bp, 1);
ii->vptr_changed = bp_unpack_value (&bp, 1);
if (ii->agg_contents || ii->polymorphic)
ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
bp_pack_value (&bp, ipa_is_param_used (info, j), 1);
streamer_write_bitpack (&bp);
for (j = 0; j < ipa_get_param_count (info); j++)
- streamer_write_hwi (ob, ipa_get_controlled_uses (info, j));
+ {
+ streamer_write_hwi (ob, ipa_get_controlled_uses (info, j));
+ stream_write_tree (ob, ipa_get_type (info, j), true);
+ }
for (e = node->callees; e; e = e->next_callee)
{
struct ipa_edge_args *args = IPA_EDGE_REF (e);
for (k = 0; k < ipa_get_param_count (info); k++)
info->descriptors[k].move_cost = streamer_read_uhwi (ib);
-
+
bp = streamer_read_bitpack (ib);
if (ipa_get_param_count (info) != 0)
info->analysis_done = true;
for (k = 0; k < ipa_get_param_count (info); k++)
ipa_set_param_used (info, k, bp_unpack_value (&bp, 1));
for (k = 0; k < ipa_get_param_count (info); k++)
- ipa_set_controlled_uses (info, k, streamer_read_hwi (ib));
+ {
+ ipa_set_controlled_uses (info, k, streamer_read_hwi (ib));
+ info->descriptors[k].decl_or_type = stream_read_tree (ib, data_in);
+ }
for (e = node->callees; e; e = e->next_callee)
{
struct ipa_edge_args *args = IPA_EDGE_REF (e);
}
ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
- if (ts && vec_safe_length (ts->alignments) > 0)
+ if (ts && vec_safe_length (ts->m_vr) > 0)
{
- count = ts->alignments->length ();
-
+ count = ts->m_vr->length ();
streamer_write_uhwi (ob, count);
for (unsigned i = 0; i < count; ++i)
{
- ipa_alignment *parm_al = &(*ts->alignments)[i];
-
struct bitpack_d bp;
+ ipa_vr *parm_vr = &(*ts->m_vr)[i];
bp = bitpack_create (ob->main_stream);
- bp_pack_value (&bp, parm_al->known, 1);
+ bp_pack_value (&bp, parm_vr->known, 1);
+ streamer_write_bitpack (&bp);
+ if (parm_vr->known)
+ {
+ streamer_write_enum (ob->main_stream, value_rang_type,
+ VR_LAST, parm_vr->type);
+ streamer_write_wide_int (ob, parm_vr->min);
+ streamer_write_wide_int (ob, parm_vr->max);
+ }
+ }
+ }
+ else
+ streamer_write_uhwi (ob, 0);
+
+ if (ts && vec_safe_length (ts->bits) > 0)
+ {
+ count = ts->bits->length ();
+ streamer_write_uhwi (ob, count);
+
+ for (unsigned i = 0; i < count; ++i)
+ {
+ const ipa_bits& bits_jfunc = (*ts->bits)[i];
+ struct bitpack_d bp = bitpack_create (ob->main_stream);
+ bp_pack_value (&bp, bits_jfunc.known, 1);
streamer_write_bitpack (&bp);
- if (parm_al->known)
+ if (bits_jfunc.known)
{
- streamer_write_uhwi (ob, parm_al->align);
- streamer_write_hwi_in_range (ob->main_stream, 0, parm_al->align,
- parm_al->misalign);
+ streamer_write_widest_int (ob, bits_jfunc.value);
+ streamer_write_widest_int (ob, bits_jfunc.mask);
}
}
}
aggvals = av;
}
ipa_set_node_agg_value_chain (node, aggvals);
-
+
count = streamer_read_uhwi (ib);
if (count > 0)
{
ipcp_grow_transformations_if_necessary ();
ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
- vec_safe_grow_cleared (ts->alignments, count);
-
+ vec_safe_grow_cleared (ts->m_vr, count);
for (i = 0; i < count; i++)
{
- ipa_alignment *parm_al;
- parm_al = &(*ts->alignments)[i];
+ ipa_vr *parm_vr;
+ parm_vr = &(*ts->m_vr)[i];
struct bitpack_d bp;
bp = streamer_read_bitpack (ib);
- parm_al->known = bp_unpack_value (&bp, 1);
- if (parm_al->known)
+ parm_vr->known = bp_unpack_value (&bp, 1);
+ if (parm_vr->known)
+ {
+ parm_vr->type = streamer_read_enum (ib, value_range_type,
+ VR_LAST);
+ parm_vr->min = streamer_read_wide_int (ib);
+ parm_vr->max = streamer_read_wide_int (ib);
+ }
+ }
+ }
+ count = streamer_read_uhwi (ib);
+ if (count > 0)
+ {
+ ipcp_grow_transformations_if_necessary ();
+
+ ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
+ vec_safe_grow_cleared (ts->bits, count);
+
+ for (i = 0; i < count; i++)
+ {
+ ipa_bits& bits_jfunc = (*ts->bits)[i];
+ struct bitpack_d bp = streamer_read_bitpack (ib);
+ bits_jfunc.known = bp_unpack_value (&bp, 1);
+ if (bits_jfunc.known)
{
- parm_al->align = streamer_read_uhwi (ib);
- parm_al->misalign
- = streamer_read_hwi_in_range (ib, "ipa-prop misalign",
- 0, parm_al->align);
+ bits_jfunc.value = streamer_read_widest_int (ib);
+ bits_jfunc.mask = streamer_read_widest_int (ib);
}
}
}
class ipcp_modif_dom_walker : public dom_walker
{
public:
- ipcp_modif_dom_walker (struct func_body_info *fbi,
+ ipcp_modif_dom_walker (struct ipa_func_body_info *fbi,
vec<ipa_param_descriptor> descs,
struct ipa_agg_replacement_value *av,
bool *sc, bool *cc)
: dom_walker (CDI_DOMINATORS), m_fbi (fbi), m_descriptors (descs),
m_aggval (av), m_something_changed (sc), m_cfg_changed (cc) {}
- virtual void before_dom_children (basic_block);
+ virtual edge before_dom_children (basic_block);
private:
- struct func_body_info *m_fbi;
+ struct ipa_func_body_info *m_fbi;
vec<ipa_param_descriptor> m_descriptors;
struct ipa_agg_replacement_value *m_aggval;
bool *m_something_changed, *m_cfg_changed;
};
-void
+edge
ipcp_modif_dom_walker::before_dom_children (basic_block bb)
{
gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
struct ipa_agg_replacement_value *v;
- gimple stmt = gsi_stmt (gsi);
+ gimple *stmt = gsi_stmt (gsi);
tree rhs, val, t;
HOST_WIDE_INT offset, size;
int index;
if (vce)
continue;
- if (!ipa_load_from_parm_agg_1 (m_fbi, m_descriptors, stmt, rhs, &index,
- &offset, &size, &by_ref))
+ if (!ipa_load_from_parm_agg (m_fbi, m_descriptors, stmt, rhs, &index,
+ &offset, &size, &by_ref))
continue;
for (v = m_aggval; v; v = v->next)
if (v->index == index
&& gimple_purge_dead_eh_edges (gimple_bb (stmt)))
*m_cfg_changed = true;
}
-
+ return NULL;
}
-/* Update alignment of formal parameters as described in
+/* Update bits info of formal parameters as described in
ipcp_transformation_summary. */
static void
-ipcp_update_alignments (struct cgraph_node *node)
+ipcp_update_bits (struct cgraph_node *node)
{
- tree fndecl = node->decl;
- tree parm = DECL_ARGUMENTS (fndecl);
+ tree parm = DECL_ARGUMENTS (node->decl);
tree next_parm = parm;
ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
- if (!ts || vec_safe_length (ts->alignments) == 0)
+
+ if (!ts || vec_safe_length (ts->bits) == 0)
return;
- const vec<ipa_alignment, va_gc> &alignments = *ts->alignments;
- unsigned count = alignments.length ();
+
+ vec<ipa_bits, va_gc> &bits = *ts->bits;
+ unsigned count = bits.length ();
for (unsigned i = 0; i < count; ++i, parm = next_parm)
{
if (node->clone.combined_args_to_skip
&& bitmap_bit_p (node->clone.combined_args_to_skip, i))
continue;
+
gcc_checking_assert (parm);
next_parm = DECL_CHAIN (parm);
- if (!alignments[i].known || !is_gimple_reg (parm))
- continue;
+ if (!bits[i].known
+ || !(INTEGRAL_TYPE_P (TREE_TYPE (parm)) || POINTER_TYPE_P (TREE_TYPE (parm)))
+ || !is_gimple_reg (parm))
+ continue;
+
tree ddef = ssa_default_def (DECL_STRUCT_FUNCTION (node->decl), parm);
if (!ddef)
continue;
if (dump_file)
- fprintf (dump_file, " Adjusting alignment of param %u to %u, "
- "misalignment to %u\n", i, alignments[i].align,
- alignments[i].misalign);
-
- struct ptr_info_def *pi = get_ptr_info (ddef);
- gcc_checking_assert (pi);
- unsigned old_align;
- unsigned old_misalign;
- bool old_known = get_ptr_info_alignment (pi, &old_align, &old_misalign);
-
- if (old_known
- && old_align >= alignments[i].align)
{
- if (dump_file)
- fprintf (dump_file, " But the alignment was already %u.\n",
- old_align);
- continue;
+ fprintf (dump_file, "Adjusting mask for param %u to ", i);
+ print_hex (bits[i].mask, dump_file);
+ fprintf (dump_file, "\n");
+ }
+
+ if (INTEGRAL_TYPE_P (TREE_TYPE (ddef)))
+ {
+ unsigned prec = TYPE_PRECISION (TREE_TYPE (ddef));
+ signop sgn = TYPE_SIGN (TREE_TYPE (ddef));
+
+ wide_int nonzero_bits = wide_int::from (bits[i].mask, prec, UNSIGNED)
+ | wide_int::from (bits[i].value, prec, sgn);
+ set_nonzero_bits (ddef, nonzero_bits);
+ }
+ else
+ {
+ unsigned tem = bits[i].mask.to_uhwi ();
+ unsigned HOST_WIDE_INT bitpos = bits[i].value.to_uhwi ();
+ unsigned align = tem & -tem;
+ unsigned misalign = bitpos & (align - 1);
+
+ if (align > 1)
+ {
+ if (dump_file)
+ fprintf (dump_file, "Adjusting align: %u, misalign: %u\n", align, misalign);
+
+ unsigned old_align, old_misalign;
+ struct ptr_info_def *pi = get_ptr_info (ddef);
+ bool old_known = get_ptr_info_alignment (pi, &old_align, &old_misalign);
+
+ if (old_known
+ && old_align > align)
+ {
+ if (dump_file)
+ {
+ fprintf (dump_file, "But alignment was already %u.\n", old_align);
+ if ((old_misalign & (align - 1)) != misalign)
+ fprintf (dump_file, "old_misalign (%u) and misalign (%u) mismatch\n",
+ old_misalign, misalign);
+ }
+ continue;
+ }
+
+ if (old_known
+ && ((misalign & (old_align - 1)) != old_misalign)
+ && dump_file)
+ fprintf (dump_file, "old_misalign (%u) and misalign (%u) mismatch\n",
+ old_misalign, misalign);
+
+ set_ptr_info_alignment (pi, align, misalign);
+ }
+ }
+ }
+}
+
+/* Update value range of formal parameters as described in
+ ipcp_transformation_summary. */
+
+static void
+ipcp_update_vr (struct cgraph_node *node)
+{
+ tree fndecl = node->decl;
+ tree parm = DECL_ARGUMENTS (fndecl);
+ tree next_parm = parm;
+ ipcp_transformation_summary *ts = ipcp_get_transformation_summary (node);
+ if (!ts || vec_safe_length (ts->m_vr) == 0)
+ return;
+ const vec<ipa_vr, va_gc> &vr = *ts->m_vr;
+ unsigned count = vr.length ();
+
+ for (unsigned i = 0; i < count; ++i, parm = next_parm)
+ {
+ if (node->clone.combined_args_to_skip
+ && bitmap_bit_p (node->clone.combined_args_to_skip, i))
+ continue;
+ gcc_checking_assert (parm);
+ next_parm = DECL_CHAIN (parm);
+ tree ddef = ssa_default_def (DECL_STRUCT_FUNCTION (node->decl), parm);
+
+ if (!ddef || !is_gimple_reg (parm))
+ continue;
+
+ if (vr[i].known
+ && (vr[i].type == VR_RANGE || vr[i].type == VR_ANTI_RANGE))
+ {
+ tree type = TREE_TYPE (ddef);
+ unsigned prec = TYPE_PRECISION (type);
+ if (INTEGRAL_TYPE_P (TREE_TYPE (ddef)))
+ {
+ if (dump_file)
+ {
+ fprintf (dump_file, "Setting value range of param %u ", i);
+ fprintf (dump_file, "%s[",
+ (vr[i].type == VR_ANTI_RANGE) ? "~" : "");
+ print_decs (vr[i].min, dump_file);
+ fprintf (dump_file, ", ");
+ print_decs (vr[i].max, dump_file);
+ fprintf (dump_file, "]\n");
+ }
+ set_range_info (ddef, vr[i].type,
+ wide_int_storage::from (vr[i].min, prec,
+ TYPE_SIGN (type)),
+ wide_int_storage::from (vr[i].max, prec,
+ TYPE_SIGN (type)));
+ }
+ else if (POINTER_TYPE_P (TREE_TYPE (ddef))
+ && vr[i].type == VR_ANTI_RANGE
+ && wi::eq_p (vr[i].min, 0)
+ && wi::eq_p (vr[i].max, 0))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Setting nonnull for %u\n", i);
+ set_ptr_nonnull (ddef);
+ }
}
- set_ptr_info_alignment (pi, alignments[i].align, alignments[i].misalign);
}
}
ipcp_transform_function (struct cgraph_node *node)
{
vec<ipa_param_descriptor> descriptors = vNULL;
- struct func_body_info fbi;
+ struct ipa_func_body_info fbi;
struct ipa_agg_replacement_value *aggval;
int param_count;
bool cfg_changed = false, something_changed = false;
fprintf (dump_file, "Modification phase of node %s/%i\n",
node->name (), node->order);
- ipcp_update_alignments (node);
+ ipcp_update_bits (node);
+ ipcp_update_vr (node);
aggval = ipa_get_agg_replacements_for_node (node);
if (!aggval)
return 0;
fbi.bb_infos.release ();
free_dominance_info (CDI_DOMINATORS);
(*ipcp_transformations)[node->uid].agg_values = NULL;
- (*ipcp_transformations)[node->uid].alignments = NULL;
+ (*ipcp_transformations)[node->uid].bits = NULL;
+ (*ipcp_transformations)[node->uid].m_vr = NULL;
+
descriptors.release ();
if (!something_changed)