ipa_set_jf_known_type (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
tree base_type, tree component_type)
{
+ gcc_assert (TREE_CODE (component_type) == RECORD_TYPE
+ && TYPE_BINFO (component_type));
jfunc->type = IPA_JF_KNOWN_TYPE;
jfunc->value.known_type.offset = offset,
jfunc->value.known_type.base_type = base_type;
-/* Like detect_type_change but with extra argument COMP_TYPE which will become
- the component type part of new JFUNC of dynamic type change is detected and
- the new base type is identified. */
+/* Detect whether the dynamic type of ARG of COMP_TYPE has changed (before
+ callsite CALL) by looking for assignments to its virtual table pointer. If
+ it is, return true and fill in the jump function JFUNC with relevant type
+ information or set it to unknown. ARG is the object itself (not a pointer
+ to it, unless dereferenced). BASE is the base of the memory access as
+ returned by get_ref_base_and_extent, as is the offset. */
static bool
-detect_type_change_1 (tree arg, tree base, tree comp_type, gimple call,
- struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
+detect_type_change (tree arg, tree base, tree comp_type, gimple call,
+ struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
{
struct type_change_info tci;
ao_ref ao;
|| handled_component_p (arg));
/* Const calls cannot call virtual methods through VMT and so type changes do
not matter. */
- if (!flag_devirtualize || !gimple_vuse (call))
+ if (!flag_devirtualize || !gimple_vuse (call)
+ /* Be sure expected_type is polymorphic. */
+ || !comp_type
+ || TREE_CODE (comp_type) != RECORD_TYPE
+ || !TYPE_BINFO (comp_type)
+ || !BINFO_VTABLE (TYPE_BINFO (comp_type)))
return false;
ao_ref_init (&ao, arg);
return true;
}
-/* Detect whether the dynamic type of ARG has changed (before callsite CALL) by
- looking for assignments to its virtual table pointer. If it is, return true
- and fill in the jump function JFUNC with relevant type information or set it
- to unknown. ARG is the object itself (not a pointer to it, unless
- dereferenced). BASE is the base of the memory access as returned by
- get_ref_base_and_extent, as is the offset. */
-
-static bool
-detect_type_change (tree arg, tree base, gimple call,
- struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
-{
- return detect_type_change_1 (arg, base, TREE_TYPE (arg), call, jfunc, offset);
-}
-
/* Like detect_type_change but ARG is supposed to be a non-dereferenced pointer
SSA name (its dereference will become the base and the offset is assumed to
be zero). */
static bool
-detect_type_change_ssa (tree arg, gimple call, struct ipa_jump_func *jfunc)
+detect_type_change_ssa (tree arg, tree comp_type,
+ gimple call, struct ipa_jump_func *jfunc)
{
- tree comp_type;
-
gcc_checking_assert (TREE_CODE (arg) == SSA_NAME);
if (!flag_devirtualize
- || !POINTER_TYPE_P (TREE_TYPE (arg))
- || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != RECORD_TYPE)
+ || !POINTER_TYPE_P (TREE_TYPE (arg)))
return false;
- comp_type = TREE_TYPE (TREE_TYPE (arg));
arg = build2 (MEM_REF, ptr_type_node, arg,
build_int_cst (ptr_type_node, 0));
- return detect_type_change_1 (arg, arg, comp_type, call, jfunc, 0);
+ return detect_type_change (arg, arg, comp_type, call, jfunc, 0);
}
/* Callback of walk_aliased_vdefs. Flags that it has been invoked to the
compute_complex_assign_jump_func (struct ipa_node_params *info,
struct param_analysis_info *parms_ainfo,
struct ipa_jump_func *jfunc,
- gimple call, gimple stmt, tree name)
+ gimple call, gimple stmt, tree name,
+ tree param_type)
{
HOST_WIDE_INT offset, size, max_size;
tree op1, tc_ssa, base, ssa;
{
bool agg_p = parm_ref_data_pass_through_p (&parms_ainfo[index],
call, tc_ssa);
- bool type_p = !detect_type_change_ssa (tc_ssa, call, jfunc);
+ bool type_p = false;
+
+ if (param_type && POINTER_TYPE_P (param_type))
+ type_p = !detect_type_change_ssa (tc_ssa, TREE_TYPE (param_type),
+ call, jfunc);
if (type_p || jfunc->type == IPA_JF_UNKNOWN)
ipa_set_jf_simple_pass_through (jfunc, index, agg_p, type_p);
}
/* Dynamic types are changed in constructors and destructors. */
index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
- if (index >= 0)
+ if (index >= 0 && param_type && POINTER_TYPE_P (param_type))
{
- bool type_p = !detect_type_change (op1, base, call, jfunc, offset);
+ bool type_p = !detect_type_change (op1, base, TREE_TYPE (param_type),
+ call, jfunc, offset);
if (type_p || jfunc->type == IPA_JF_UNKNOWN)
ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (op1), index,
parm_ref_data_pass_through_p (&parms_ainfo[index],
compute_complex_ancestor_jump_func (struct ipa_node_params *info,
struct param_analysis_info *parms_ainfo,
struct ipa_jump_func *jfunc,
- gimple call, gimple phi)
+ gimple call, gimple phi, tree param_type)
{
HOST_WIDE_INT offset;
gimple assign, cond;
return;
}
- bool type_p = !detect_type_change (obj, expr, call, jfunc, offset);
+ bool type_p = false;
+ if (param_type && POINTER_TYPE_P (param_type))
+ type_p = !detect_type_change (obj, expr, TREE_TYPE (param_type),
+ call, jfunc, offset);
if (type_p || jfunc->type == IPA_JF_UNKNOWN)
ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index,
parm_ref_data_pass_through_p (&parms_ainfo[index],
/* Given OP which is passed as an actual argument to a called function,
determine if it is possible to construct a KNOWN_TYPE jump function for it
- and if so, create one and store it to JFUNC. */
+ and if so, create one and store it to JFUNC.
+ EXPECTED_TYPE represents a type the argument should be in */
static void
compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc,
- gimple call)
+ gimple call, tree expected_type)
{
HOST_WIDE_INT offset, size, max_size;
tree base;
if (!flag_devirtualize
|| TREE_CODE (op) != ADDR_EXPR
- || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE
+ /* Be sure expected_type is polymorphic. */
+ || !expected_type
+ || TREE_CODE (expected_type) != RECORD_TYPE
+ || !TYPE_BINFO (expected_type)
+ || !BINFO_VTABLE (TYPE_BINFO (expected_type)))
return;
op = TREE_OPERAND (op, 0);
|| is_global_var (base))
return;
- if (!TYPE_BINFO (TREE_TYPE (base))
- || detect_type_change (op, base, call, jfunc, offset))
+ if (detect_type_change (op, base, expected_type, call, jfunc, offset))
return;
- ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base), TREE_TYPE (op));
+ ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base),
+ expected_type);
}
/* Inspect the given TYPE and return true iff it has the same structure (the
}
}
+static tree
+ipa_get_callee_param_type (struct cgraph_edge *e, int i)
+{
+ int n;
+ tree type = (e->callee
+ ? TREE_TYPE (e->callee->symbol.decl)
+ : gimple_call_fntype (e->call_stmt));
+ tree t = TYPE_ARG_TYPES (type);
+
+ for (n = 0; n < i; n++)
+ {
+ if (!t)
+ break;
+ t = TREE_CHAIN (t);
+ }
+ if (t)
+ return TREE_VALUE (t);
+ if (!e->callee)
+ return NULL;
+ t = DECL_ARGUMENTS (e->callee->symbol.decl);
+ for (n = 0; n < i; n++)
+ {
+ if (!t)
+ return NULL;
+ t = TREE_CHAIN (t);
+ }
+ if (t)
+ return TREE_TYPE (t);
+ return NULL;
+}
+
/* Compute jump function for all arguments of callsite CS and insert the
information in the jump_functions array in the ipa_edge_args corresponding
to this callsite. */
{
struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, n);
tree arg = gimple_call_arg (call, n);
+ tree param_type = ipa_get_callee_param_type (cs, n);
if (is_gimple_ip_invariant (arg))
ipa_set_jf_constant (jfunc, arg, cs);
bool agg_p, type_p;
agg_p = parm_ref_data_pass_through_p (&parms_ainfo[index],
call, arg);
- type_p = !detect_type_change_ssa (arg, call, jfunc);
+ if (param_type && POINTER_TYPE_P (param_type))
+ type_p = !detect_type_change_ssa (arg, TREE_TYPE (param_type),
+ call, jfunc);
+ else
+ type_p = false;
if (type_p || jfunc->type == IPA_JF_UNKNOWN)
- ipa_set_jf_simple_pass_through (jfunc, index, agg_p, type_p);
+ ipa_set_jf_simple_pass_through (jfunc, index, agg_p,
+ type_p);
}
}
else
gimple stmt = SSA_NAME_DEF_STMT (arg);
if (is_gimple_assign (stmt))
compute_complex_assign_jump_func (info, parms_ainfo, jfunc,
- call, stmt, arg);
+ call, stmt, arg, param_type);
else if (gimple_code (stmt) == GIMPLE_PHI)
compute_complex_ancestor_jump_func (info, parms_ainfo, jfunc,
- call, stmt);
+ call, stmt, param_type);
}
}
else
- compute_known_type_jump_func (arg, jfunc, call);
+ compute_known_type_jump_func (arg, jfunc, call,
+ param_type
+ && POINTER_TYPE_P (param_type)
+ ? TREE_TYPE (param_type)
+ : NULL);
if ((jfunc->type != IPA_JF_PASS_THROUGH
|| !ipa_get_jf_pass_through_agg_preserved (jfunc))
anc_offset = 0;
index = ipa_get_param_decl_index (info, SSA_NAME_VAR (obj));
gcc_assert (index >= 0);
- if (detect_type_change_ssa (obj, call, &jfunc))
+ if (detect_type_change_ssa (obj, obj_type_ref_class (target),
+ call, &jfunc))
return;
}
else
index = ipa_get_param_decl_index (info,
SSA_NAME_VAR (TREE_OPERAND (expr, 0)));
gcc_assert (index >= 0);
- if (detect_type_change (obj, expr, call, &jfunc, anc_offset))
+ if (detect_type_change (obj, expr, obj_type_ref_class (target),
+ call, &jfunc, anc_offset))
return;
}
jfunc.type = IPA_JF_UNKNOWN;
compute_known_type_jump_func (OBJ_TYPE_REF_OBJECT (otr), &jfunc,
- call);
+ call, obj_type_ref_class (otr));
if (jfunc.type != IPA_JF_KNOWN_TYPE)
return NULL_TREE;
binfo = ipa_binfo_from_known_type_jfunc (&jfunc);