From patchwork Wed Dec 1 20:16:20 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Jambor X-Patchwork-Id: 73903 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]) by ozlabs.org (Postfix) with SMTP id 5A8F0B6F14 for ; Thu, 2 Dec 2010 07:23:24 +1100 (EST) Received: (qmail 20646 invoked by alias); 1 Dec 2010 20:23:05 -0000 Received: (qmail 20387 invoked by uid 22791); 1 Dec 2010 20:22:56 -0000 X-SWARE-Spam-Status: No, hits=-6.0 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, TW_FN, TW_JF, TW_TM X-Spam-Check-By: sourceware.org Received: from cantor.suse.de (HELO mx1.suse.de) (195.135.220.2) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 01 Dec 2010 20:22:43 +0000 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.221.2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.suse.de (Postfix) with ESMTP id C642A966A7 for ; Wed, 1 Dec 2010 21:22:40 +0100 (CET) Resent-From: Martin Jambor Resent-Date: Wed, 1 Dec 2010 21:22:40 +0100 Resent-Message-ID: <20101201202240.GC18215@virgil.arch.suse.de> Resent-To: GCC Patches Message-Id: <20101201201710.811179528@virgil.suse.cz> User-Agent: quilt/0.48-4.4 Date: Wed, 01 Dec 2010 21:16:20 +0100 From: Martin Jambor To: GCC Patches Cc: Richard Guenther , Jan Hubicka Subject: [PATCH, PR 45934 2/6] Remove devirtualizations that cannot be done References: <20101201201618.861802217@virgil.suse.cz> Content-Disposition: inline; filename=remove_impossible_devirt.diff X-IsSubscribed: yes 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 This patch removes parts of the current devirtualization machinery that cannot work because of potential dynamic type change which might be under way during sub-object construction and destruction. In the general case we simply cannot know this about global objects and we do not want to proceed with the necessary detection when folding. So devirtualization based on global decls, global IPA-CP constants is being removed and devirtualization in folding is dumbed down to never work on sub-objects. This patch removes the two testcases that test devirtualization based on global variables. After this patch, g++.dg/otr-fold-[12].C, g++.dg/tree-ssa/pr43411.C and g++.dg/tree-ssa/pr45605.C fail, this is addressed by the very last patch in the series. Bootstrapped and tested separately on x86-64-linux, I think I also did make check-c++ on i686 but I am no longer really sure, it certainly passed along with the rest. Thanks, Martin 2010-11-30 Martin Jambor PR tree-optimization/45934 PR tree-optimization/46302 * 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. * gimple.h (gimple_get_relevant_ref_binfo): Remove declaration. * ipa-cp.c (ipcp_propagate_types): Do not derive types from constants. (ipcp_discover_new_direct_edges): Do not do devirtualization based on constants. * ipa-prop.c (mem_ref_offset): New variable base to store the result of get_ref_base_and_extent. (compute_known_type_jump_func): Use get_ref_base_and_extent and get_binfo_at_offset instead of gimple_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. * tree.c (get_binfo_at_offset): Get type from non-artificial fields. * 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_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/gimple.h =================================================================== --- icln.orig/gcc/gimple.h +++ icln/gcc/gimple.h @@ -892,7 +892,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/ipa-prop.c =================================================================== --- icln.orig/gcc/ipa-prop.c +++ icln/gcc/ipa-prop.c @@ -362,7 +362,7 @@ compute_complex_assign_jump_func (struct 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); @@ -404,20 +404,21 @@ 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)); + /* Dynamic types are changed only in constructors and destructors and */ + index = ipa_get_param_decl_index (info, SSA_NAME_VAR (base)); if (index >= 0) { jfunc->type = IPA_JF_ANCESTOR; @@ -530,13 +531,26 @@ compute_complex_ancestor_jump_func (stru static void compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc) { - 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; + + 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; @@ -1346,6 +1360,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 +1375,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 +1436,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 +1548,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/tree.c =================================================================== --- icln.orig/gcc/tree.c +++ icln/gcc/tree.c @@ -10950,8 +10950,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)) @@ -10964,12 +10963,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/testsuite/g++.dg/ipa/ipcp-ivi-1.C =================================================================== --- icln.orig/gcc/testsuite/g++.dg/ipa/ipcp-ivi-1.C +++ /dev/null @@ -1,65 +0,0 @@ -/* Verify that simple virtual calls are inlined even without early - inlining. */ -/* { dg-do run } */ -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining" } */ - -extern "C" void abort (void); - -class A -{ -public: - int data; - 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; -} - -int __attribute__ ((noinline)) middleman (class A *obj, int i) -{ - return obj->foo (i); -} - -int __attribute__ ((noinline,noclone)) get_input(void) -{ - return 1; -} - -class B b; - -int main (int argc, char *argv[]) -{ - int i; - - for (i = 0; i < get_input (); i++) - if (middleman (&b, get_input ()) != 3) - abort (); - return 0; -} - -/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int.*middleman" "inline" } } */ -/* { dg-final { cleanup-ipa-dump "inline" } } */ Index: icln/gcc/testsuite/g++.dg/ipa/ivinline-6.C =================================================================== --- icln.orig/gcc/testsuite/g++.dg/ipa/ivinline-6.C +++ /dev/null @@ -1,58 +0,0 @@ -/* Verify that virtual call inlining works also when it has to get the - type from an ipa invariant and that even in this case it does not - pick a wrong method when there is a user defined ancestor in an - object. */ -/* { dg-do run } */ -/* { dg-options "-O3 -fdump-ipa-inline -fno-early-inlining -fno-ipa-cp" } */ - -extern "C" void abort (void); - -class A -{ -public: - int data; - virtual int foo (int i); -}; - -class B : public A -{ -public: - class A confusion; - virtual int foo (int i); -}; - -int A::foo (int i) -{ - return i + 1; -} - -int B::foo (int i) -{ - return i + 2; -} - -int middleman (class A *obj, int i) -{ - return obj->foo (i); -} - -int __attribute__ ((noinline,noclone)) get_input(void) -{ - return 1; -} - -class B b; - -int main (int argc, char *argv[]) -{ - int i, j = get_input (); - - for (i = 0; i < j; i++) - if ((middleman (&b, j) + 100 * middleman (&b.confusion, j)) != 203) - abort (); - return 0; -} - -/* { dg-final { scan-ipa-dump "A::foo\[^\\n\]*inline copy in int main" "inline" } } */ -/* { dg-final { scan-ipa-dump "B::foo\[^\\n\]*inline copy in int main" "inline" } } */ -/* { dg-final { cleanup-ipa-dump "inline" } } */