re PR ipa/65610 (Compare debug failure with -g3 -fsanitize=undefined -fno-sanitize=vptr -O3)
PR ipa/65610 * ipa-utils.h (inlined_polymorphic_ctor_dtor_block_p): Declare. * ipa-polymorphic-call.c (inlined_polymorphic_ctor_dtor_block_p): New function. (decl_maybe_in_construction_p, noncall_stmt_may_be_vtbl_ptr_store): Use it. * ipa-prop.c (param_type_may_change_p): Likewise. * tree-ssa-live.c: Include ipa-utils.h and its dependencies. (remove_unused_scope_block_p): Add in_ctor_dtor_block argument. Before inlining, preserve inlined_polymorphic_ctor_dtor_block_p blocks and the outermost block with FUNCTION_DECL BLOCK_ABSTRACT_ORIGIN inside of them. Adjust recursive calls. (remove_unused_locals): Adjust remove_unused_scope_block_p caller. * g++.dg/ubsan/pr65610.C: New test. From-SVN: r221781
This commit is contained in:
parent
ef4bac7802
commit
00a0ea64ee
|
@ -1,3 +1,20 @@
|
||||||
|
2015-03-30 Jakub Jelinek <jakub@redhat.com>
|
||||||
|
|
||||||
|
PR ipa/65610
|
||||||
|
* ipa-utils.h (inlined_polymorphic_ctor_dtor_block_p): Declare.
|
||||||
|
* ipa-polymorphic-call.c (inlined_polymorphic_ctor_dtor_block_p): New
|
||||||
|
function.
|
||||||
|
(decl_maybe_in_construction_p, noncall_stmt_may_be_vtbl_ptr_store):
|
||||||
|
Use it.
|
||||||
|
* ipa-prop.c (param_type_may_change_p): Likewise.
|
||||||
|
* tree-ssa-live.c: Include ipa-utils.h and its dependencies.
|
||||||
|
(remove_unused_scope_block_p): Add in_ctor_dtor_block
|
||||||
|
argument. Before inlining, preserve
|
||||||
|
inlined_polymorphic_ctor_dtor_block_p blocks and the outermost block
|
||||||
|
with FUNCTION_DECL BLOCK_ABSTRACT_ORIGIN inside of them. Adjust
|
||||||
|
recursive calls.
|
||||||
|
(remove_unused_locals): Adjust remove_unused_scope_block_p caller.
|
||||||
|
|
||||||
2015-03-27 Jan Hubicka <hubicka@ucw.cz>
|
2015-03-27 Jan Hubicka <hubicka@ucw.cz>
|
||||||
|
|
||||||
PR ipa/65076
|
PR ipa/65076
|
||||||
|
|
|
@ -513,6 +513,38 @@ contains_type_p (tree outer_type, HOST_WIDE_INT offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return a FUNCTION_DECL if BLOCK represents a constructor or destructor.
|
||||||
|
If CHECK_CLONES is true, also check for clones of ctor/dtors. */
|
||||||
|
|
||||||
|
tree
|
||||||
|
inlined_polymorphic_ctor_dtor_block_p (tree block, bool check_clones)
|
||||||
|
{
|
||||||
|
tree fn = BLOCK_ABSTRACT_ORIGIN (block);
|
||||||
|
if (fn == NULL || TREE_CODE (fn) != FUNCTION_DECL)
|
||||||
|
return NULL_TREE;
|
||||||
|
|
||||||
|
if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
|
||||||
|
|| (!DECL_CXX_CONSTRUCTOR_P (fn) && !DECL_CXX_DESTRUCTOR_P (fn)))
|
||||||
|
{
|
||||||
|
if (!check_clones)
|
||||||
|
return NULL_TREE;
|
||||||
|
|
||||||
|
/* Watch for clones where we constant propagated the first
|
||||||
|
argument (pointer to the instance). */
|
||||||
|
fn = DECL_ABSTRACT_ORIGIN (fn);
|
||||||
|
if (!fn
|
||||||
|
|| TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
|
||||||
|
|| (!DECL_CXX_CONSTRUCTOR_P (fn) && !DECL_CXX_DESTRUCTOR_P (fn)))
|
||||||
|
return NULL_TREE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
|
||||||
|
return NULL_TREE;
|
||||||
|
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* We know that the instance is stored in variable or parameter
|
/* We know that the instance is stored in variable or parameter
|
||||||
(not dynamically allocated) and we want to disprove the fact
|
(not dynamically allocated) and we want to disprove the fact
|
||||||
that it may be in construction at invocation of CALL.
|
that it may be in construction at invocation of CALL.
|
||||||
|
@ -550,30 +582,11 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
|
||||||
&& flags_from_decl_or_type (function) & (ECF_PURE | ECF_CONST))
|
&& flags_from_decl_or_type (function) & (ECF_PURE | ECF_CONST))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
bool check_clones = !base || is_global_var (base);
|
||||||
for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
|
for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
|
||||||
block = BLOCK_SUPERCONTEXT (block))
|
block = BLOCK_SUPERCONTEXT (block))
|
||||||
if (BLOCK_ABSTRACT_ORIGIN (block)
|
if (tree fn = inlined_polymorphic_ctor_dtor_block_p (block, check_clones))
|
||||||
&& TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
|
|
||||||
{
|
{
|
||||||
tree fn = BLOCK_ABSTRACT_ORIGIN (block);
|
|
||||||
|
|
||||||
if (TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
|
|
||||||
|| (!DECL_CXX_CONSTRUCTOR_P (fn)
|
|
||||||
&& !DECL_CXX_DESTRUCTOR_P (fn)))
|
|
||||||
{
|
|
||||||
/* Watch for clones where we constant propagated the first
|
|
||||||
argument (pointer to the instance). */
|
|
||||||
fn = DECL_ABSTRACT_ORIGIN (fn);
|
|
||||||
if (!fn
|
|
||||||
|| (base && !is_global_var (base))
|
|
||||||
|| TREE_CODE (TREE_TYPE (fn)) != METHOD_TYPE
|
|
||||||
|| (!DECL_CXX_CONSTRUCTOR_P (fn)
|
|
||||||
&& !DECL_CXX_DESTRUCTOR_P (fn)))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
tree type = TYPE_MAIN_VARIANT (method_class_type (TREE_TYPE (fn)));
|
tree type = TYPE_MAIN_VARIANT (method_class_type (TREE_TYPE (fn)));
|
||||||
|
|
||||||
if (!outer_type || !types_odr_comparable (type, outer_type))
|
if (!outer_type || !types_odr_comparable (type, outer_type))
|
||||||
|
@ -1163,15 +1176,7 @@ noncall_stmt_may_be_vtbl_ptr_store (gimple stmt)
|
||||||
block = BLOCK_SUPERCONTEXT (block))
|
block = BLOCK_SUPERCONTEXT (block))
|
||||||
if (BLOCK_ABSTRACT_ORIGIN (block)
|
if (BLOCK_ABSTRACT_ORIGIN (block)
|
||||||
&& TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
|
&& TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
|
||||||
{
|
return inlined_polymorphic_ctor_dtor_block_p (block, false);
|
||||||
tree fn = BLOCK_ABSTRACT_ORIGIN (block);
|
|
||||||
|
|
||||||
if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
|
|
||||||
return false;
|
|
||||||
return (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
|
|
||||||
&& (DECL_CXX_CONSTRUCTOR_P (fn)
|
|
||||||
|| DECL_CXX_DESTRUCTOR_P (fn)));
|
|
||||||
}
|
|
||||||
return (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE
|
return (TREE_CODE (TREE_TYPE (current_function_decl)) == METHOD_TYPE
|
||||||
&& (DECL_CXX_CONSTRUCTOR_P (current_function_decl)
|
&& (DECL_CXX_CONSTRUCTOR_P (current_function_decl)
|
||||||
|| DECL_CXX_DESTRUCTOR_P (current_function_decl)));
|
|| DECL_CXX_DESTRUCTOR_P (current_function_decl)));
|
||||||
|
|
|
@ -715,18 +715,8 @@ param_type_may_change_p (tree function, tree arg, gimple call)
|
||||||
/* Walk the inline stack and watch out for ctors/dtors. */
|
/* Walk the inline stack and watch out for ctors/dtors. */
|
||||||
for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
|
for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
|
||||||
block = BLOCK_SUPERCONTEXT (block))
|
block = BLOCK_SUPERCONTEXT (block))
|
||||||
if (BLOCK_ABSTRACT_ORIGIN (block)
|
if (inlined_polymorphic_ctor_dtor_block_p (block, false))
|
||||||
&& TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
|
return true;
|
||||||
{
|
|
||||||
tree fn = BLOCK_ABSTRACT_ORIGIN (block);
|
|
||||||
|
|
||||||
if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
|
|
||||||
continue;
|
|
||||||
if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
|
|
||||||
&& (DECL_CXX_CONSTRUCTOR_P (fn)
|
|
||||||
|| DECL_CXX_DESTRUCTOR_P (fn)))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ bool possible_polymorphic_call_target_p (tree, HOST_WIDE_INT,
|
||||||
const ipa_polymorphic_call_context &,
|
const ipa_polymorphic_call_context &,
|
||||||
struct cgraph_node *);
|
struct cgraph_node *);
|
||||||
tree method_class_type (const_tree);
|
tree method_class_type (const_tree);
|
||||||
|
tree inlined_polymorphic_ctor_dtor_block_p (tree, bool);
|
||||||
bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
|
bool decl_maybe_in_construction_p (tree, tree, gimple, tree);
|
||||||
tree vtable_pointer_value_to_binfo (const_tree);
|
tree vtable_pointer_value_to_binfo (const_tree);
|
||||||
bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *);
|
bool vtable_pointer_value_to_vtable (const_tree, tree *, unsigned HOST_WIDE_INT *);
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
2015-03-30 Jakub Jelinek <jakub@redhat.com>
|
||||||
|
|
||||||
|
PR ipa/65610
|
||||||
|
* g++.dg/ubsan/pr65610.C: New test.
|
||||||
|
|
||||||
2015-03-30 Marek Polacek <polacek@redhat.com>
|
2015-03-30 Marek Polacek <polacek@redhat.com>
|
||||||
|
|
||||||
PR c++/65398
|
PR c++/65398
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// PR ipa/65610
|
||||||
|
// { dg-do compile }
|
||||||
|
// { dg-options "-std=c++11 -fsanitize=undefined -fno-sanitize=vptr -fcompare-debug" }
|
||||||
|
|
||||||
|
class A;
|
||||||
|
class B {};
|
||||||
|
enum C { D };
|
||||||
|
class E;
|
||||||
|
class F;
|
||||||
|
class G;
|
||||||
|
class H
|
||||||
|
{
|
||||||
|
F m1 (const A &t) const;
|
||||||
|
G m2 () const;
|
||||||
|
};
|
||||||
|
class G {};
|
||||||
|
template <class S, class T>
|
||||||
|
class I;
|
||||||
|
template <class S, class T>
|
||||||
|
class J
|
||||||
|
{
|
||||||
|
friend class I <S,T>;
|
||||||
|
J <S,T> *j;
|
||||||
|
};
|
||||||
|
template <class S, class T>
|
||||||
|
struct I
|
||||||
|
{
|
||||||
|
virtual ~I ();
|
||||||
|
virtual void m3 (void *p) {}
|
||||||
|
J <S,T> *i;
|
||||||
|
void m4 (J <S,T>*& t);
|
||||||
|
};
|
||||||
|
template <class S, class T>
|
||||||
|
void I <S,T>::m4 (J <S,T> * &t)
|
||||||
|
{
|
||||||
|
m4 (t->j);
|
||||||
|
m3 (t);
|
||||||
|
}
|
||||||
|
template <class S, class T>
|
||||||
|
I <S,T>::~I ()
|
||||||
|
{
|
||||||
|
m4 (i);
|
||||||
|
}
|
||||||
|
struct F
|
||||||
|
{
|
||||||
|
explicit inline F (C v);
|
||||||
|
inline ~F ();
|
||||||
|
I <B, E> f;
|
||||||
|
};
|
||||||
|
inline F::F (C v) {}
|
||||||
|
inline F::~F () {}
|
||||||
|
F H::m1 (const A &t) const
|
||||||
|
{
|
||||||
|
F q (D);
|
||||||
|
G r = m2 ();
|
||||||
|
return q;
|
||||||
|
}
|
|
@ -76,6 +76,10 @@ along with GCC; see the file COPYING3. If not see
|
||||||
#include "diagnostic-core.h"
|
#include "diagnostic-core.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "tree-ssa.h"
|
#include "tree-ssa.h"
|
||||||
|
#include "lto-streamer.h"
|
||||||
|
#include "ipa-ref.h"
|
||||||
|
#include "cgraph.h"
|
||||||
|
#include "ipa-utils.h"
|
||||||
|
|
||||||
#ifdef ENABLE_CHECKING
|
#ifdef ENABLE_CHECKING
|
||||||
static void verify_live_on_entry (tree_live_info_p);
|
static void verify_live_on_entry (tree_live_info_p);
|
||||||
|
@ -509,12 +513,29 @@ mark_scope_block_unused (tree scope)
|
||||||
done by the inliner. */
|
done by the inliner. */
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
remove_unused_scope_block_p (tree scope)
|
remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
|
||||||
{
|
{
|
||||||
tree *t, *next;
|
tree *t, *next;
|
||||||
bool unused = !TREE_USED (scope);
|
bool unused = !TREE_USED (scope);
|
||||||
int nsubblocks = 0;
|
int nsubblocks = 0;
|
||||||
|
|
||||||
|
/* For ipa-polymorphic-call.c purposes, preserve blocks:
|
||||||
|
1) with BLOCK_ABSTRACT_ORIGIN of a ctor/dtor or their clones */
|
||||||
|
if (inlined_polymorphic_ctor_dtor_block_p (scope, true))
|
||||||
|
{
|
||||||
|
in_ctor_dtor_block = true;
|
||||||
|
unused = false;
|
||||||
|
}
|
||||||
|
/* 2) inside such blocks, the outermost block with BLOCK_ABSTRACT_ORIGIN
|
||||||
|
being a FUNCTION_DECL. */
|
||||||
|
else if (in_ctor_dtor_block
|
||||||
|
&& BLOCK_ABSTRACT_ORIGIN (scope)
|
||||||
|
&& TREE_CODE (BLOCK_ABSTRACT_ORIGIN (scope)) == FUNCTION_DECL)
|
||||||
|
{
|
||||||
|
in_ctor_dtor_block = false;
|
||||||
|
unused = false;
|
||||||
|
}
|
||||||
|
|
||||||
for (t = &BLOCK_VARS (scope); *t; t = next)
|
for (t = &BLOCK_VARS (scope); *t; t = next)
|
||||||
{
|
{
|
||||||
next = &DECL_CHAIN (*t);
|
next = &DECL_CHAIN (*t);
|
||||||
|
@ -594,7 +615,7 @@ remove_unused_scope_block_p (tree scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (t = &BLOCK_SUBBLOCKS (scope); *t ;)
|
for (t = &BLOCK_SUBBLOCKS (scope); *t ;)
|
||||||
if (remove_unused_scope_block_p (*t))
|
if (remove_unused_scope_block_p (*t, in_ctor_dtor_block))
|
||||||
{
|
{
|
||||||
if (BLOCK_SUBBLOCKS (*t))
|
if (BLOCK_SUBBLOCKS (*t))
|
||||||
{
|
{
|
||||||
|
@ -959,7 +980,7 @@ remove_unused_locals (void)
|
||||||
cfun->local_decls->truncate (dstidx);
|
cfun->local_decls->truncate (dstidx);
|
||||||
}
|
}
|
||||||
|
|
||||||
remove_unused_scope_block_p (DECL_INITIAL (current_function_decl));
|
remove_unused_scope_block_p (DECL_INITIAL (current_function_decl), false);
|
||||||
clear_unused_block_pointer ();
|
clear_unused_block_pointer ();
|
||||||
|
|
||||||
BITMAP_FREE (usedvars);
|
BITMAP_FREE (usedvars);
|
||||||
|
|
Loading…
Reference in New Issue