From patchwork Thu May 12 16:08:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Jambor X-Patchwork-Id: 621659 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 3r5HwK73Vkz9sD5 for ; Fri, 13 May 2016 02:08:32 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=Bp2332w2; dkim-atps=neutral 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:mime-version:content-type; q=dns; s=default; b=F8haKfEzMaHL5TW0qhr4UludRFbdbM88nEgcYEUitMlQk8fkdY iEAfWdx5wlOQy98TNvis2jinznKEwcgCuE5c2k2uISPCN7PqPXrgyG1Vsnh8Y5j2 7/WhS2ER2Dy83bIY72dI+SILHpAmmOOGCM2PK3Yt4XYIa/bs2I5FyfRQM= 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:mime-version:content-type; s= default; bh=RgxwlWpLBOJ0lMYclOUJGw+L558=; b=Bp2332w2SmFnVgtGStzT DG9Lq9OV+LtWeameb7xFXYHCjUAYdm3rx1kH4dbCcIbMaHYbPz0DZhAGiYtrBdeP pLt+H+ibzjxyINoi/PrOkK/voqxk/0xiylYNLRdOtldcCUVNqAbDlGyKCxYl9Ast cXZ28PHgr9qKqnqwZ4c4jb4= Received: (qmail 44296 invoked by alias); 12 May 2016 16:08: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 44275 invoked by uid 89); 12 May 2016 16:08:20 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, SPF_PASS autolearn=ham version=3.3.2 spammy=wi, TREE_READONLY, tree_readonly, lays X-HELO: mx2.suse.de Received: from mx2.suse.de (HELO mx2.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (CAMELLIA256-SHA encrypted) ESMTPS; Thu, 12 May 2016 16:08:09 +0000 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 85C18AC95 for ; Thu, 12 May 2016 16:08:06 +0000 (UTC) Date: Thu, 12 May 2016 18:08:06 +0200 From: Martin Jambor To: GCC Patches Cc: Jan Hubicka Subject: [PATCH 1/3] Indirect inlining of targets from references of global constants Message-ID: <20160512160806.GI5580@virgil.suse.cz> Mail-Followup-To: GCC Patches , Jan Hubicka MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.6.0 (2016-04-01) X-IsSubscribed: yes Hi, the patch below implements deducing aggregate contents from pointers to constant variables for inlining and IPA-CP, which finally makes us perform the optimization requested in https://gcc.gnu.org/ml/gcc/2014-07/msg00240.html. It also lays down the basis for doing optimization requested in PR 69708 but two additional small patches are required for that. This means we do not give up if we can't use AA to prove that the memory in question has not been clobbered since invocation of the function but only mark this fact in the indirect_call_info. Later on we still use this information if we know that the parameter in question points to a constant variable. If this is deemed a god approach, we will probably want to add a similar bit to inlining conditions. Bootstrapped, lto-bootstrapped and tested on x86_64-linux. OK for trunk? Thanks, Martin 2016-05-11 Martin Jambor PR ipa/69708 * cgraph.h (cgraph_indirect_call_info): New field guaranteed_unmodified. * ipa-cp.c (ipa_get_indirect_edge_target_1): Also pass parameter value to ipa_find_agg_cst_for_param, check guaranteed_unmodified when appropriate. * ipa-inline-analysis.c (evaluate_conditions_for_known_args): Also pass the parameter value to ipa_find_agg_cst_for_param. * ipa-prop.c (ipa_load_from_parm_agg): New parameter guaranteed_unmodified, store AA results there instead of bailing out if present. (ipa_note_param_call): Also initialize guaranteed_unmodified flag. (ipa_analyze_indirect_call_uses): Also set guaranteed_unmodified flag. (find_constructor_constant_at_offset): New function. (ipa_find_agg_cst_from_init): Likewise. (ipa_find_agg_cst_for_param): Also seearch for aggregate values in static initializers of contants, report back through a new paameter from_global_constant if that was the case. (try_make_edge_direct_simple_call): Also pass parameter value to ipa_find_agg_cst_for_param, check guaranteed_unmodified when appropriate. (ipa_write_indirect_edge_info): Stream new flag guaranteed_unmodified. (ipa_read_indirect_edge_info): Likewise. * ipa-prop.h (ipa_find_agg_cst_for_param): Update declaration. (ipa_load_from_parm_agg): Likewise. testsuite/ * gcc.dg/ipa/iinline-cstagg-1.c: New test. * gcc.dg/ipa/ipcp-cstagg-1.c: Likewise. * gcc.dg/ipa/ipcp-cstagg-2.c: Likewise. * gcc.dg/ipa/ipcp-cstagg-3.c: Likewise. * gcc.dg/ipa/ipcp-cstagg-4.c: Likewise. --- gcc/cgraph.h | 9 +- gcc/ipa-cp.c | 26 ++-- gcc/ipa-inline-analysis.c | 3 +- gcc/ipa-prop.c | 180 ++++++++++++++++++++++++---- gcc/ipa-prop.h | 13 +- gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c | 37 ++++++ gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c | 42 +++++++ gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c | 46 +++++++ gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c | 58 +++++++++ gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c | 64 ++++++++++ 10 files changed, 440 insertions(+), 38 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c create mode 100644 gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 8ad9f45..ecafe63 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1579,9 +1579,14 @@ struct GTY(()) cgraph_indirect_call_info unsigned agg_contents : 1; /* Set when this is a call through a member pointer. */ unsigned member_ptr : 1; - /* When the previous bit is set, this one determines whether the destination - is loaded from a parameter passed by reference. */ + /* When the agg_contents bit is set, this one determines whether the + destination is loaded from a parameter passed by reference. */ unsigned by_ref : 1; + /* When the agg_contents bit is set, this one determines whether we can + deduce from the function body that the loaded value from the reference is + never modified between the invocation of the function and the load + point. */ + unsigned guaranteed_unmodified : 1; /* For polymorphic calls this specify whether the virtual table pointer may have changed in between function entry and the call. */ unsigned vptr_changed : 1; diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index 5900d4d..2183da0 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -1999,9 +1999,9 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, if (ie->indirect_info->agg_contents) { - if (agg_reps) + t = NULL; + if (agg_reps && ie->indirect_info->guaranteed_unmodified) { - t = NULL; while (agg_reps) { if (agg_reps->index == param_index @@ -2014,15 +2014,22 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, agg_reps = agg_reps->next; } } - else if (known_aggs.length () > (unsigned int) param_index) + if (!t) { struct ipa_agg_jump_function *agg; - agg = known_aggs[param_index]; - t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset, - ie->indirect_info->by_ref); + if (known_aggs.length () > (unsigned int) param_index) + agg = known_aggs[param_index]; + else + agg = NULL; + bool from_global_constant; + t = ipa_find_agg_cst_for_param (agg, known_csts[param_index], + ie->indirect_info->offset, + ie->indirect_info->by_ref, + &from_global_constant); + if (!from_global_constant + && !ie->indirect_info->guaranteed_unmodified) + t = NULL_TREE; } - else - t = NULL; } else t = known_csts[param_index]; @@ -2066,7 +2073,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie, { struct ipa_agg_jump_function *agg; agg = known_aggs[param_index]; - t = ipa_find_agg_cst_for_param (agg, ie->indirect_info->offset, + t = ipa_find_agg_cst_for_param (agg, known_csts[param_index], + ie->indirect_info->offset, true); } diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c index 68824f7..8cfc9f6 100644 --- a/gcc/ipa-inline-analysis.c +++ b/gcc/ipa-inline-analysis.c @@ -853,7 +853,8 @@ evaluate_conditions_for_known_args (struct cgraph_node *node, if (known_aggs.exists ()) { agg = known_aggs[c->operand_num]; - val = ipa_find_agg_cst_for_param (agg, c->offset, c->by_ref); + val = ipa_find_agg_cst_for_param (agg, known_vals[c->operand_num], + c->offset, c->by_ref); } else val = NULL_TREE; diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index f02ec47..afbc32b 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -930,10 +930,15 @@ parm_ref_data_pass_through_p (struct ipa_func_body_info *fbi, int index, return !modified; } -/* Return true if we can prove that OP is a memory reference loading unmodified - data from an aggregate passed as a parameter and if the aggregate is passed - by reference, that the alias type of the load corresponds to the type of the - formal parameter (so that we can rely on this type for TBAA in callers). +/* Return true if we can prove that OP is a memory reference loading + data from an aggregate passed as a parameter. + + The function works in two modes. If GUARANTEED_UNMODIFIED is NULL, it return + false if it cannot prove that the value has not been modified before the + load in STMT. If GUARANTEED_UNMODIFIED is not NULL, it will return true even + if it cannot prove the value has not been modified, in that case it will + store false to *GUARANTEED_UNMODIFIED, otherwise it will store true there. + INFO and PARMS_AINFO describe parameters of the current function (but the latter can be NULL), STMT is the load statement. If function returns true, *INDEX_P, *OFFSET_P and *BY_REF is filled with the parameter index, offset @@ -945,7 +950,7 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi, vec descriptors, gimple *stmt, tree op, int *index_p, HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p, - bool *by_ref_p) + bool *by_ref_p, bool *guaranteed_unmodified) { int index; HOST_WIDE_INT size, max_size; @@ -966,6 +971,8 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi, *by_ref_p = false; if (size_p) *size_p = size; + if (guaranteed_unmodified) + *guaranteed_unmodified = true; return true; } return false; @@ -1002,13 +1009,18 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi, index = load_from_unmodified_param (fbi, descriptors, def); } - if (index >= 0 - && parm_ref_data_preserved_p (fbi, index, stmt, op)) + if (index >= 0) { + bool data_preserved = parm_ref_data_preserved_p (fbi, index, stmt, op); + if (!data_preserved && !guaranteed_unmodified) + return false; + *index_p = index; *by_ref_p = true; if (size_p) *size_p = size; + if (guaranteed_unmodified) + *guaranteed_unmodified = data_preserved; return true; } return false; @@ -1824,6 +1836,7 @@ ipa_note_param_call (struct cgraph_node *node, int param_index, cs->indirect_info->param_index = param_index; cs->indirect_info->agg_contents = 0; cs->indirect_info->member_ptr = 0; + cs->indirect_info->guaranteed_unmodified = 0; return cs; } @@ -1905,15 +1918,17 @@ ipa_analyze_indirect_call_uses (struct ipa_func_body_info *fbi, gcall *call, int index; gimple *def = SSA_NAME_DEF_STMT (target); + bool guaranteed_unmodified; if (gimple_assign_single_p (def) && ipa_load_from_parm_agg (fbi, info->descriptors, def, gimple_assign_rhs1 (def), &index, &offset, - NULL, &by_ref)) + NULL, &by_ref, &guaranteed_unmodified)) { struct cgraph_edge *cs = ipa_note_param_call (fbi->node, index, call); cs->indirect_info->offset = offset; cs->indirect_info->agg_contents = 1; cs->indirect_info->by_ref = by_ref; + cs->indirect_info->guaranteed_unmodified = guaranteed_unmodified; return; } @@ -2014,6 +2029,7 @@ ipa_analyze_indirect_call_uses (struct ipa_func_body_info *fbi, gcall *call, cs->indirect_info->offset = offset; cs->indirect_info->agg_contents = 1; cs->indirect_info->member_ptr = 1; + cs->indirect_info->guaranteed_unmodified = 1; } return; @@ -2701,18 +2717,126 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target, return ie; } -/* Retrieve value from aggregate jump function AGG for the given OFFSET or - return NULL if there is not any. BY_REF specifies whether the value has to - be passed by reference or by value. */ +/* Attempt to locate an interprocedural constant at a given REQ_OFFSET in + CONSTRUCTOR and return it. Return NULL if the search fails for some + reason. */ + +static tree +find_constructor_constant_at_offset (tree constructor, HOST_WIDE_INT req_offset) +{ + tree type = TREE_TYPE (constructor); + if (TREE_CODE (type) != ARRAY_TYPE + && TREE_CODE (type) != RECORD_TYPE) + return NULL; + + unsigned ix; + tree index, val; + FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (constructor), ix, index, val) + { + HOST_WIDE_INT elt_offset; + if (TREE_CODE (type) == ARRAY_TYPE) + { + offset_int off; + tree unit_size = TYPE_SIZE_UNIT (TREE_TYPE (type)); + gcc_assert (TREE_CODE (unit_size) == INTEGER_CST); + + if (index) + { + off = wi::to_offset (index); + if (TYPE_DOMAIN (type) && TYPE_MIN_VALUE (TYPE_DOMAIN (type))) + { + tree low_bound = TYPE_MIN_VALUE (TYPE_DOMAIN (type)); + gcc_assert (TREE_CODE (unit_size) == INTEGER_CST); + off = wi::sext (off - wi::to_offset (low_bound), + TYPE_PRECISION (TREE_TYPE (index))); + } + off *= wi::to_offset (unit_size); + } + else + off = wi::to_offset (unit_size) * ix; + + off = wi::lshift (off, LOG2_BITS_PER_UNIT); + if (!wi::fits_shwi_p (off) || wi::neg_p (off)) + continue; + elt_offset = off.to_shwi (); + } + else if (TREE_CODE (type) == RECORD_TYPE) + { + gcc_checking_assert (index && TREE_CODE (index) == FIELD_DECL); + if (DECL_BIT_FIELD (index)) + continue; + elt_offset = int_bit_position (index); + } + else + gcc_unreachable (); + + if (elt_offset > req_offset) + return NULL; + + if (TREE_CODE (val) == CONSTRUCTOR) + return find_constructor_constant_at_offset (val, + req_offset - elt_offset); + + if (elt_offset == req_offset + && is_gimple_reg_type (TREE_TYPE (val)) + && is_gimple_ip_invariant (val)) + return val; + } + return NULL; +} + +/* Check whether SCALAR could be used to look up an aggregate interprocedural + invariant from a static constructor and if so, return it. Otherwise return + NULL. */ + +static tree +ipa_find_agg_cst_from_init (tree scalar, HOST_WIDE_INT offset, bool by_ref) +{ + if (by_ref) + { + if (TREE_CODE (scalar) != ADDR_EXPR) + return NULL;; + scalar = TREE_OPERAND (scalar, 0); + } + + if (TREE_CODE (scalar) != VAR_DECL + || !is_global_var (scalar) + || !TREE_READONLY (scalar) + || !DECL_INITIAL (scalar) + || TREE_CODE (DECL_INITIAL (scalar)) != CONSTRUCTOR) + return NULL; + + return find_constructor_constant_at_offset (DECL_INITIAL (scalar), offset); +} + +/* Retrieve value from aggregate jump function AGG or static initializer of + SCALAR (which can be NULL) for the given OFFSET or return NULL if there is + not any. BY_REF specifies whether the value has to be passed by reference + or by value. If FROM_GLOBAL_CONSTANT is non-NULL, then the boolean it + points to is set to true if the value comes from an initializer of a + constant. */ tree -ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, - HOST_WIDE_INT offset, bool by_ref) +ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar, + HOST_WIDE_INT offset, bool by_ref, + bool *from_global_constant) { struct ipa_agg_jf_item *item; int i; - if (by_ref != agg->by_ref) + if (scalar) + { + tree res = ipa_find_agg_cst_from_init (scalar, offset, by_ref); + if (res) + { + if (from_global_constant) + *from_global_constant = true; + return res; + } + } + + if (!agg + || by_ref != agg->by_ref) return NULL; FOR_EACH_VEC_SAFE_ELT (agg->items, i, item) @@ -2721,6 +2845,8 @@ ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, /* Currently we do not have clobber values, return NULL for them once we do. */ gcc_checking_assert (is_gimple_ip_invariant (item->value)); + if (from_global_constant) + *from_global_constant = false; return item->value; } return NULL; @@ -2819,13 +2945,21 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie, struct cgraph_edge *cs; tree target; bool agg_contents = ie->indirect_info->agg_contents; - - if (ie->indirect_info->agg_contents) - target = ipa_find_agg_cst_for_param (&jfunc->agg, - ie->indirect_info->offset, - ie->indirect_info->by_ref); + tree scalar = ipa_value_from_jfunc (new_root_info, jfunc); + if (agg_contents) + { + bool from_global_constant; + target = ipa_find_agg_cst_for_param (&jfunc->agg, scalar, + ie->indirect_info->offset, + ie->indirect_info->by_ref, + &from_global_constant); + if (target + && !from_global_constant + && !ie->indirect_info->guaranteed_unmodified) + return NULL; + } else - target = ipa_value_from_jfunc (new_root_info, jfunc); + target = scalar; if (!target) return NULL; cs = ipa_make_edge_direct_to_target (ie, target); @@ -2893,7 +3027,9 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie, { tree vtable; unsigned HOST_WIDE_INT offset; - tree t = ipa_find_agg_cst_for_param (&jfunc->agg, + tree scalar = (jfunc->type == IPA_JF_CONST) ? ipa_get_jf_constant (jfunc) + : NULL; + tree t = ipa_find_agg_cst_for_param (&jfunc->agg, scalar, ie->indirect_info->offset, true); if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset)) @@ -4560,6 +4696,7 @@ ipa_write_indirect_edge_info (struct output_block *ob, bp_pack_value (&bp, ii->agg_contents, 1); bp_pack_value (&bp, ii->member_ptr, 1); bp_pack_value (&bp, ii->by_ref, 1); + bp_pack_value (&bp, ii->guaranteed_unmodified, 1); bp_pack_value (&bp, ii->vptr_changed, 1); streamer_write_bitpack (&bp); if (ii->agg_contents || ii->polymorphic) @@ -4592,6 +4729,7 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib, ii->agg_contents = bp_unpack_value (&bp, 1); ii->member_ptr = bp_unpack_value (&bp, 1); ii->by_ref = bp_unpack_value (&bp, 1); + ii->guaranteed_unmodified = bp_unpack_value (&bp, 1); ii->vptr_changed = bp_unpack_value (&bp, 1); if (ii->agg_contents || ii->polymorphic) ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib); diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index 4e11ca6..e32d078 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -636,11 +636,14 @@ tree ipa_impossible_devirt_target (struct cgraph_edge *, tree); void ipa_analyze_node (struct cgraph_node *); /* Aggregate jump function related functions. */ -tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *, HOST_WIDE_INT, - bool); -bool ipa_load_from_parm_agg (struct ipa_func_body_info *, - vec, gimple *, tree, int *, - HOST_WIDE_INT *, HOST_WIDE_INT *, bool *); +tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar, + HOST_WIDE_INT offset, bool by_ref, + bool *from_global_constant = NULL); +bool ipa_load_from_parm_agg (struct ipa_func_body_info *fbi, + vec descriptors, + gimple *stmt, tree op, int *index_p, + HOST_WIDE_INT *offset_p, HOST_WIDE_INT *size_p, + bool *by_ref, bool *guaranteed_unmodified = NULL); /* Debugging interface. */ void ipa_print_node_params (FILE *, struct cgraph_node *node); diff --git a/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c b/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c new file mode 100644 index 0000000..8656cb3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/iinline-cstagg-1.c @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-inline-details -fno-early-inlining -fno-ipa-sra -fno-ipa-cp" } */ + +typedef struct S +{ + int add_offset; + int (*call)(int); +} S; + +static int +bar (const S *f, int x) +{ + x = f->call(x); + return x; +} + +static int +thisisthetarget (int x) +{ + return x * x; +} + +static const S s = {16, thisisthetarget}; + +int +outerfunction (int x) +{ + return bar (&s, x); +} + +int +obfuscate (int x) +{ + return bar ((S *) 0, x); +} + +/* { dg-final { scan-ipa-dump "thisisthetarget\[^\\n\]*inline copy in outerfunction" "inline" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c new file mode 100644 index 0000000..4d64ea7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-1.c @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp-details" } */ + +typedef struct S +{ + int add_offset; + int (*call)(int); +} S; + +extern const S *gs; + +static int __attribute__((noinline)) +bar (const S *f, int x) +{ + x = f->call(x); + x = f->call(x); + x = f->call(x); + gs = f; + return x; +} + +static int +sq (int x) +{ + return x * x; +} + +static const S s = {16, sq}; + +int +g (int x) +{ + return bar (&s, x); +} + +int +obfuscate (int x) +{ + return bar ((S *) 0, x); +} + +/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c new file mode 100644 index 0000000..f820140 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-2.c @@ -0,0 +1,46 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp-details" } */ + +typedef struct S +{ + int (*call)(int); +} S; + +static int __attribute__((noinline)) +bar (const S *f, int x) +{ + x = f->call(x); + return x; +} + +extern void impossible_aa (void); + +static int __attribute__((noinline)) +baz (const S *f, int x) +{ + impossible_aa (); + return bar (f, x); +} + +static int +sq (int x) +{ + return x * x; +} + +static const S s = {sq}; + +int +g (int x) +{ + return baz (&s, x); +} + +int +obfuscate (int x) +{ + return baz ((S *) 0, x); +} + +/* { dg-final { scan-ipa-dump "Discovered an indirect call to a known target" "cp" } } */ + diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c new file mode 100644 index 0000000..917f138 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-3.c @@ -0,0 +1,58 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp-details" } */ + +#define N 4 + +typedef int (* const A[N])(int); + +extern const A *ga; + +static int __attribute__((noinline)) +bar (const A *f, int x) +{ + x = (*f)[2](x); + x = (*f)[2](x); + x = (*f)[2](x); + ga = f; + return x; +} + +static int +zero (int x) +{ + return 0; +} + +static int +addone (int x) +{ + return x + 1; +} + +static int +sq (int x) +{ + return x * x; +} + +static int +cube (int x) +{ + return x * x * x; +} + +static const A a = {zero, addone, sq, cube}; + +int +g (int x) +{ + return bar (&a, x); +} + +int +obfuscate (int x) +{ + return bar ((A *) 0, x); +} + +/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */ diff --git a/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c new file mode 100644 index 0000000..7458402 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ipa/ipcp-cstagg-4.c @@ -0,0 +1,64 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -fdump-ipa-cp-details" } */ + +#define N 4 + +typedef int (* const A[N])(int); + +typedef struct S +{ + int add_offset; + A a; +} S; + +extern const S *gs; + +static int __attribute__((noinline)) +bar (const S *f, int x) +{ + gs = f; + x = f->a[2](x); + x = f->a[2](x); + x = f->a[2](x); + return x; +} + +static int +zero (int x) +{ + return 0; +} + +static int +addone (int x) +{ + return x + 1; +} + +static int +sq (int x) +{ + return x * x; +} + +static int +cube (int x) +{ + return x * x * x; +} + +static const S s = {64, {zero, addone, sq, cube}}; + +int +g (int x) +{ + return bar (&s, x); +} + +int +obfuscate (int x) +{ + return bar ((S *) 0, x); +} + +/* { dg-final { scan-ipa-dump-times "Discovered an indirect call to a known target" 3 "cp" } } */