This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH, PR 45934] Devirtualization aware of dynamic type changes
- From: Martin Jambor <mjambor at suse dot cz>
- To: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Cc: Jan Hubicka <hubicka at ucw dot cz>, Richard Guenther <rguenther at suse dot de>
- Date: Mon, 22 Nov 2010 23:05:56 +0100
- Subject: [PATCH, PR 45934] Devirtualization aware of dynamic type changes
Hi,
this patch fixes a wide range of issues including PR 45934 which are
caused by the fact that the dynamic type of object changes during
construction and destruction and devirtualization has to take these
changes into account and has to avoid creating calls to descendants'
implementation of a virtual functions when a constructor or a
destructor of an ancestor is running. This is achieved by doing two
things:
1) We cannot devirtualize OBJ_TYPE_REFs according to the static type
of the object during folding. This code was dumbed down to what gcc
4.5 does, which basically devirtualizes only when the O_T_R object is
a declaration - as opposed to an ancestor field within a declaration.
This is necessary to make g++.dg/opt/devirt1.C pass and is safe. Note
that we will want to fold O_T_Rs to their first parameter and we are
currently not that far from it.
Nevertheless, it also makes other testcases fail, specifically
g++.dg/otr-fold-1.C, g++.dg/otr-fold-2.C, g++.dg/tree-ssa/pr43411.C
and g++.dg/tree-ssa/pr45605.C. These testcases depend on type-based
devirtualization and I will post a followup (RFC) patch that deals
with them at -O2.
2) We can never devirtualize according to a type of a global object
because we do not know whether or not the current function has been
(perhaps indirectly) called from its constructor or destructor. This
also means that we cannot devirtualize according to types of constants
propagated by IPA-CP because those are always global object.
Therefore I removed code paths deriving types from constant IPA-CP
lattices and I check that declarations are not global when extracting
BINFOs from them.
This also means that testcases g++.dg/ipa/ipcp-ivi-1.C and
ivinline-6.C test something which cannot be safely done and therefore
I propose to remove them.
3) Whenever IPA_JF_KNOWN_TYPE, no-op IPA_JF_PASS_THROUGH or
IPA_JF_ANCESTOR jump functions are constructed, we also walk VDEFS and
look for preceeding code that actually changes its dynamic type by
assigning to its virtual method table pointer. From the RHS we can
then identify the new type of the object and construct an appropriate
IPA_JF_KNOWN_TYPE jump function instead.
This patch does not attempt to limit walking of VDEFs, I left that for
later. I am also not quite sure how to do that. During the body scan
that ipa-prop.c does to do modification analysis it could look for
assignments with a RHS that looks like a VMT address (address of an
array ref into a variable declaration which is DECL_VIRTUAL), and walk
VDEFS only if it finds one. The potentially inefficient VDEF walking
would then happen only in constructors, destructors and functions into
which they were inlined by early inlining (and we could punt for big
functions like that).
This patch also replaces all remaining uses of
gimple_get_relevant_ref_binfo with a combination of
get_base_ref_and_extent and get_binfo_at_offset because it is actually
convenient and it is easy to have the two binfo searching functions
diverge even though they should not.
I assume there will be comments and suggestions. Nevertheless, the
patch does pass bootstrap and regression tests (except for the
testcases described above) on x86_64-linux and also make check-c++ on
i686. Mind that it has to be applied on top of my fix for PR 46053
(http://gcc.gnu.org/ml/gcc-patches/2010-11/msg02291.html).
Thanks,
Martin
2010-11-19 Martin Jambor <mjambor@suse.cz>
PR tree-optimization/45934
* gimple-fold.c (get_base_binfo_for_type): Removed.
(gimple_get_relevant_ref_binfo): Likewise.
(gimple_fold_obj_type_ref_call): Dumb down to 4.5 functionality,
removed parameter inplace, updated the caller.
* ipa-cp.c (ipcp_propagate_types): Do not derive types from constants.
(ipcp_discover_new_direct_edges): Do not do devirtualization based on
constants.
* gimple.h (gimple_get_relevant_ref_binfo): Remove declaration.
* tree.c (get_binfo_at_offset): Get type from non-artificial fields.
* ipa-prop.c (check_type_change): New function.
(detect_type_change_1): Likewise.
(detect_type_change): Likewise.
(compute_complex_assign_jump_func): Check dynamic type change.
(compute_complex_ancestor_jump_func): Likewise.
(compute_scalar_jump_functions): Likewise.
(compute_known_type_jump_func): Likewise, use get_ref_base_and_extent
and get_binfo_at_offset instead of get_relevant_ref_binfo.
(ipa_analyze_node): Push and pop cfun, set current_function_decl.
(update_jump_functions_after_inlining): Do not derive types from
constants.
(try_make_edge_direct_virtual_call): Likewise.
* testsuite/g++.dg/ipa/devirt-c-1.C: New test.
* testsuite/g++.dg/ipa/devirt-c-2.C: Likewise.
* testsuite/g++.dg/ipa/devirt-c-3.C: Likewise.
* testsuite/g++.dg/ipa/devirt-c-4.C: Likewise.
* testsuite/g++.dg/ipa/devirt-c-5.C: Likewise.
* testsuite/g++.dg/ipa/devirt-d-1.C: Likewise.
* testsuite/g++.dg/torture/pr45934.C: Likewise.
The following removals are not part of the patch but I'll svn remove
them when commiting the patch if approved:
* testsuite/g++.dg/ipa/ipcp-ivi-1.C: Removed.
* testsuite/g++.dg/ipa/ivinline-6.C: Likewise.
Index: icln/gcc/gimple-fold.c
===================================================================
--- icln.orig/gcc/gimple-fold.c
+++ icln/gcc/gimple-fold.c
@@ -1360,88 +1360,6 @@ gimple_fold_builtin (gimple stmt)
return result;
}
-/* Search for a base binfo of BINFO that corresponds to TYPE and return it if
- it is found or NULL_TREE if it is not. */
-
-static tree
-get_base_binfo_for_type (tree binfo, tree type)
-{
- int i;
- tree base_binfo;
- tree res = NULL_TREE;
-
- for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
- if (TREE_TYPE (base_binfo) == type)
- {
- gcc_assert (!res);
- res = base_binfo;
- }
-
- return res;
-}
-
-/* Return a binfo describing the part of object referenced by expression REF.
- Return NULL_TREE if it cannot be determined. REF can consist of a series of
- COMPONENT_REFs of a declaration or of an INDIRECT_REF or it can also be just
- a simple declaration, indirect reference or an SSA_NAME. If the function
- discovers an INDIRECT_REF or an SSA_NAME, it will assume that the
- encapsulating type is described by KNOWN_BINFO, if it is not NULL_TREE.
- Otherwise the first non-artificial field declaration or the base declaration
- will be examined to get the encapsulating type. */
-
-tree
-gimple_get_relevant_ref_binfo (tree ref, tree known_binfo)
-{
- while (true)
- {
- if (TREE_CODE (ref) == COMPONENT_REF)
- {
- tree par_type;
- tree binfo;
- tree field = TREE_OPERAND (ref, 1);
-
- if (!DECL_ARTIFICIAL (field))
- {
- tree type = TREE_TYPE (field);
- if (TREE_CODE (type) == RECORD_TYPE)
- return TYPE_BINFO (type);
- else
- return NULL_TREE;
- }
-
- par_type = TREE_TYPE (TREE_OPERAND (ref, 0));
- binfo = TYPE_BINFO (par_type);
- if (!binfo
- || BINFO_N_BASE_BINFOS (binfo) == 0)
- return NULL_TREE;
-
- /* Offset 0 indicates the primary base, whose vtable contents are
- represented in the binfo for the derived class. */
- if (int_bit_position (field) != 0)
- {
- tree d_binfo;
-
- /* Get descendant binfo. */
- d_binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (ref, 0),
- known_binfo);
- if (!d_binfo)
- return NULL_TREE;
- return get_base_binfo_for_type (d_binfo, TREE_TYPE (field));
- }
-
- ref = TREE_OPERAND (ref, 0);
- }
- else if (DECL_P (ref) && TREE_CODE (TREE_TYPE (ref)) == RECORD_TYPE)
- return TYPE_BINFO (TREE_TYPE (ref));
- else if (known_binfo
- && (TREE_CODE (ref) == SSA_NAME
- || TREE_CODE (ref) == MEM_REF))
- return known_binfo;
- else
- return NULL_TREE;
- }
-}
-
/* Return a declaration of a function which an OBJ_TYPE_REF references. TOKEN
is integer form of OBJ_TYPE_REF_TOKEN of the reference expression.
KNOWN_BINFO carries the binfo describing the true type of
@@ -1525,7 +1443,7 @@ gimple_adjust_this_by_delta (gimple_stmt
INPLACE is false. Return true iff the statement was changed. */
static bool
-gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi, bool inplace)
+gimple_fold_obj_type_ref_call (gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
tree ref = gimple_call_fn (stmt);
@@ -1533,27 +1451,21 @@ gimple_fold_obj_type_ref_call (gimple_st
tree binfo, fndecl, delta;
HOST_WIDE_INT token;
- if (TREE_CODE (obj) == ADDR_EXPR)
- obj = TREE_OPERAND (obj, 0);
- else
+ if (TREE_CODE (obj) != ADDR_EXPR)
return false;
-
- binfo = gimple_get_relevant_ref_binfo (obj, NULL_TREE);
+ obj = TREE_OPERAND (obj, 0);
+ if (!DECL_P (obj)
+ || TREE_CODE (TREE_TYPE (obj)) != RECORD_TYPE)
+ return false;
+ binfo = TYPE_BINFO (TREE_TYPE (obj));
if (!binfo)
return false;
+
token = tree_low_cst (OBJ_TYPE_REF_TOKEN (ref), 1);
- fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
- !DECL_P (obj));
+ fndecl = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta, false);
if (!fndecl)
return false;
-
- if (integer_nonzerop (delta))
- {
- if (inplace)
- return false;
- gimple_adjust_this_by_delta (gsi, delta);
- }
-
+ gcc_checking_assert (integer_zerop (delta));
gimple_call_set_fndecl (stmt, fndecl);
return true;
}
@@ -1591,7 +1503,7 @@ gimple_fold_call (gimple_stmt_iterator *
here where we can just smash the call operand. */
callee = gimple_call_fn (stmt);
if (TREE_CODE (callee) == OBJ_TYPE_REF)
- return gimple_fold_obj_type_ref_call (gsi, inplace);
+ return gimple_fold_obj_type_ref_call (gsi);
}
return false;
Index: icln/gcc/ipa-prop.c
===================================================================
--- icln.orig/gcc/ipa-prop.c
+++ icln/gcc/ipa-prop.c
@@ -350,6 +350,107 @@ ipa_print_all_jump_functions (FILE *f)
}
}
+/* Helper function for detect_type_change_1 to check whether a particular
+ statement is indeed an assignment to the virtual table pointer. */
+
+static bool
+check_type_change (ao_ref *ao, tree vdef, void *data)
+{
+ gimple def_stmt = SSA_NAME_DEF_STMT (vdef);
+ tree t, l, b, *res = (tree *) data;
+
+ if (!is_gimple_assign (def_stmt))
+ return false;
+
+ t = gimple_assign_rhs1 (def_stmt);
+ if (TREE_CODE (t) != ADDR_EXPR)
+ return false;
+ t = TREE_OPERAND (t, 0);
+ if (TREE_CODE (t) == ARRAY_REF)
+ t = TREE_OPERAND (t, 0);
+ if (TREE_CODE (t) != VAR_DECL
+ || !DECL_VIRTUAL_P (t))
+ return false;
+
+ l = gimple_assign_lhs (def_stmt);
+ while (handled_component_p (l))
+ l = TREE_OPERAND (l, 0);
+ if (TREE_CODE (l) == MEM_REF)
+ l = TREE_OPERAND (l, 0);
+ b = ao->ref;
+ while (handled_component_p (b))
+ b = TREE_OPERAND (b, 0);
+ if (TREE_CODE (b) == MEM_REF)
+ b = TREE_OPERAND (b, 0);
+ if (l != b)
+ return false;
+
+ *res = DECL_CONTEXT (t);
+ 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. If
+ ANC_TYPE is non-NULL, look for binfo of its ancestor of that type at offset
+ ANC_OFFSET. ARG is supposed to be a dereferenced pointer or a
+ declaration. */
+
+static bool
+detect_type_change_1 (tree arg, gimple call, struct ipa_jump_func *jfunc,
+ tree anc_type, HOST_WIDE_INT anc_offset)
+{
+ tree res = NULL_TREE;
+ ao_ref ar;
+
+ /* Const calls cannot call virtual methods through VMT and so type changes do
+ not matter. */
+ if (!gimple_vuse (call))
+ return false;
+ ao_ref_init (&ar, arg);
+ walk_aliased_vdefs (&ar, gimple_vuse (call), check_type_change, &res, NULL);
+ if (!res)
+ return false;
+
+ if (anc_type)
+ {
+ tree new_binfo = get_binfo_at_offset (TYPE_BINFO (res), anc_offset,
+ anc_type);
+
+ if (new_binfo)
+ {
+ jfunc->type = IPA_JF_KNOWN_TYPE;
+ jfunc->value.base_binfo = new_binfo;
+ }
+ else
+ jfunc->type = IPA_JF_UNKNOWN;
+ }
+ else
+ {
+ jfunc->type = IPA_JF_KNOWN_TYPE;
+ jfunc->value.base_binfo = TYPE_BINFO (res);
+ }
+
+ return true;
+}
+
+/* Like detect_type_change_1 but ARG is supposed to be a non-dereferenced
+ pointer SSA name. */
+
+static bool
+detect_type_change (tree arg, gimple call, struct ipa_jump_func *jfunc,
+ tree anc_type, HOST_WIDE_INT anc_offset)
+{
+ if (!POINTER_TYPE_P (TREE_TYPE (arg))
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != RECORD_TYPE)
+ return false;
+ gcc_checking_assert (TREE_CODE (arg) != ADDR_EXPR);
+ arg = build_simple_mem_ref (arg);
+
+ return detect_type_change_1 (arg, call, jfunc, anc_type, anc_offset);
+}
+
+
/* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
of an assignment statement STMT, try to find out whether NAME can be
described by a (possibly polynomial) pass-through jump-function or an
@@ -359,10 +460,10 @@ ipa_print_all_jump_functions (FILE *f)
static void
compute_complex_assign_jump_func (struct ipa_node_params *info,
struct ipa_jump_func *jfunc,
- gimple stmt, tree name)
+ gimple call, gimple stmt, tree name)
{
HOST_WIDE_INT offset, size, max_size;
- tree op1, op2, type;
+ tree op1, op2, base, type;
int index;
op1 = gimple_assign_rhs1 (stmt);
@@ -388,7 +489,8 @@ compute_complex_assign_jump_func (struct
jfunc->value.pass_through.operation = gimple_assign_rhs_code (stmt);
jfunc->value.pass_through.operand = op2;
}
- else if (gimple_assign_unary_nop_p (stmt))
+ else if (gimple_assign_unary_nop_p (stmt)
+ && !detect_type_change (op1, call, jfunc, NULL_TREE, 0))
{
jfunc->type = IPA_JF_PASS_THROUGH;
jfunc->value.pass_through.formal_id = index;
@@ -404,21 +506,22 @@ compute_complex_assign_jump_func (struct
type = TREE_TYPE (op1);
if (TREE_CODE (type) != RECORD_TYPE)
return;
- op1 = get_ref_base_and_extent (op1, &offset, &size, &max_size);
- if (TREE_CODE (op1) != MEM_REF
+ base = get_ref_base_and_extent (op1, &offset, &size, &max_size);
+ if (TREE_CODE (base) != MEM_REF
/* If this is a varying address, punt. */
|| max_size == -1
|| max_size != size)
return;
- offset += mem_ref_offset (op1).low * BITS_PER_UNIT;
- op1 = TREE_OPERAND (op1, 0);
- if (TREE_CODE (op1) != SSA_NAME
- || !SSA_NAME_IS_DEFAULT_DEF (op1)
+ offset += mem_ref_offset (base).low * BITS_PER_UNIT;
+ base = TREE_OPERAND (base, 0);
+ if (TREE_CODE (base) != SSA_NAME
+ || !SSA_NAME_IS_DEFAULT_DEF (base)
|| offset < 0)
return;
- index = ipa_get_param_decl_index (info, SSA_NAME_VAR (op1));
- if (index >= 0)
+ /* Dynamic types are changed only in constructors and destructors and */
+ index = ipa_get_param_decl_index (info, SSA_NAME_VAR (base));
+ if (index >= 0 && !detect_type_change_1 (op1, call, jfunc, type, offset))
{
jfunc->type = IPA_JF_ANCESTOR;
jfunc->value.ancestor.formal_id = index;
@@ -452,19 +555,23 @@ compute_complex_assign_jump_func (struct
static void
compute_complex_ancestor_jump_func (struct ipa_node_params *info,
struct ipa_jump_func *jfunc,
- gimple phi)
+ gimple call, gimple phi)
{
HOST_WIDE_INT offset, size, max_size;
gimple assign, cond;
basic_block phi_bb, assign_bb, cond_bb;
- tree tmp, parm, expr;
+ tree tmp, parm, expr, anc_type;
int index, i;
- if (gimple_phi_num_args (phi) != 2
- || !integer_zerop (PHI_ARG_DEF (phi, 1)))
+ if (gimple_phi_num_args (phi) != 2)
return;
- tmp = PHI_ARG_DEF (phi, 0);
+ if (integer_zerop (PHI_ARG_DEF (phi, 1)))
+ tmp = PHI_ARG_DEF (phi, 0);
+ else if (integer_zerop (PHI_ARG_DEF (phi, 0)))
+ tmp = PHI_ARG_DEF (phi, 1);
+ else
+ return;
if (TREE_CODE (tmp) != SSA_NAME
|| SSA_NAME_IS_DEFAULT_DEF (tmp)
|| !POINTER_TYPE_P (TREE_TYPE (tmp))
@@ -508,7 +615,6 @@ compute_complex_ancestor_jump_func (stru
|| !integer_zerop (gimple_cond_rhs (cond)))
return;
-
phi_bb = gimple_bb (phi);
for (i = 0; i < 2; i++)
{
@@ -517,10 +623,14 @@ compute_complex_ancestor_jump_func (stru
return;
}
- jfunc->type = IPA_JF_ANCESTOR;
- jfunc->value.ancestor.formal_id = index;
- jfunc->value.ancestor.offset = offset;
- jfunc->value.ancestor.type = TREE_TYPE (TREE_TYPE (tmp));
+ anc_type = TREE_TYPE (TREE_TYPE (tmp));
+ if (!detect_type_change (parm, call, jfunc, anc_type, offset))
+ {
+ jfunc->type = IPA_JF_ANCESTOR;
+ jfunc->value.ancestor.formal_id = index;
+ jfunc->value.ancestor.offset = offset;
+ jfunc->value.ancestor.type = anc_type;
+ }
}
/* Given OP whch is passed as an actual argument to a called function,
@@ -528,15 +638,32 @@ compute_complex_ancestor_jump_func (stru
and if so, create one and store it to JFUNC. */
static void
-compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc)
+compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc,
+ gimple call)
{
- tree binfo;
+ HOST_WIDE_INT offset, size, max_size;
+ tree base, binfo;
- if (TREE_CODE (op) != ADDR_EXPR)
+ if (TREE_CODE (op) != ADDR_EXPR
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
return;
op = TREE_OPERAND (op, 0);
- binfo = gimple_get_relevant_ref_binfo (op, NULL_TREE);
+ base = get_ref_base_and_extent (op, &offset, &size, &max_size);
+ if (!DECL_P (base)
+ || max_size == -1
+ || max_size != size
+ || TREE_CODE (TREE_TYPE (base)) != RECORD_TYPE
+ || is_global_var (base))
+ return;
+
+ if (detect_type_change_1 (op, call, jfunc, TREE_TYPE (op), offset))
+ return;
+
+ binfo = TYPE_BINFO (TREE_TYPE (base));
+ if (!binfo)
+ return;
+ binfo = get_binfo_at_offset (binfo, offset, TREE_TYPE (op));
if (binfo)
{
jfunc->type = IPA_JF_KNOWN_TYPE;
@@ -574,7 +701,9 @@ compute_scalar_jump_functions (struct ip
{
int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg));
- if (index >= 0)
+ if (index >= 0
+ && !detect_type_change (arg, call, &functions[num],
+ NULL_TREE, 0))
{
functions[num].type = IPA_JF_PASS_THROUGH;
functions[num].value.pass_through.formal_id = index;
@@ -586,14 +715,14 @@ compute_scalar_jump_functions (struct ip
gimple stmt = SSA_NAME_DEF_STMT (arg);
if (is_gimple_assign (stmt))
compute_complex_assign_jump_func (info, &functions[num],
- stmt, arg);
+ call, stmt, arg);
else if (gimple_code (stmt) == GIMPLE_PHI)
compute_complex_ancestor_jump_func (info, &functions[num],
- stmt);
+ call, stmt);
}
}
else
- compute_known_type_jump_func (arg, &functions[num]);
+ compute_known_type_jump_func (arg, &functions[num], call);
}
}
@@ -1346,6 +1475,9 @@ ipa_analyze_node (struct cgraph_node *no
struct param_analysis_info *parms_info;
int i, param_count;
+ push_cfun (DECL_STRUCT_FUNCTION (node->decl));
+ current_function_decl = node->decl;
+
ipa_initialize_node_params (node);
param_count = ipa_get_param_count (info);
@@ -1358,6 +1490,9 @@ ipa_analyze_node (struct cgraph_node *no
for (i = 0; i < param_count; i++)
if (parms_info[i].visited_statements)
BITMAP_FREE (parms_info[i].visited_statements);
+
+ current_function_decl = NULL;
+ pop_cfun ();
}
@@ -1416,17 +1551,6 @@ update_jump_functions_after_inlining (st
src = ipa_get_ith_jump_func (top, dst->value.ancestor.formal_id);
if (src->type == IPA_JF_KNOWN_TYPE)
combine_known_type_and_ancestor_jfs (src, dst);
- else if (src->type == IPA_JF_CONST)
- {
- struct ipa_jump_func kt_func;
-
- kt_func.type = IPA_JF_UNKNOWN;
- compute_known_type_jump_func (src->value.constant, &kt_func);
- if (kt_func.type == IPA_JF_KNOWN_TYPE)
- combine_known_type_and_ancestor_jfs (&kt_func, dst);
- else
- dst->type = IPA_JF_UNKNOWN;
- }
else if (src->type == IPA_JF_PASS_THROUGH
&& src->value.pass_through.operation == NOP_EXPR)
dst->value.ancestor.formal_id = src->value.pass_through.formal_id;
@@ -1539,15 +1663,6 @@ try_make_edge_direct_virtual_call (struc
if (jfunc->type == IPA_JF_KNOWN_TYPE)
binfo = jfunc->value.base_binfo;
- else if (jfunc->type == IPA_JF_CONST)
- {
- tree cst = jfunc->value.constant;
- if (TREE_CODE (cst) == ADDR_EXPR)
- binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
- NULL_TREE);
- else
- return NULL;
- }
else
return NULL;
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-1.C
@@ -0,0 +1,76 @@
+/* Verify that ipa-cp correctly detects the dynamic type of an object
+ under construction when doing devirtualization. */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */
+
+extern "C" void abort (void);
+
+class A
+{
+public:
+ int data;
+ A();
+ virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+ virtual int foo (int i);
+};
+
+class C : public A
+{
+public:
+ virtual int foo (int i);
+};
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+int C::foo (int i)
+{
+ return i + 3;
+}
+
+static int middleman (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+A::A ()
+{
+ if (middleman (this, get_input ()) != 2)
+ abort ();
+}
+
+static void bah ()
+{
+ class B b;
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ bah ();
+ return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo" "cp" } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-2.C
@@ -0,0 +1,84 @@
+/* Verify that ipa-cp correctly detects the dynamic type of an object
+ under construction when doing devirtualization. */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */
+
+extern "C" void abort (void);
+
+class Distraction
+{
+public:
+ float f;
+ double d;
+ Distraction ()
+ {
+ f = 8.3;
+ d = 10.2;
+ }
+ virtual float bar (float z);
+};
+
+class A
+{
+public:
+ int data;
+ A();
+ virtual int foo (int i);
+};
+
+class B : public Distraction, public A
+{
+public:
+ virtual int foo (int i);
+};
+
+float Distraction::bar (float z)
+{
+ f += z;
+ return f/2;
+}
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+static int middleman (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+A::A()
+{
+ if (middleman (this, get_input ()) != 2)
+ abort ();
+}
+
+static void bah ()
+{
+ class B b;
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ bah ();
+ return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo" "cp" } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-3.C
@@ -0,0 +1,85 @@
+/* Verify that ipa-cp correctly detects the dynamic type of an object
+ under construction when doing devirtualization. */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */
+
+extern "C" void abort (void);
+
+class Distraction
+{
+public:
+ float f;
+ double d;
+ Distraction ()
+ {
+ f = 8.3;
+ d = 10.2;
+ }
+ virtual float bar (float z);
+};
+
+class A
+{
+public:
+ int data;
+ A();
+ virtual int foo (int i);
+};
+
+class B : public Distraction, public A
+{
+public:
+ virtual int foo (int i);
+};
+
+float Distraction::bar (float z)
+{
+ f += z;
+ return f/2;
+}
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+static int __attribute__ ((noinline))
+middleman (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+inline __attribute__ ((always_inline)) A::A()
+{
+ if (middleman (this, get_input ()) != 2)
+ abort ();
+}
+
+static void bah ()
+{
+ class B b;
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ bah ();
+ return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo" "cp" } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-4.C
@@ -0,0 +1,113 @@
+/* Verify that ipa-cp correctly detects the dynamic type of an object
+ under construction when doing devirtualization. */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-inline -fdump-tree-optimized" } */
+
+extern "C" void abort (void);
+
+class Distraction
+{
+public:
+ float f;
+ double d;
+ Distraction ()
+ {
+ f = 8.3;
+ d = 10.2;
+ }
+ virtual float bar (float z);
+};
+
+class A
+{
+public:
+ int data;
+ A();
+ virtual int foo (int i);
+};
+
+class B : public Distraction, public A
+{
+public:
+ B();
+ virtual int foo (int i);
+};
+
+class C : public B
+{
+public:
+ virtual int foo (int i);
+};
+
+
+float Distraction::bar (float z)
+{
+ f += z;
+ return f/2;
+}
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+int C::foo (int i)
+{
+ return i + 3;
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+static int __attribute__ ((noinline))
+middleman (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+static void __attribute__ ((noinline))
+sth2 (A *a)
+{
+ if (a->foo (get_input ()) != 3)
+ abort ();
+}
+
+inline void __attribute__ ((always_inline)) sth1 (B *b)
+{
+ sth2 (b);
+}
+
+inline __attribute__ ((always_inline)) A::A()
+{
+ if (middleman (this, get_input ()) != 2)
+ abort ();
+}
+
+B::B() : Distraction(), A()
+{
+ sth1 (this);
+}
+
+static void bah ()
+{
+ class C c;
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ bah ();
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-d-1.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-d-1.C
@@ -0,0 +1,76 @@
+/* Verify that ipa-cp correctly detects the dynamic type of an object
+ under destruction when doing devirtualization. */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */
+
+extern "C" void abort (void);
+
+class A
+{
+public:
+ int data;
+ ~A();
+ virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+ virtual int foo (int i);
+};
+
+class C : public A
+{
+public:
+ virtual int foo (int i);
+};
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+int C::foo (int i)
+{
+ return i + 3;
+}
+
+static int middleman (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+A::~A ()
+{
+ if (middleman (this, get_input ()) != 2)
+ abort ();
+}
+
+static void bah ()
+{
+ class B b;
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ bah ();
+ return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*A::foo" "cp" } } */
+/* { dg-final { scan-tree-dump-times "OBJ_TYPE_REF" 0 "optimized"} } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: icln/gcc/testsuite/g++.dg/torture/pr45934.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/torture/pr45934.C
@@ -0,0 +1,23 @@
+/* { dg-do run } */
+
+extern "C" void abort ();
+
+struct B *b;
+
+struct B
+{
+ virtual void f () { }
+ ~B() { b->f(); }
+};
+
+struct D : public B
+{
+ virtual void f () { abort (); }
+};
+
+int main ()
+{
+ D d;
+ b = &d;
+ return 0;
+}
Index: icln/gcc/tree.c
===================================================================
--- icln.orig/gcc/tree.c
+++ icln/gcc/tree.c
@@ -10949,8 +10949,7 @@ get_binfo_at_offset (tree binfo, HOST_WI
if (type == expected_type)
return binfo;
- if (TREE_CODE (type) != RECORD_TYPE
- || offset < 0)
+ if (offset < 0)
return NULL_TREE;
for (fld = TYPE_FIELDS (type); fld; fld = DECL_CHAIN (fld))
@@ -10963,12 +10962,18 @@ get_binfo_at_offset (tree binfo, HOST_WI
if (pos <= offset && (pos + size) > offset)
break;
}
- if (!fld || !DECL_ARTIFICIAL (fld))
+ if (!fld || TREE_CODE (TREE_TYPE (fld)) != RECORD_TYPE)
return NULL_TREE;
+ if (!DECL_ARTIFICIAL (fld))
+ {
+ binfo = TYPE_BINFO (TREE_TYPE (fld));
+ if (!binfo)
+ return NULL_TREE;
+ }
/* Offset 0 indicates the primary base, whose vtable contents are
represented in the binfo for the derived class. */
- if (offset != 0)
+ else if (offset != 0)
{
tree base_binfo, found_binfo = NULL_TREE;
for (i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
Index: icln/gcc/gimple.h
===================================================================
--- icln.orig/gcc/gimple.h
+++ icln/gcc/gimple.h
@@ -895,7 +895,6 @@ unsigned get_gimple_rhs_num_ops (enum tr
gimple gimple_alloc_stat (enum gimple_code, unsigned MEM_STAT_DECL);
const char *gimple_decl_printable_name (tree, int);
bool gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace);
-tree gimple_get_relevant_ref_binfo (tree ref, tree known_binfo);
tree gimple_get_virt_mehtod_for_binfo (HOST_WIDE_INT, tree, tree *, bool);
void gimple_adjust_this_by_delta (gimple_stmt_iterator *, tree);
/* Returns true iff T is a valid GIMPLE statement. */
Index: icln/gcc/ipa-cp.c
===================================================================
--- icln.orig/gcc/ipa-cp.c
+++ icln/gcc/ipa-cp.c
@@ -781,26 +781,16 @@ ipcp_propagate_types (struct ipa_node_pa
struct ipa_node_params *callee_info,
struct ipa_jump_func *jf, int i)
{
- tree cst, binfo;
-
switch (jf->type)
{
case IPA_JF_UNKNOWN:
case IPA_JF_CONST_MEMBER_PTR:
+ case IPA_JF_CONST:
break;
case IPA_JF_KNOWN_TYPE:
return ipcp_add_param_type (callee_info, i, jf->value.base_binfo);
- case IPA_JF_CONST:
- cst = jf->value.constant;
- if (TREE_CODE (cst) != ADDR_EXPR)
- break;
- binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0), NULL_TREE);
- if (!binfo)
- break;
- return ipcp_add_param_type (callee_info, i, binfo);
-
case IPA_JF_PASS_THROUGH:
case IPA_JF_ANCESTOR:
return ipcp_copy_types (caller_info, callee_info, i, jf);
@@ -1292,35 +1282,13 @@ ipcp_discover_new_direct_edges (struct c
for (ie = node->indirect_calls; ie; ie = next_ie)
{
struct cgraph_indirect_call_info *ici = ie->indirect_info;
- tree target, delta = NULL_TREE;
next_ie = ie->next_callee;
- if (ici->param_index != index)
+ if (ici->param_index != index
+ || ici->polymorphic)
continue;
- if (ici->polymorphic)
- {
- tree binfo;
- HOST_WIDE_INT token;
-
- if (TREE_CODE (cst) != ADDR_EXPR)
- continue;
-
- binfo = gimple_get_relevant_ref_binfo (TREE_OPERAND (cst, 0),
- NULL_TREE);
- if (!binfo)
- continue;
- gcc_assert (ie->indirect_info->anc_offset == 0);
- token = ie->indirect_info->otr_token;
- target = gimple_get_virt_mehtod_for_binfo (token, binfo, &delta,
- true);
- if (!target)
- continue;
- }
- else
- target = cst;
-
- ipa_make_edge_direct_to_target (ie, target, delta);
+ ipa_make_edge_direct_to_target (ie, cst, NULL_TREE);
}
}
Index: icln/gcc/testsuite/g++.dg/ipa/devirt-c-5.C
===================================================================
--- /dev/null
+++ icln/gcc/testsuite/g++.dg/ipa/devirt-c-5.C
@@ -0,0 +1,82 @@
+/* Verify that ipa-cp correctly detects the dynamic type of an object
+ under construction when doing devirtualization. */
+/* { dg-do run } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp" } */
+
+extern "C" void abort (void);
+
+class B;
+
+class A
+{
+public:
+ int data;
+ A();
+ A(B *b);
+ virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+ virtual int foo (int i);
+};
+
+class C : public A
+{
+public:
+ virtual int foo (int i);
+};
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+int C::foo (int i)
+{
+ return i + 3;
+}
+
+static int middleman (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+A::A ()
+{
+}
+
+A::A (B *b)
+{
+ if (middleman (b, get_input ()) != 3)
+ abort ();
+}
+
+static void bah ()
+{
+ B b;
+ A a(&b);
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ bah ();
+ return 0;
+}
+
+/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target.*B::foo" "cp" } } */
+/* { dg-final { cleanup-ipa-dump "cp" } } */