while (TREE_CODE (op) == SSA_NAME
&& !SSA_NAME_IS_DEFAULT_DEF (op)
&& SSA_NAME_DEF_STMT (op)
- && gimple_assign_single_p (SSA_NAME_DEF_STMT (op)))
+ && (gimple_assign_single_p (SSA_NAME_DEF_STMT (op))
+ || gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI))
{
- if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
- return op;
- op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
+ /* Special case
+ if (ptr == 0)
+ ptr = 0;
+ else
+ ptr = ptr.foo;
+ This pattern is implicitly produced for casts to non-primary
+ bases. When doing context analysis, we do not really care
+ about the case pointer is NULL, becuase the call will be
+ undefined anyway. */
+ if (gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI)
+ {
+ gimple phi = SSA_NAME_DEF_STMT (op);
+
+ if (gimple_phi_num_args (phi) != 2)
+ return op;
+ if (integer_zerop (gimple_phi_arg_def (phi, 0)))
+ op = gimple_phi_arg_def (phi, 1);
+ else if (integer_zerop (gimple_phi_arg_def (phi, 1)))
+ op = gimple_phi_arg_def (phi, 0);
+ else
+ return op;
+ }
+ else
+ {
+ if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
+ return op;
+ op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
+ }
STRIP_NOPS (op);
}
return op;
is set), try to walk memory writes and find the actual construction of the
instance.
+ Return true if memory is unchanged from function entry.
+
We do not include this analysis in the context analysis itself, because
it needs memory SSA to be fully built and the walk may be expensive.
So it is not suitable for use withing fold_stmt and similar uses. */
function_entry_reached ? " (multiple types encountered)" : "");
}
- return true;
+ return false;
}
/* See if speculation given by SPEC_OUTER_TYPE, SPEC_OFFSET and SPEC_MAYBE_DERIVED_TYPE
gcc_checking_assert (cs->indirect_info->otr_token
== tree_to_shwi (OBJ_TYPE_REF_TOKEN (target)));
- if (context.get_dynamic_type (instance,
- OBJ_TYPE_REF_OBJECT (target),
- obj_type_ref_class (target), call))
- cs->indirect_info->context = context;
+ context.get_dynamic_type (instance,
+ OBJ_TYPE_REF_OBJECT (target),
+ obj_type_ref_class (target), call);
+ cs->indirect_info->context = context;
}
if (TREE_CODE (target) == SSA_NAME)
callee = cgraph_node::get_create (target);
}
+ /* If the edge is already speculated. */
+ if (speculative && ie->speculative)
+ {
+ struct cgraph_edge *e2;
+ struct ipa_ref *ref;
+ ie->speculative_call_info (e2, ie, ref);
+ if (e2->callee->ultimate_alias_target ()
+ != callee->ultimate_alias_target ())
+ {
+ if (dump_file)
+ fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
+ "(%s/%i -> %s/%i) but the call is already speculated to %s/%i. Giving up.\n",
+ xstrdup (ie->caller->name ()),
+ ie->caller->order,
+ xstrdup (callee->name ()),
+ callee->order,
+ xstrdup (e2->callee->name ()),
+ e2->callee->order);
+ }
+ else
+ {
+ if (dump_file)
+ fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
+ "(%s/%i -> %s/%i) this agree with previous speculation.\n",
+ xstrdup (ie->caller->name ()),
+ ie->caller->order,
+ xstrdup (callee->name ()),
+ callee->order);
+ }
+ return NULL;
+ }
+
if (!dbg_cnt (devirt))
return NULL;
ctx.offset_by (ie->indirect_info->offset);
- /* TODO: We want to record if type change happens.
- Old code did not do that that seems like a bug. */
- ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
- ie->indirect_info->otr_type);
+ if (ie->indirect_info->vptr_changed)
+ ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
+ ie->indirect_info->otr_type);
updated = ie->indirect_info->context.combine_with
(ctx, ie->indirect_info->otr_type);
}
/* Try to do lookup via known virtual table pointer value. */
- if (!ie->indirect_info->by_ref)
+ if (!ie->indirect_info->by_ref
+ && (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively))
{
tree vtable;
unsigned HOST_WIDE_INT offset;
true);
if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
{
- target = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
+ t = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
vtable, offset);
- if (target)
+ if (t)
{
- if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE
- && DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE)
+ if ((TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE
+ && DECL_FUNCTION_CODE (t) == BUILT_IN_UNREACHABLE)
|| !possible_polymorphic_call_target_p
- (ie, cgraph_node::get (target)))
- target = ipa_impossible_devirt_target (ie, target);
- return ipa_make_edge_direct_to_target (ie, target);
+ (ie, cgraph_node::get (t)))
+ {
+ /* Do not speculate builtin_unreachable, it is stpid! */
+ if (!ie->indirect_info->vptr_changed)
+ target = ipa_impossible_devirt_target (ie, target);
+ }
+ else
+ {
+ target = t;
+ speculative = ie->indirect_info->vptr_changed;
+ }
}
}
}
else
target = ipa_impossible_devirt_target (ie, NULL_TREE);
}
- else if (flag_devirtualize_speculatively
+ else if (!target && flag_devirtualize_speculatively
&& !ie->speculative && ie->maybe_hot_p ())
{
cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type,
if (target)
{
if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target)))
- target = ipa_impossible_devirt_target (ie, target);
+ {
+ if (!speculative)
+ return NULL;
+ target = ipa_impossible_devirt_target (ie, target);
+ }
return ipa_make_edge_direct_to_target (ie, target, speculative);
}
else
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->vptr_changed, 1);
streamer_write_bitpack (&bp);
if (ii->agg_contents || ii->polymorphic)
streamer_write_hwi (ob, ii->offset);
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->vptr_changed = bp_unpack_value (&bp, 1);
if (ii->agg_contents || ii->polymorphic)
ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
else