===================================================================
@@ -355,8 +355,17 @@ ipa_print_all_jump_functions (FILE *f)
struct type_change_info
{
+ /* The declaration or SSA_NAME pointer of the base that we are checking for
+ type change. */
+ tree object;
+ /* If we actually can tell the type that the object has changed to, it is
+ stored in this field. Otherwise it remains NULL_TREE. */
+ tree known_current_type;
/* Set to true if dynamic type change has been detected. */
bool type_maybe_changed;
+ /* Set to true if multiple types have been encountered. known_current_type
+ must be disregarded in that case. */
+ bool multiple_types_encountered;
};
/* Return true if STMT can modify a virtual method table pointer.
@@ -416,6 +425,49 @@ stmt_may_be_vtbl_ptr_store (gimple stmt)
return true;
}
+/* If STMT can be proved to be an assignment to the virtual method table
+ pointer of ANALYZED_OBJ and the type associated witht the new table
+ identified, return the type. Otherwise return NULL_TREE. */
+
+static tree
+extr_type_from_vtbl_ptr_store (gimple stmt, tree analyzed_obj)
+{
+ tree lhs, t, obj;
+
+ if (!is_gimple_assign (stmt))
+ return NULL_TREE;
+
+ lhs = gimple_assign_lhs (stmt);
+
+ if (TREE_CODE (lhs) != COMPONENT_REF)
+ return NULL_TREE;
+ obj = lhs;
+
+ if (!DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1)))
+ return NULL_TREE;
+
+ do
+ {
+ obj = TREE_OPERAND (obj, 0);
+ }
+ while (TREE_CODE (obj) == COMPONENT_REF);
+ if (TREE_CODE (obj) == MEM_REF)
+ obj = TREE_OPERAND (obj, 0);
+ if (TREE_CODE (obj) == ADDR_EXPR)
+ obj = TREE_OPERAND (obj, 0);
+ if (obj != analyzed_obj)
+ return NULL_TREE;
+
+ t = gimple_assign_rhs1 (stmt);
+ if (TREE_CODE (t) != ADDR_EXPR)
+ return NULL_TREE;
+ t = get_base_address (TREE_OPERAND (t, 0));
+ if (!t || TREE_CODE (t) != VAR_DECL || !DECL_VIRTUAL_P (t))
+ return NULL_TREE;
+
+ return DECL_CONTEXT (t);
+}
+
/* Callbeck of walk_aliased_vdefs and a helper function for
detect_type_change to check whether a particular statement may modify
the virtual table pointer, and if possible also determine the new type of
@@ -430,6 +482,12 @@ check_stmt_for_type_change (ao_ref *ao A
if (stmt_may_be_vtbl_ptr_store (stmt))
{
+ tree type;
+ type = extr_type_from_vtbl_ptr_store (stmt, tci->object);
+ if (tci->type_maybe_changed
+ && type != tci->known_current_type)
+ tci->multiple_types_encountered = true;
+ tci->known_current_type = type;
tci->type_maybe_changed = true;
return true;
}
@@ -449,6 +507,7 @@ detect_type_change (tree arg, tree base,
struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
{
struct type_change_info tci;
+ tree type;
ao_ref ao;
gcc_checking_assert (DECL_P (arg)
@@ -459,8 +518,6 @@ detect_type_change (tree arg, tree base,
if (!flag_devirtualize || !gimple_vuse (call))
return false;
- tci.type_maybe_changed = false;
-
ao.ref = arg;
ao.base = base;
ao.offset = offset;
@@ -469,12 +526,33 @@ detect_type_change (tree arg, tree base,
ao.ref_alias_set = -1;
ao.base_alias_set = -1;
+ type = TREE_TYPE (arg);
+ while (handled_component_p (arg))
+ arg = TREE_OPERAND (arg, 0);
+ if (TREE_CODE (arg) == MEM_REF)
+ arg = TREE_OPERAND (arg, 0);
+ if (TREE_CODE (arg) == ADDR_EXPR)
+ arg = TREE_OPERAND (arg, 0);
+ tci.object = arg;
+ tci.known_current_type = NULL_TREE;
+ tci.type_maybe_changed = false;
+ tci.multiple_types_encountered = false;
+
walk_aliased_vdefs (&ao, gimple_vuse (call), check_stmt_for_type_change,
&tci, NULL);
if (!tci.type_maybe_changed)
return false;
- jfunc->type = IPA_JF_UNKNOWN;
+ if (!tci.known_current_type
+ || tci.multiple_types_encountered
+ || offset != 0)
+ jfunc->type = IPA_JF_UNKNOWN;
+ else
+ {
+ jfunc->type = IPA_JF_KNOWN_TYPE;
+ jfunc->value.base_binfo = TYPE_BINFO (tci.known_current_type);
+ }
+
return true;
}
===================================================================
@@ -1,7 +1,7 @@
/* 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" } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */
extern "C" void abort (void);
@@ -69,3 +69,8 @@ int main (int argc, char *argv[])
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" } } */
===================================================================
@@ -1,7 +1,7 @@
/* 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" } */
+/* { dg-options "-O3 -fno-early-inlining -fno-inline -fdump-ipa-cp -fdump-tree-optimized" } */
extern "C" void abort (void);
@@ -77,3 +77,8 @@ int main (int argc, char *argv[])
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" } } */
===================================================================
@@ -2565,7 +2565,9 @@ struct function;
nodes, this points to either the FUNCTION_DECL for the containing
function, the RECORD_TYPE or UNION_TYPE for the containing type, or
NULL_TREE or a TRANSLATION_UNIT_DECL if the given decl has "file
- scope". */
+ scope". In particular, for VAR_DECLs which are virtual table pointers
+ (they have DECL_VIRTUAL set), we use DECL_CONTEXT to determine the type
+ they belong to. */
#define DECL_CONTEXT(NODE) (DECL_MINIMAL_CHECK (NODE)->decl_minimal.context)
#define DECL_FIELD_CONTEXT(NODE) \
(FIELD_DECL_CHECK (NODE)->decl_minimal.context)
===================================================================
@@ -0,0 +1,87 @@
+/* Verify that ipa-cp will not get confused by placement new constructing an
+ object within another one when looking for dynamic type change . */
+/* { dg-do run } */
+/* { dg-options "-O3" } */
+
+extern "C" void abort (void);
+namespace std {
+ typedef __SIZE_TYPE__ size_t;
+}
+inline void* __attribute__ ((always_inline))
+operator new(std::size_t, void* __p) throw()
+{
+ return __p;
+}
+
+class A
+{
+public:
+ char data[256];
+ A();
+ virtual int foo (int i);
+};
+
+class B : public A
+{
+public:
+ virtual int foo (int i);
+};
+
+class C
+{
+public:
+ C();
+ virtual double foo (double i);
+};
+
+int A::foo (int i)
+{
+ return i + 1;
+}
+
+int B::foo (int i)
+{
+ return i + 2;
+}
+
+double C::foo (double i)
+{
+ return i + 3.5;
+}
+
+static int __attribute__ ((noinline)) middleman (class A *obj, int i)
+{
+ return obj->foo (i);
+}
+
+int __attribute__ ((noinline,noclone)) get_input(void)
+{
+ return 1;
+}
+
+__attribute__ ((always_inline)) C::C ()
+{
+}
+
+A::A ()
+{
+}
+
+static __attribute__ ((noinline)) void bah ()
+{
+ class B b;
+
+ C *c = new ((void *) &b.data) C;
+
+ if (middleman (&b, get_input ()) != 3)
+ abort ();
+}
+
+int main (int argc, char *argv[])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ bah ();
+ return 0;
+}