From patchwork Tue Jul 15 09:43:07 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Hubicka X-Patchwork-Id: 369909 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id D2F0B140081 for ; Tue, 15 Jul 2014 19:43:28 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; q=dns; s=default; b=E+hslq9Fk0qwoSF7M qefV5bAXr1egHCuPKUu5VRI0M7EROZY6fgp96bQERKm7v6M6hZLwumG/F+EibDAP H/u8k1cW900vRV9SBNsWTIrIM+WKuTHOY3dNWSfuysQfsZQrHQUXx/ycR2FrpqD6 Twhy+VJMdZZ7Jzqne6xOjlI0f4= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:references:mime-version :content-type:in-reply-to; s=default; bh=eSSMQy7XMXevkUyAYQ4a5bI UL/s=; b=QVi7a2GWdfvnYpFaCaz5LW7Ntc9T+xZQlLaHam41+YcNo82gCZ7tMsc M6buEMuFheZgMybmhuDkFUFUJtxhiAfP+t7a16Hbka/RK3//KUIy/+AkmPsmaTaX 3YDZs9NA9cksIbzkjCkAq3tSWbpyZQvvLgoO6XZL7M3JOnUvOuko= Received: (qmail 11014 invoked by alias); 15 Jul 2014 09:43:21 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 10992 invoked by uid 89); 15 Jul 2014 09:43:19 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.4 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: nikam.ms.mff.cuni.cz Received: from nikam.ms.mff.cuni.cz (HELO nikam.ms.mff.cuni.cz) (195.113.20.16) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Tue, 15 Jul 2014 09:43:11 +0000 Received: by nikam.ms.mff.cuni.cz (Postfix, from userid 16202) id 7C21F542B6F; Tue, 15 Jul 2014 11:43:07 +0200 (CEST) Date: Tue, 15 Jul 2014 11:43:07 +0200 From: Jan Hubicka To: Jan Hubicka Cc: Jason Merrill , gcc-patches@gcc.gnu.org Subject: Re: Strenghten assumption about dynamic type changes (placement new) Message-ID: <20140715094307.GA12811@kam.mff.cuni.cz> References: <20140702201805.GB15987@kam.mff.cuni.cz> <53B49446.7090402@redhat.com> <20140708135023.GC27691@kam.mff.cuni.cz> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20140708135023.GC27691@kam.mff.cuni.cz> User-Agent: Mutt/1.5.21 (2010-09-15) > > On 07/02/2014 01:18 PM, Jan Hubicka wrote: > > >We propagate types from places we know instances are created across pointers > > >passed to functions. Once non-POD type is created at a given memory location, > > >one can not change its type by placement_new into something else. > > > > Hmm. If the memory location is untyped (i.e. from malloc) or a > > character array, or a union, you can indeed destroy an object of one > > type and create an object of a different type in that location. > > > > >Jason, this assumes that one can not destroy the type and re-construct same > > >type at the same spot. > > > > That is an invalid assumption; you can destroy one object and > > construct a new one in the same location. Doing it within a method > > would be unusual, but I don't think there's a rule against it. > > > Jason, > I am looking into tracking dynamic types now. Obviously I need to set very > exact rules about when these may change. Can you take a few minutes and tell me > what of these sequences are valid? > > I think b variants are invalid, currently we also assume t1 to be invalid, but > t2 to be valid. > With placement news, I wonder if we can arrange them to do before return: > ptr = __builtin_placement_new (ptr) > this builtin would be folded away after IPA wwhen we no longer need to track > types same way as builtin_constant. That way I won't get two different dynamic > types mixed at one pointer location, since these will look as two pointers > until after inlining. But given that C++ makes placement new to be written by > hand, perhaps this is not possible? It would be useful to know rules in these testcases. I am attaching WIP patch for detecting dynamic type of heap allocated objects. It basically takes Martin's detect_type_change code from ipa-prop and adds discovery of constructor calls. I however need to know if I need to plan extra safe when propagating these types. > > #include > inline void* operator new(__SIZE_TYPE__, void* __p) throw() { return __p;} > > struct A > { > virtual void foo() {printf ("A\n");} > }; > struct B: A > { > virtual void foo() {printf ("B\n");} > }; > struct C: A > { > virtual void foo() {printf ("C\n");} > }; > > struct A * > type(struct B *a) > { > struct C *b; > ((struct B *)a)->~B(); > b = new (a) C; > return b; > } > struct A * > type_back(struct A *a) > { > struct B *b; > ((struct C *)a)->~C(); > b = new (a) B; > return b; > } > > void > t1() > { > struct B a; > struct A *b; > a.foo(); > b=type(&a); > b->foo(); > b=type_back (b); > a.foo(); > } > void > t1b() > { > struct B a; > a.foo(); > type(&a); > ((struct A *)&a)->foo(); > type_back (&a); > ((struct A *)&a)->foo(); > } > void > t2() > { > struct B *a = new (B); > struct A *b; > a->foo(); > b=type(a); > b->foo(); > } > void > t2b() > { > struct B *a = new (B); > struct A *b; > a->foo(); > type(a); > ((struct A *)a)->foo(); > } > main() > { > t1(); > t1b(); > t2(); > t2b(); > } Index: gimple-fold.c =================================================================== --- gimple-fold.c (revision 212546) +++ gimple-fold.c (working copy) @@ -372,7 +372,7 @@ tree val = OBJ_TYPE_REF_EXPR (rhs); if (is_gimple_min_invariant (val)) return val; - else if (flag_devirtualize && virtual_method_call_p (val)) + else if (flag_devirtualize && virtual_method_call_p (rhs)) { bool final; vec targets Index: ipa-devirt.c =================================================================== --- ipa-devirt.c (revision 212546) +++ ipa-devirt.c (working copy) @@ -2092,6 +2113,26 @@ return true; } +/* See if OP is SSA name initialized as a copy or by single assignment. + If so, walk the SSA graph up. */ + +static tree +walk_ssa_copies (tree op) +{ + STRIP_NOPS (op); + 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))) + { + 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; +} + /* Given REF call in FNDECL, determine class of the polymorphic call (OTR_TYPE), its token (OTR_TOKEN) and CONTEXT. CALL is optional argument giving the actual statement (usually call) where @@ -2120,16 +2161,9 @@ /* Walk SSA for outer object. */ do { - if (TREE_CODE (base_pointer) == SSA_NAME - && !SSA_NAME_IS_DEFAULT_DEF (base_pointer) - && SSA_NAME_DEF_STMT (base_pointer) - && gimple_assign_single_p (SSA_NAME_DEF_STMT (base_pointer))) + base_pointer = walk_ssa_copies (base_pointer); + if (TREE_CODE (base_pointer) == ADDR_EXPR) { - base_pointer = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (base_pointer)); - STRIP_NOPS (base_pointer); - } - else if (TREE_CODE (base_pointer) == ADDR_EXPR) - { HOST_WIDE_INT size, max_size; HOST_WIDE_INT offset2; tree base = get_ref_base_and_extent (TREE_OPERAND (base_pointer, 0), @@ -2174,7 +2208,7 @@ context->outer_type, call, current_function_decl); - return NULL; + return base_pointer; } else break; @@ -2264,6 +2298,402 @@ return base_pointer; } +/* Structure to be passed in between detect_type_change and + check_stmt_for_type_change. */ + +struct type_change_info +{ + /* Offset into the object where there is the virtual method pointer we are + looking for. */ + HOST_WIDE_INT offset; + /* The declaration or SSA_NAME pointer of the base that we are checking for + type change. */ + tree instance; + /* The reference to virtual table pointer used. */ + tree vtbl_ptr_ref; + tree otr_type; + /* 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; + HOST_WIDE_INT known_current_offset; + + /* 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. + + This function makes special assumptions about both constructors and + destructors which are all the functions that are allowed to alter the VMT + pointers. It assumes that destructors begin with assignment into all VMT + pointers and that constructors essentially look in the following way: + + 1) The very first thing they do is that they call constructors of ancestor + sub-objects that have them. + + 2) Then VMT pointers of this and all its ancestors is set to new values + corresponding to the type corresponding to the constructor. + + 3) Only afterwards, other stuff such as constructor of member sub-objects + and the code written by the user is run. Only this may include calling + virtual functions, directly or indirectly. + + There is no way to call a constructor of an ancestor sub-object in any + other way. + + This means that we do not have to care whether constructors get the correct + type information because they will always change it (in fact, if we define + the type to be given by the VMT pointer, it is undefined). + + The most important fact to derive from the above is that if, for some + statement in the section 3, we try to detect whether the dynamic type has + changed, we can safely ignore all calls as we examine the function body + backwards until we reach statements in section 2 because these calls cannot + be ancestor constructors or destructors (if the input is not bogus) and so + do not change the dynamic type (this holds true only for automatically + allocated objects but at the moment we devirtualize only these). We then + must detect that statements in section 2 change the dynamic type and can try + to derive the new type. That is enough and we can stop, we will never see + the calls into constructors of sub-objects in this code. Therefore we can + safely ignore all call statements that we traverse. + */ + +static bool +stmt_may_be_vtbl_ptr_store (gimple stmt) +{ + if (is_gimple_call (stmt)) + return false; + else if (is_gimple_assign (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + + if (gimple_clobber_p (stmt)) + return false; + if (!AGGREGATE_TYPE_P (TREE_TYPE (lhs))) + { + if (flag_strict_aliasing + && !POINTER_TYPE_P (TREE_TYPE (lhs))) + return false; + + if (TREE_CODE (lhs) == COMPONENT_REF + && !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1))) + return false; + /* In the future we might want to use get_base_ref_and_offset to find + if there is a field corresponding to the offset and if so, proceed + almost like if it was a component ref. */ + } + } + return true; +} + +/* If STMT can be proved to be an assignment to the virtual method table + pointer of ANALYZED_OBJ and the type associated with the new table + identified, return the type. Otherwise return NULL_TREE. */ + +static tree +extr_type_from_vtbl_ptr_store (gimple stmt, struct type_change_info *tci, + HOST_WIDE_INT *type_offset) +{ + HOST_WIDE_INT offset, size, max_size; + tree lhs, rhs, base, binfo; + + if (!gimple_assign_single_p (stmt)) + return NULL_TREE; + + lhs = gimple_assign_lhs (stmt); + rhs = gimple_assign_rhs1 (stmt); + if (TREE_CODE (lhs) != COMPONENT_REF + || !DECL_VIRTUAL_P (TREE_OPERAND (lhs, 1))) + return NULL_TREE; + + if (operand_equal_p (lhs, tci->vtbl_ptr_ref, 0)) + ; + else + { + base = get_ref_base_and_extent (lhs, &offset, &size, &max_size); + if (offset != tci->offset + || size != POINTER_SIZE + || max_size != POINTER_SIZE) + return NULL_TREE; + if (TREE_CODE (base) == MEM_REF) + { + if (!operand_equal_p (tci->instance, TREE_OPERAND (base, 0), 0) + || !integer_zerop (TREE_OPERAND (base, 1))) + return NULL_TREE; + } + else if (!operand_equal_p (tci->instance, base, 0) + || tci->offset) + return NULL_TREE; + } + + binfo = vtable_pointer_value_to_binfo (rhs); + + if (!binfo) + return NULL; + *type_offset = tree_to_shwi (BINFO_OFFSET (binfo)) * BITS_PER_UNIT; + if (TYPE_BINFO (BINFO_TYPE (binfo)) == binfo) + return BINFO_TYPE (binfo); + + /* TODO: Figure out the type containing BINFO. */ + debug_tree (binfo); + return NULL; +} + +/* Record dynamic type change of TCI to TYPE. */ + +void +record_known_type (struct type_change_info *tci, tree type, HOST_WIDE_INT offset) +{ + if (dump_file) + { + if (type) + { + fprintf (dump_file, " Recording type: "); + print_generic_expr (dump_file, type, TDF_SLIM); + fprintf (dump_file, " at offset %i\n", (int)offset); + } + else + fprintf (dump_file, " Recording unknown type\n"); + } + if (tci->type_maybe_changed + && (type != tci->known_current_type + || offset != tci->known_current_offset)) + tci->multiple_types_encountered = true; + tci->known_current_type = type; + tci->known_current_offset = offset; + tci->type_maybe_changed = true; +} + +/* Callback 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 + the (sub-)object. It stores its result into DATA, which points to a + type_change_info structure. */ + +static bool +check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data) +{ + gimple stmt = SSA_NAME_DEF_STMT (vdef); + struct type_change_info *tci = (struct type_change_info *) data; + tree fn; + + /* If we already gave up, just terminate the rest of walk. */ + if (tci->multiple_types_encountered) + return true; + + /* Check for a constructor call. */ + if (is_gimple_call (stmt) + && (fn = gimple_call_fndecl (stmt)) != NULL_TREE + && DECL_CXX_CONSTRUCTOR_P (fn) + && TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE + && gimple_call_num_args (stmt)) + { + tree op = walk_ssa_copies (gimple_call_arg (stmt, 0)); + tree type = method_class_type (TREE_TYPE (fn)); + HOST_WIDE_INT offset, size, max_size; + + if (dump_file) + { + fprintf (dump_file, " Checking constructor call: "); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + + if (TREE_CODE (op) == ADDR_EXPR) + { + op = get_ref_base_and_extent (TREE_OPERAND (op, 0), + &offset, &size, &max_size); + if (op && TREE_CODE (op) == MEM_REF) + { + if (!tree_fits_shwi_p (TREE_OPERAND (op, 1))) + return false; + offset += tree_to_shwi (TREE_OPERAND (op, 1)) + * BITS_PER_UNIT; + op = TREE_OPERAND (op, 0); + } + else + return false; + op = walk_ssa_copies (op); + } + if (operand_equal_p (op, tci->instance, 0) + && TYPE_SIZE (type) + && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST + && tree_fits_shwi_p (TYPE_SIZE (type)) + && tree_to_shwi (TYPE_SIZE (type)) + offset > tci->offset) + { + record_known_type (tci, type, tci->offset - offset); + return true; + } + } + + /* Check for inlined virtual table store. */ + if (stmt_may_be_vtbl_ptr_store (stmt)) + { + tree type; + HOST_WIDE_INT offset = 0; + if (dump_file) + { + fprintf (dump_file, " Checking vtbl store: "); + print_gimple_stmt (dump_file, stmt, 0, 0); + } + + type = extr_type_from_vtbl_ptr_store (stmt, tci, &offset); + gcc_assert (!type || TYPE_MAIN_VARIANT (type) == type); + record_known_type (tci, type, offset); + return true; + } + else + return false; +} + +/* CONTEXT is polymorphic call context obtained from get_polymorphic_context. + INSTANCE is pointer to the instance as returned by get_polymorphic_context. + If the type of instance is not fully determined (either OUTER_TYPE is unknown + or MAYBE_IN_CONSTRUCTION/INCLUDE_DERIVED_TYPES is set), try to walk memory + writes and find the actual construction of the instance. + + 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. */ + +bool +get_dynamic_type (tree instance, + ipa_polymorphic_call_context *context, + tree otr_type, + gimple call) +{ + struct type_change_info tci; + ao_ref ao; + bool function_entry_reached = false; + tree instance_ref = NULL; + gimple stmt = call; + + if (!context->maybe_in_construction && !context->maybe_derived_type) + return false; + + /* We need to obtain refernce to virtual table pointer. It is better + to look it up in the code rather than build our own. This require bit + of pattern matching, but we end up verifying that what we found is + correct. */ + if (gimple_code (call) == GIMPLE_CALL) + { + tree ref = gimple_call_fn (call); + HOST_WIDE_INT offset2, size, max_size; + + if (TREE_CODE (ref) == OBJ_TYPE_REF) + { + ref = OBJ_TYPE_REF_EXPR (ref); + ref = walk_ssa_copies (ref); + + /* Check if definition looks like vtable lookup. */ + if (TREE_CODE (ref) == SSA_NAME + && !SSA_NAME_IS_DEFAULT_DEF (ref) + && gimple_assign_load_p (SSA_NAME_DEF_STMT (ref)) + && TREE_CODE (gimple_assign_rhs1 + (SSA_NAME_DEF_STMT (ref))) == MEM_REF) + { + ref = get_base_address + (TREE_OPERAND (gimple_assign_rhs1 + (SSA_NAME_DEF_STMT (ref)), 0)); + ref = walk_ssa_copies (ref); + /* Find base address of the lookup and see if it looks like + vptr load. */ + if (TREE_CODE (ref) == SSA_NAME + && !SSA_NAME_IS_DEFAULT_DEF (ref) + && gimple_assign_load_p (SSA_NAME_DEF_STMT (ref))) + { + tree ref_exp = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (ref)); + tree base_ref = get_ref_base_and_extent (ref_exp, &offset2, &size, &max_size); + + /* Finally verify that what we found is based on INSTANCE. */ + if (base_ref + && TREE_CODE (base_ref) == MEM_REF + && TREE_OPERAND (base_ref, 0) == instance) + { + stmt = SSA_NAME_DEF_STMT (ref); + instance_ref = ref_exp; + } + } + } + } + } + + + /* If we failed to look up the refernece in code, build our own. */ + if (!instance_ref) + { + /* If the statement in question does not use memory, we can't tell + anything. */ + if (!gimple_vuse (stmt)) + return false; + instance_ref = build2 (MEM_REF, + TREE_TYPE (BINFO_VTABLE (TYPE_BINFO (otr_type))), + instance, + build_int_cst (ptr_type_node, + context->offset / BITS_PER_UNIT)); + } + + + ao_ref_init (&ao, instance_ref); + /*ao.base = instance_ref; + ao.offset = context->offset;*/ + ao.size = POINTER_SIZE; + ao.max_size = ao.size; + + if (dump_file) + { + fprintf (dump_file, "Determining dynamic type for call: "); + print_gimple_stmt (dump_file, call, 0, 0); + fprintf (dump_file, " Starting walk at: "); + print_gimple_stmt (dump_file, stmt, 0, 0); + fprintf (dump_file, " Instance pointer: "); + print_generic_expr (dump_file, instance, TDF_SLIM); + fprintf (dump_file, " offset: %i (bits)", (int)context->offset); + fprintf (dump_file, " vtbl reference: "); + print_generic_expr (dump_file, instance_ref, TDF_SLIM); + fprintf (dump_file, "\n"); + } + + tci.offset = context->offset; + tci.instance = instance; + tci.vtbl_ptr_ref = instance_ref; + gcc_assert (TREE_CODE (instance) != MEM_REF); + tci.known_current_type = NULL_TREE; + tci.known_current_offset = 0; + tci.otr_type = otr_type; + tci.type_maybe_changed = false; + tci.multiple_types_encountered = false; + + walk_aliased_vdefs (&ao, gimple_vuse (stmt), check_stmt_for_type_change, + &tci, NULL, &function_entry_reached); + if (!tci.type_maybe_changed) + { + if (context->maybe_in_construction) + context->maybe_in_construction = false; + if (dump_file) + fprintf (dump_file, " No dynamic type change found.\n"); + return true; + } + + if (tci.known_current_type + && !function_entry_reached + && !tci.multiple_types_encountered) + { + context->outer_type = tci.known_current_type; + context->offset = tci.known_current_offset; + context->maybe_in_construction = false; + context->maybe_derived_type = false; + if (dump_file) + fprintf (dump_file, " Determined dynamic type.\n"); + } + else if (dump_file) + fprintf (dump_file, " Found multiple types.\n"); + + return true; +} + /* Walk bases of OUTER_TYPE that contain OTR_TYPE at OFFSET. Lookup their respecitve virtual methods for OTR_TOKEN and OTR_TYPE and insert them to NODES. Index: ipa-prop.c =================================================================== --- ipa-prop.c (revision 212546) +++ ipa-prop.c (working copy) @@ -2134,7 +2134,36 @@ struct ipa_node_params *info = fbi->info; HOST_WIDE_INT offset; bool by_ref; + struct cgraph_edge *cs = cgraph_edge (cgraph_get_node (current_function_decl), call); + if (cs->indirect_info->polymorphic) + { + tree otr_type; + HOST_WIDE_INT otr_token; + ipa_polymorphic_call_context context; + tree instance; + tree target = gimple_call_fn (call); + + instance = get_polymorphic_call_info (current_function_decl, + target, + &otr_type, &otr_token, + &context, call); + + if (get_dynamic_type (instance, &context, otr_type, call)) + { + gcc_assert (TREE_CODE (otr_type) == RECORD_TYPE); + cs->indirect_info->polymorphic = true; + cs->indirect_info->param_index = -1; + cs->indirect_info->otr_token = otr_token; + cs->indirect_info->otr_type = otr_type; + cs->indirect_info->outer_type = context.outer_type; + cs->indirect_info->offset = context.offset; + cs->indirect_info->maybe_in_construction + = context.maybe_in_construction; + cs->indirect_info->maybe_derived_type = context.maybe_derived_type; + } + } + if (SSA_NAME_IS_DEFAULT_DEF (target)) { tree var = SSA_NAME_VAR (target); Index: ipa-utils.h =================================================================== --- ipa-utils.h (revision 212546) +++ ipa-utils.h (working copy) @@ -89,6 +89,7 @@ HOST_WIDE_INT *, ipa_polymorphic_call_context *, gimple call = NULL); +bool get_dynamic_type (tree, ipa_polymorphic_call_context *, tree, gimple); bool get_polymorphic_call_info_from_invariant (ipa_polymorphic_call_context *, tree, tree, HOST_WIDE_INT); bool decl_maybe_in_construction_p (tree, tree, gimple, tree); Index: tree-ssa-pre.c =================================================================== --- tree-ssa-pre.c (revision 212546) +++ tree-ssa-pre.c (working copy) @@ -63,6 +63,7 @@ #include "domwalk.h" #include "ipa-prop.h" #include "tree-ssa-propagate.h" +#include "ipa-utils.h" /* TODO: @@ -4359,12 +4360,34 @@ { tree fn = gimple_call_fn (stmt); if (fn - && TREE_CODE (fn) == OBJ_TYPE_REF - && TREE_CODE (OBJ_TYPE_REF_EXPR (fn)) == SSA_NAME) + && flag_devirtualize + && virtual_method_call_p (fn)) { - fn = ipa_intraprocedural_devirtualization (stmt); - if (fn && dbg_cnt (devirt)) + tree otr_type; + HOST_WIDE_INT otr_token; + ipa_polymorphic_call_context context; + tree instance; + bool final; + + instance = get_polymorphic_call_info (current_function_decl, + fn, + &otr_type, &otr_token, &context, stmt); + + get_dynamic_type (instance, &context, otr_type, stmt); + + vec targets + = possible_polymorphic_call_targets (obj_type_ref_class (fn), + tree_to_uhwi + (OBJ_TYPE_REF_TOKEN (fn)), + context, + &final); + if (final && targets.length () <= 1 && dbg_cnt (devirt)) { + tree fn; + if (targets.length () == 1) + fn = targets[0]->decl; + else + fn = builtin_decl_implicit (BUILT_IN_UNREACHABLE); if (dump_enabled_p ()) { location_t loc = gimple_location_safe (stmt); @@ -4376,6 +4399,8 @@ gimple_call_set_fndecl (stmt, fn); gimple_set_modified (stmt, true); } + else + gcc_assert (!ipa_intraprocedural_devirtualization (stmt)); } }