From patchwork Wed Nov 8 20:54:43 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Jambor X-Patchwork-Id: 835985 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-466330-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="tGBPfI+P"; dkim-atps=neutral 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 3yXJTS5FpMz9s7B for ; Thu, 9 Nov 2017 07:55:04 +1100 (AEDT) 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=hnq/ngfPHXjhCtqhr39Mu775CPhCIdvM50mIQdX+9M3pum+Zqx BpIOR83vQiopzQU0T+0p7SC4qzBLR2u0Xm22AfmR85KeGToFwDwBOv4RvKg/zLkA dAbAQ06g8suXS1BPLqeRvl2KcScUAAZD0m8GXYqZdzgyQSjg2kKeowlr8= 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=l+6XLUUmeUfPwFp0VWoOrXFMNCY=; b=tGBPfI+P83gQ6i1zCFBU 8FcaBjO8FTFP3tx61cO8LL+dK3x9Krgq7YIse5011Q04Kc3gT/ZqfC8vguaJKXc4 nnIcDpfOwvcIGPltEAdu1FjCHNjr/yToWdLFHNd+bSCCC962oeAuRawnC6npdVgV /wAEf+98aN7axQJIumBrl0A= Received: (qmail 26725 invoked by alias); 8 Nov 2017 20:54:54 -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 26716 invoked by uid 89); 8 Nov 2017 20:54:53 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, SPF_PASS autolearn=ham version=3.3.2 spammy=rop, H*Ad:U*jh, first 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 ESMTP; Wed, 08 Nov 2017 20:54:46 +0000 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 14637AD0B for ; Wed, 8 Nov 2017 20:54:44 +0000 (UTC) Date: Wed, 8 Nov 2017 21:54:43 +0100 From: Martin Jambor To: GCC Patches Cc: Jan Hubicka Subject: [PATCH 1/2] Moving parameter manipulation into its own file Message-ID: <20171108205442.jeosnobdino4fde7@virgil.suse.cz> MIME-Version: 1.0 Content-Disposition: inline User-Agent: NeoMutt/20170912 (1.9.0) X-IsSubscribed: yes Hi, the following patch moves all function and call parameter manipulation (as opposed to analysis) data structures and functions from ipa-prop.h and ipa-prop.c to new files ipa-param-manipulation.h and ipa-param-manipulation.c respectively. It does no functional change. Please look at the followup patch if you'd like to see where I am heading with this. While I am willing to hold up the followup patch for GCC 9, I would like to commit this now because it increases modularity in an area where it is needed. Bootstrapped and teste on x86_64-linux. OK for trunk? Martin 2017-08-23 Martin Jambor * ipa-param-manipulation.c: New file. * ipa-param-manipulation.h: Likewise. * Makefile.in (OBJS): Add ipa-param-manipulation.o. (PLUGIN_HEADERS): Addded ipa-param-manipulation.h * ipa-param.h (ipa_parm_op): Moved to ipa-param-manipulation.h. (ipa_parm_adjustment): Likewise. (ipa_parm_adjustment_vec): Likewise. (ipa_get_vector_of_formal_parms): Moved declaration to ipa-param-manipulation.h. (ipa_get_vector_of_formal_parm_types): Likewise. (ipa_modify_formal_parameters): Likewise. (ipa_modify_call_arguments): Likewise. (ipa_combine_adjustments): Likewise. (ipa_dump_param_adjustments): Likewise. (ipa_modify_expr): Likewise. (ipa_get_adjustment_candidate): Likewise. * ipa-prop.c (ipa_get_vector_of_formal_parms): Moved to ipa-param-manipulation.c. (ipa_get_vector_of_formal_parm_types): Likewise. (ipa_modify_formal_parameters): Likewise. (ipa_modify_call_arguments): Likewise. (ipa_modify_expr): Likewise. (get_ssa_base_param): Likewise. (ipa_get_adjustment_candidate): Likewise. (index_in_adjustments_multiple_times_p): Likewise. (ipa_combine_adjustments): Likewise. (ipa_dump_param_adjustments): Likewise. * tree-sra.c: Also include ipa-param-manipulation.h * omp-simd-clone.c: Include ipa-param-manipulation.h instead of ipa-param.h. --- gcc/Makefile.in | 3 +- gcc/ipa-param-manipulation.c | 767 +++++++++++++++++++++++++++++++++++++++++++ gcc/ipa-param-manipulation.h | 120 +++++++ gcc/ipa-prop.c | 725 ---------------------------------------- gcc/ipa-prop.h | 94 ------ gcc/omp-simd-clone.c | 2 +- gcc/tree-sra.c | 1 + 7 files changed, 891 insertions(+), 821 deletions(-) create mode 100644 gcc/ipa-param-manipulation.c create mode 100644 gcc/ipa-param-manipulation.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 51968e4a66f..1bb1d6ec0ff 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1354,6 +1354,7 @@ OBJS = \ ipa-predicate.o \ ipa-profile.o \ ipa-prop.o \ + ipa-param-manipulation.o \ ipa-pure-const.o \ ipa-icf.o \ ipa-icf-gimple.o \ @@ -3467,7 +3468,7 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(C_COMMON_H) c-family/c-objc.h $(C_PRETTY_PRINT_H) \ tree-iterator.h $(PLUGIN_H) $(TREE_SSA_H) langhooks.h incpath.h debug.h \ $(EXCEPT_H) tree-ssa-sccvn.h real.h output.h $(IPA_UTILS_H) \ - $(C_PRAGMA_H) $(CPPLIB_H) $(FUNCTION_H) \ + ipa-param-manipulation.h $(C_PRAGMA_H) $(CPPLIB_H) $(FUNCTION_H) \ cppdefault.h flags.h $(MD5_H) params.def params.h params-enum.h \ prefix.h tree-inline.h $(GIMPLE_PRETTY_PRINT_H) realmpfr.h \ $(IPA_PROP_H) $(TARGET_H) $(RTL_H) $(TM_P_H) $(CFGLOOP_H) $(EMIT_RTL_H) \ diff --git a/gcc/ipa-param-manipulation.c b/gcc/ipa-param-manipulation.c new file mode 100644 index 00000000000..bedd201f6dd --- /dev/null +++ b/gcc/ipa-param-manipulation.c @@ -0,0 +1,767 @@ +/* Manipulation of formal and actual parameters of functions and function + calls. + Copyright (C) 2017 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "rtl.h" +#include "tree.h" +#include "gimple.h" +#include "ssa.h" +#include "cgraph.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "gimplify.h" +#include "gimple-iterator.h" +#include "gimplify-me.h" +#include "tree-dfa.h" +#include "ipa-param-manipulation.h" +#include "print-tree.h" +#include "gimple-pretty-print.h" +#include "builtins.h" + +/* Return a heap allocated vector containing formal parameters of FNDECL. */ + +vec +ipa_get_vector_of_formal_parms (tree fndecl) +{ + vec args; + int count; + tree parm; + + gcc_assert (!flag_wpa); + count = 0; + for (parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm)) + count++; + + args.create (count); + for (parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm)) + args.quick_push (parm); + + return args; +} + +/* Return a heap allocated vector containing types of formal parameters of + function type FNTYPE. */ + +vec +ipa_get_vector_of_formal_parm_types (tree fntype) +{ + vec types; + int count = 0; + tree t; + + for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t)) + count++; + + types.create (count); + for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t)) + types.quick_push (TREE_VALUE (t)); + + return types; +} + +/* Modify the function declaration FNDECL and its type according to the plan in + ADJUSTMENTS. It also sets base fields of individual adjustments structures + to reflect the actual parameters being modified which are determined by the + base_index field. */ + +void +ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments) +{ + vec oparms = ipa_get_vector_of_formal_parms (fndecl); + tree orig_type = TREE_TYPE (fndecl); + tree old_arg_types = TYPE_ARG_TYPES (orig_type); + + /* The following test is an ugly hack, some functions simply don't have any + arguments in their type. This is probably a bug but well... */ + bool care_for_types = (old_arg_types != NULL_TREE); + bool last_parm_void; + vec otypes; + if (care_for_types) + { + last_parm_void = (TREE_VALUE (tree_last (old_arg_types)) + == void_type_node); + otypes = ipa_get_vector_of_formal_parm_types (orig_type); + if (last_parm_void) + gcc_assert (oparms.length () + 1 == otypes.length ()); + else + gcc_assert (oparms.length () == otypes.length ()); + } + else + { + last_parm_void = false; + otypes.create (0); + } + + int len = adjustments.length (); + tree *link = &DECL_ARGUMENTS (fndecl); + tree new_arg_types = NULL; + for (int i = 0; i < len; i++) + { + struct ipa_parm_adjustment *adj; + gcc_assert (link); + + adj = &adjustments[i]; + tree parm; + if (adj->op == IPA_PARM_OP_NEW) + parm = NULL; + else + parm = oparms[adj->base_index]; + adj->base = parm; + + if (adj->op == IPA_PARM_OP_COPY) + { + if (care_for_types) + new_arg_types = tree_cons (NULL_TREE, otypes[adj->base_index], + new_arg_types); + *link = parm; + link = &DECL_CHAIN (parm); + } + else if (adj->op != IPA_PARM_OP_REMOVE) + { + tree new_parm; + tree ptype; + + if (adj->by_ref) + ptype = build_pointer_type (adj->type); + else + { + ptype = adj->type; + if (is_gimple_reg_type (ptype) + && TYPE_MODE (ptype) != BLKmode) + { + unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ptype)); + if (TYPE_ALIGN (ptype) != malign) + ptype = build_aligned_type (ptype, malign); + } + } + + if (care_for_types) + new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types); + + new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE, + ptype); + const char *prefix = adj->arg_prefix ? adj->arg_prefix : "SYNTH"; + DECL_NAME (new_parm) = create_tmp_var_name (prefix); + DECL_ARTIFICIAL (new_parm) = 1; + DECL_ARG_TYPE (new_parm) = ptype; + DECL_CONTEXT (new_parm) = fndecl; + TREE_USED (new_parm) = 1; + DECL_IGNORED_P (new_parm) = 1; + layout_decl (new_parm, 0); + + if (adj->op == IPA_PARM_OP_NEW) + adj->base = NULL; + else + adj->base = parm; + adj->new_decl = new_parm; + + *link = new_parm; + link = &DECL_CHAIN (new_parm); + } + } + + *link = NULL_TREE; + + tree new_reversed = NULL; + if (care_for_types) + { + new_reversed = nreverse (new_arg_types); + if (last_parm_void) + { + if (new_reversed) + TREE_CHAIN (new_arg_types) = void_list_node; + else + new_reversed = void_list_node; + } + } + + /* Use copy_node to preserve as much as possible from original type + (debug info, attribute lists etc.) + Exception is METHOD_TYPEs must have THIS argument. + When we are asked to remove it, we need to build new FUNCTION_TYPE + instead. */ + tree new_type = NULL; + if (TREE_CODE (orig_type) != METHOD_TYPE + || (adjustments[0].op == IPA_PARM_OP_COPY + && adjustments[0].base_index == 0)) + { + new_type = build_distinct_type_copy (orig_type); + TYPE_ARG_TYPES (new_type) = new_reversed; + } + else + { + new_type + = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type), + new_reversed)); + TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type); + DECL_VINDEX (fndecl) = NULL_TREE; + } + + /* When signature changes, we need to clear builtin info. */ + if (DECL_BUILT_IN (fndecl)) + { + DECL_BUILT_IN_CLASS (fndecl) = NOT_BUILT_IN; + DECL_FUNCTION_CODE (fndecl) = (enum built_in_function) 0; + } + + TREE_TYPE (fndecl) = new_type; + DECL_VIRTUAL_P (fndecl) = 0; + DECL_LANG_SPECIFIC (fndecl) = NULL; + otypes.release (); + oparms.release (); +} + +/* Modify actual arguments of a function call CS as indicated in ADJUSTMENTS. + If this is a directly recursive call, CS must be NULL. Otherwise it must + contain the corresponding call graph edge. */ + +void +ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt, + ipa_parm_adjustment_vec adjustments) +{ + struct cgraph_node *current_node = cgraph_node::get (current_function_decl); + vec vargs; + vec **debug_args = NULL; + gcall *new_stmt; + gimple_stmt_iterator gsi, prev_gsi; + tree callee_decl; + int i, len; + + len = adjustments.length (); + vargs.create (len); + callee_decl = !cs ? gimple_call_fndecl (stmt) : cs->callee->decl; + current_node->remove_stmt_references (stmt); + + gsi = gsi_for_stmt (stmt); + prev_gsi = gsi; + gsi_prev (&prev_gsi); + for (i = 0; i < len; i++) + { + struct ipa_parm_adjustment *adj; + + adj = &adjustments[i]; + + if (adj->op == IPA_PARM_OP_COPY) + { + tree arg = gimple_call_arg (stmt, adj->base_index); + + vargs.quick_push (arg); + } + else if (adj->op != IPA_PARM_OP_REMOVE) + { + tree expr, base, off; + location_t loc; + unsigned int deref_align = 0; + bool deref_base = false; + + /* We create a new parameter out of the value of the old one, we can + do the following kind of transformations: + + - A scalar passed by reference is converted to a scalar passed by + value. (adj->by_ref is false and the type of the original + actual argument is a pointer to a scalar). + + - A part of an aggregate is passed instead of the whole aggregate. + The part can be passed either by value or by reference, this is + determined by value of adj->by_ref. Moreover, the code below + handles both situations when the original aggregate is passed by + value (its type is not a pointer) and when it is passed by + reference (it is a pointer to an aggregate). + + When the new argument is passed by reference (adj->by_ref is true) + it must be a part of an aggregate and therefore we form it by + simply taking the address of a reference inside the original + aggregate. */ + + gcc_checking_assert (adj->offset % BITS_PER_UNIT == 0); + base = gimple_call_arg (stmt, adj->base_index); + loc = DECL_P (base) ? DECL_SOURCE_LOCATION (base) + : EXPR_LOCATION (base); + + if (TREE_CODE (base) != ADDR_EXPR + && POINTER_TYPE_P (TREE_TYPE (base))) + off = build_int_cst (adj->alias_ptr_type, + adj->offset / BITS_PER_UNIT); + else + { + HOST_WIDE_INT base_offset; + tree prev_base; + bool addrof; + + if (TREE_CODE (base) == ADDR_EXPR) + { + base = TREE_OPERAND (base, 0); + addrof = true; + } + else + addrof = false; + prev_base = base; + base = get_addr_base_and_unit_offset (base, &base_offset); + /* Aggregate arguments can have non-invariant addresses. */ + if (!base) + { + base = build_fold_addr_expr (prev_base); + off = build_int_cst (adj->alias_ptr_type, + adj->offset / BITS_PER_UNIT); + } + else if (TREE_CODE (base) == MEM_REF) + { + if (!addrof) + { + deref_base = true; + deref_align = TYPE_ALIGN (TREE_TYPE (base)); + } + off = build_int_cst (adj->alias_ptr_type, + base_offset + + adj->offset / BITS_PER_UNIT); + off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), + off); + base = TREE_OPERAND (base, 0); + } + else + { + off = build_int_cst (adj->alias_ptr_type, + base_offset + + adj->offset / BITS_PER_UNIT); + base = build_fold_addr_expr (base); + } + } + + if (!adj->by_ref) + { + tree type = adj->type; + unsigned int align; + unsigned HOST_WIDE_INT misalign; + + if (deref_base) + { + align = deref_align; + misalign = 0; + } + else + { + get_pointer_alignment_1 (base, &align, &misalign); + if (TYPE_ALIGN (type) > align) + align = TYPE_ALIGN (type); + } + misalign += (offset_int::from (wi::to_wide (off), + SIGNED).to_short_addr () + * BITS_PER_UNIT); + misalign = misalign & (align - 1); + if (misalign != 0) + align = least_bit_hwi (misalign); + if (align < TYPE_ALIGN (type)) + type = build_aligned_type (type, align); + base = force_gimple_operand_gsi (&gsi, base, + true, NULL, true, GSI_SAME_STMT); + expr = fold_build2_loc (loc, MEM_REF, type, base, off); + REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse; + /* If expr is not a valid gimple call argument emit + a load into a temporary. */ + if (is_gimple_reg_type (TREE_TYPE (expr))) + { + gimple *tem = gimple_build_assign (NULL_TREE, expr); + if (gimple_in_ssa_p (cfun)) + { + gimple_set_vuse (tem, gimple_vuse (stmt)); + expr = make_ssa_name (TREE_TYPE (expr), tem); + } + else + expr = create_tmp_reg (TREE_TYPE (expr)); + gimple_assign_set_lhs (tem, expr); + gsi_insert_before (&gsi, tem, GSI_SAME_STMT); + } + } + else + { + expr = fold_build2_loc (loc, MEM_REF, adj->type, base, off); + REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse; + expr = build_fold_addr_expr (expr); + expr = force_gimple_operand_gsi (&gsi, expr, + true, NULL, true, GSI_SAME_STMT); + } + vargs.quick_push (expr); + } + if (adj->op != IPA_PARM_OP_COPY && MAY_HAVE_DEBUG_STMTS) + { + unsigned int ix; + tree ddecl = NULL_TREE, origin = DECL_ORIGIN (adj->base), arg; + gimple *def_temp; + + arg = gimple_call_arg (stmt, adj->base_index); + if (!useless_type_conversion_p (TREE_TYPE (origin), TREE_TYPE (arg))) + { + if (!fold_convertible_p (TREE_TYPE (origin), arg)) + continue; + arg = fold_convert_loc (gimple_location (stmt), + TREE_TYPE (origin), arg); + } + if (debug_args == NULL) + debug_args = decl_debug_args_insert (callee_decl); + for (ix = 0; vec_safe_iterate (*debug_args, ix, &ddecl); ix += 2) + if (ddecl == origin) + { + ddecl = (**debug_args)[ix + 1]; + break; + } + if (ddecl == NULL) + { + ddecl = make_node (DEBUG_EXPR_DECL); + DECL_ARTIFICIAL (ddecl) = 1; + TREE_TYPE (ddecl) = TREE_TYPE (origin); + SET_DECL_MODE (ddecl, DECL_MODE (origin)); + + vec_safe_push (*debug_args, origin); + vec_safe_push (*debug_args, ddecl); + } + def_temp = gimple_build_debug_bind (ddecl, unshare_expr (arg), stmt); + gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT); + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "replacing stmt:"); + print_gimple_stmt (dump_file, gsi_stmt (gsi), 0); + } + + new_stmt = gimple_build_call_vec (callee_decl, vargs); + vargs.release (); + if (gimple_call_lhs (stmt)) + gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); + + gimple_set_block (new_stmt, gimple_block (stmt)); + if (gimple_has_location (stmt)) + gimple_set_location (new_stmt, gimple_location (stmt)); + gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); + gimple_call_copy_flags (new_stmt, stmt); + if (gimple_in_ssa_p (cfun)) + { + gimple_set_vuse (new_stmt, gimple_vuse (stmt)); + if (gimple_vdef (stmt)) + { + gimple_set_vdef (new_stmt, gimple_vdef (stmt)); + SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "with stmt:"); + print_gimple_stmt (dump_file, new_stmt, 0); + fprintf (dump_file, "\n"); + } + gsi_replace (&gsi, new_stmt, true); + if (cs) + cs->set_call_stmt (new_stmt); + do + { + current_node->record_stmt_references (gsi_stmt (gsi)); + gsi_prev (&gsi); + } + while (gsi_stmt (gsi) != gsi_stmt (prev_gsi)); +} + +/* Return true iff BASE_INDEX is in ADJUSTMENTS more than once. */ + +static bool +index_in_adjustments_multiple_times_p (int base_index, + ipa_parm_adjustment_vec adjustments) +{ + int i, len = adjustments.length (); + bool one = false; + + for (i = 0; i < len; i++) + { + struct ipa_parm_adjustment *adj; + adj = &adjustments[i]; + + if (adj->base_index == base_index) + { + if (one) + return true; + else + one = true; + } + } + return false; +} + +/* Return adjustments that should have the same effect on function parameters + and call arguments as if they were first changed according to adjustments in + INNER and then by adjustments in OUTER. */ + +ipa_parm_adjustment_vec +ipa_combine_adjustments (ipa_parm_adjustment_vec inner, + ipa_parm_adjustment_vec outer) +{ + int i, outlen = outer.length (); + int inlen = inner.length (); + int removals = 0; + ipa_parm_adjustment_vec adjustments, tmp; + + tmp.create (inlen); + for (i = 0; i < inlen; i++) + { + struct ipa_parm_adjustment *n; + n = &inner[i]; + + if (n->op == IPA_PARM_OP_REMOVE) + removals++; + else + { + /* FIXME: Handling of new arguments are not implemented yet. */ + gcc_assert (n->op != IPA_PARM_OP_NEW); + tmp.quick_push (*n); + } + } + + adjustments.create (outlen + removals); + for (i = 0; i < outlen; i++) + { + struct ipa_parm_adjustment r; + struct ipa_parm_adjustment *out = &outer[i]; + struct ipa_parm_adjustment *in = &tmp[out->base_index]; + + memset (&r, 0, sizeof (r)); + gcc_assert (in->op != IPA_PARM_OP_REMOVE); + if (out->op == IPA_PARM_OP_REMOVE) + { + if (!index_in_adjustments_multiple_times_p (in->base_index, tmp)) + { + r.op = IPA_PARM_OP_REMOVE; + adjustments.quick_push (r); + } + continue; + } + else + { + /* FIXME: Handling of new arguments are not implemented yet. */ + gcc_assert (out->op != IPA_PARM_OP_NEW); + } + + r.base_index = in->base_index; + r.type = out->type; + + /* FIXME: Create nonlocal value too. */ + + if (in->op == IPA_PARM_OP_COPY && out->op == IPA_PARM_OP_COPY) + r.op = IPA_PARM_OP_COPY; + else if (in->op == IPA_PARM_OP_COPY) + r.offset = out->offset; + else if (out->op == IPA_PARM_OP_COPY) + r.offset = in->offset; + else + r.offset = in->offset + out->offset; + adjustments.quick_push (r); + } + + for (i = 0; i < inlen; i++) + { + struct ipa_parm_adjustment *n = &inner[i]; + + if (n->op == IPA_PARM_OP_REMOVE) + adjustments.quick_push (*n); + } + + tmp.release (); + return adjustments; +} + +/* If T is an SSA_NAME, return NULL if it is not a default def or + return its base variable if it is. If IGNORE_DEFAULT_DEF is true, + the base variable is always returned, regardless if it is a default + def. Return T if it is not an SSA_NAME. */ + +static tree +get_ssa_base_param (tree t, bool ignore_default_def) +{ + if (TREE_CODE (t) == SSA_NAME) + { + if (ignore_default_def || SSA_NAME_IS_DEFAULT_DEF (t)) + return SSA_NAME_VAR (t); + else + return NULL_TREE; + } + return t; +} + +/* Given an expression, return an adjustment entry specifying the + transformation to be done on EXPR. If no suitable adjustment entry + was found, returns NULL. + + If IGNORE_DEFAULT_DEF is set, consider SSA_NAMEs which are not a + default def, otherwise bail on them. + + If CONVERT is non-NULL, this function will set *CONVERT if the + expression provided is a component reference. ADJUSTMENTS is the + adjustments vector. */ + +ipa_parm_adjustment * +ipa_get_adjustment_candidate (tree **expr, bool *convert, + ipa_parm_adjustment_vec adjustments, + bool ignore_default_def) +{ + if (TREE_CODE (**expr) == BIT_FIELD_REF + || TREE_CODE (**expr) == IMAGPART_EXPR + || TREE_CODE (**expr) == REALPART_EXPR) + { + *expr = &TREE_OPERAND (**expr, 0); + if (convert) + *convert = true; + } + + HOST_WIDE_INT offset, size, max_size; + bool reverse; + tree base + = get_ref_base_and_extent (**expr, &offset, &size, &max_size, &reverse); + if (!base || size == -1 || max_size == -1) + return NULL; + + if (TREE_CODE (base) == MEM_REF) + { + offset += mem_ref_offset (base).to_short_addr () * BITS_PER_UNIT; + base = TREE_OPERAND (base, 0); + } + + base = get_ssa_base_param (base, ignore_default_def); + if (!base || TREE_CODE (base) != PARM_DECL) + return NULL; + + struct ipa_parm_adjustment *cand = NULL; + unsigned int len = adjustments.length (); + for (unsigned i = 0; i < len; i++) + { + struct ipa_parm_adjustment *adj = &adjustments[i]; + + if (adj->base == base + && (adj->offset == offset || adj->op == IPA_PARM_OP_REMOVE)) + { + cand = adj; + break; + } + } + + if (!cand || cand->op == IPA_PARM_OP_COPY || cand->op == IPA_PARM_OP_REMOVE) + return NULL; + return cand; +} + +/* If the expression *EXPR should be replaced by a reduction of a parameter, do + so. ADJUSTMENTS is a pointer to a vector of adjustments. CONVERT + specifies whether the function should care about type incompatibility the + current and new expressions. If it is false, the function will leave + incompatibility issues to the caller. Return true iff the expression + was modified. */ + +bool +ipa_modify_expr (tree *expr, bool convert, + ipa_parm_adjustment_vec adjustments) +{ + struct ipa_parm_adjustment *cand + = ipa_get_adjustment_candidate (&expr, &convert, adjustments, false); + if (!cand) + return false; + + tree src; + if (cand->by_ref) + { + src = build_simple_mem_ref (cand->new_decl); + REF_REVERSE_STORAGE_ORDER (src) = cand->reverse; + } + else + src = cand->new_decl; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "About to replace expr "); + print_generic_expr (dump_file, *expr); + fprintf (dump_file, " with "); + print_generic_expr (dump_file, src); + fprintf (dump_file, "\n"); + } + + if (convert && !useless_type_conversion_p (TREE_TYPE (*expr), cand->type)) + { + tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src); + *expr = vce; + } + else + *expr = src; + return true; +} + +/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human + friendly way, assuming they are meant to be applied to FNDECL. */ + +void +ipa_dump_param_adjustments (FILE *file, ipa_parm_adjustment_vec adjustments, + tree fndecl) +{ + int i, len = adjustments.length (); + bool first = true; + vec parms = ipa_get_vector_of_formal_parms (fndecl); + + fprintf (file, "IPA param adjustments: "); + for (i = 0; i < len; i++) + { + struct ipa_parm_adjustment *adj; + adj = &adjustments[i]; + + if (!first) + fprintf (file, " "); + else + first = false; + + fprintf (file, "%i. base_index: %i - ", i, adj->base_index); + print_generic_expr (file, parms[adj->base_index]); + if (adj->base) + { + fprintf (file, ", base: "); + print_generic_expr (file, adj->base); + } + if (adj->new_decl) + { + fprintf (file, ", new_decl: "); + print_generic_expr (file, adj->new_decl); + } + if (adj->new_ssa_base) + { + fprintf (file, ", new_ssa_base: "); + print_generic_expr (file, adj->new_ssa_base); + } + + if (adj->op == IPA_PARM_OP_COPY) + fprintf (file, ", copy_param"); + else if (adj->op == IPA_PARM_OP_REMOVE) + fprintf (file, ", remove_param"); + else + fprintf (file, ", offset %li", (long) adj->offset); + if (adj->by_ref) + fprintf (file, ", by_ref"); + print_node_brief (file, ", type: ", adj->type, 0); + fprintf (file, "\n"); + } + parms.release (); +} + diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h new file mode 100644 index 00000000000..364e4489c29 --- /dev/null +++ b/gcc/ipa-param-manipulation.h @@ -0,0 +1,120 @@ +/* Manipulation of formal and actual parameters of functions and function + calls. + Copyright (C) 2017 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef IPA_PARAM_MANIPULATION_H +#define IPA_PARAM_MANIPULATION_H + +/* Operation to be performed for the parameter in ipa_parm_adjustment + below. */ +enum ipa_parm_op { + IPA_PARM_OP_NONE, + + /* This describes a brand new parameter. + + The field `type' should be set to the new type, `arg_prefix' + should be set to the string prefix for the new DECL_NAME, and + `new_decl' will ultimately hold the newly created argument. */ + IPA_PARM_OP_NEW, + + /* This new parameter is an unmodified parameter at index base_index. */ + IPA_PARM_OP_COPY, + + /* This adjustment describes a parameter that is about to be removed + completely. Most users will probably need to book keep those so that they + don't leave behinfd any non default def ssa names belonging to them. */ + IPA_PARM_OP_REMOVE +}; + +/* Structure to describe transformations of formal parameters and actual + arguments. Each instance describes one new parameter and they are meant to + be stored in a vector. Additionally, most users will probably want to store + adjustments about parameters that are being removed altogether so that SSA + names belonging to them can be replaced by SSA names of an artificial + variable. */ +struct ipa_parm_adjustment +{ + /* The original PARM_DECL itself, helpful for processing of the body of the + function itself. Intended for traversing function bodies. + ipa_modify_formal_parameters, ipa_modify_call_arguments and + ipa_combine_adjustments ignore this and use base_index. + ipa_modify_formal_parameters actually sets this. */ + tree base; + + /* Type of the new parameter. However, if by_ref is true, the real type will + be a pointer to this type. */ + tree type; + + /* Alias refrerence type to be used in MEM_REFs when adjusting caller + arguments. */ + tree alias_ptr_type; + + /* The new declaration when creating/replacing a parameter. Created + by ipa_modify_formal_parameters, useful for functions modifying + the body accordingly. For brand new arguments, this is the newly + created argument. */ + tree new_decl; + + /* New declaration of a substitute variable that we may use to replace all + non-default-def ssa names when a parm decl is going away. */ + tree new_ssa_base; + + /* If non-NULL and the original parameter is to be removed (copy_param below + is NULL), this is going to be its nonlocalized vars value. */ + tree nonlocal_value; + + /* This holds the prefix to be used for the new DECL_NAME. */ + const char *arg_prefix; + + /* Offset into the original parameter (for the cases when the new parameter + is a component of an original one). */ + HOST_WIDE_INT offset; + + /* Zero based index of the original parameter this one is based on. */ + int base_index; + + /* Whether this parameter is a new parameter, a copy of an old one, + or one about to be removed. */ + enum ipa_parm_op op; + + /* Storage order of the original parameter (for the cases when the new + parameter is a component of an original one). */ + unsigned reverse : 1; + + /* The parameter is to be passed by reference. */ + unsigned by_ref : 1; +}; + +typedef vec ipa_parm_adjustment_vec; + +vec ipa_get_vector_of_formal_parms (tree fndecl); +vec ipa_get_vector_of_formal_parm_types (tree fntype); +void ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec); +void ipa_modify_call_arguments (struct cgraph_edge *, gcall *, + ipa_parm_adjustment_vec); +ipa_parm_adjustment_vec ipa_combine_adjustments (ipa_parm_adjustment_vec, + ipa_parm_adjustment_vec); +void ipa_dump_param_adjustments (FILE *, ipa_parm_adjustment_vec, tree); + +bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec); +ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *, + ipa_parm_adjustment_vec, + bool); + +#endif /* IPA_PARAM_MANIPULATION_H */ diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index a687f7cb29e..729251714de 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -4050,731 +4050,6 @@ ipa_print_all_params (FILE * f) ipa_print_node_params (f, node); } -/* Return a heap allocated vector containing formal parameters of FNDECL. */ - -vec -ipa_get_vector_of_formal_parms (tree fndecl) -{ - vec args; - int count; - tree parm; - - gcc_assert (!flag_wpa); - count = count_formal_params (fndecl); - args.create (count); - for (parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm)) - args.quick_push (parm); - - return args; -} - -/* Return a heap allocated vector containing types of formal parameters of - function type FNTYPE. */ - -vec -ipa_get_vector_of_formal_parm_types (tree fntype) -{ - vec types; - int count = 0; - tree t; - - for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t)) - count++; - - types.create (count); - for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t)) - types.quick_push (TREE_VALUE (t)); - - return types; -} - -/* Modify the function declaration FNDECL and its type according to the plan in - ADJUSTMENTS. It also sets base fields of individual adjustments structures - to reflect the actual parameters being modified which are determined by the - base_index field. */ - -void -ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments) -{ - vec oparms = ipa_get_vector_of_formal_parms (fndecl); - tree orig_type = TREE_TYPE (fndecl); - tree old_arg_types = TYPE_ARG_TYPES (orig_type); - - /* The following test is an ugly hack, some functions simply don't have any - arguments in their type. This is probably a bug but well... */ - bool care_for_types = (old_arg_types != NULL_TREE); - bool last_parm_void; - vec otypes; - if (care_for_types) - { - last_parm_void = (TREE_VALUE (tree_last (old_arg_types)) - == void_type_node); - otypes = ipa_get_vector_of_formal_parm_types (orig_type); - if (last_parm_void) - gcc_assert (oparms.length () + 1 == otypes.length ()); - else - gcc_assert (oparms.length () == otypes.length ()); - } - else - { - last_parm_void = false; - otypes.create (0); - } - - int len = adjustments.length (); - tree *link = &DECL_ARGUMENTS (fndecl); - tree new_arg_types = NULL; - for (int i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - gcc_assert (link); - - adj = &adjustments[i]; - tree parm; - if (adj->op == IPA_PARM_OP_NEW) - parm = NULL; - else - parm = oparms[adj->base_index]; - adj->base = parm; - - if (adj->op == IPA_PARM_OP_COPY) - { - if (care_for_types) - new_arg_types = tree_cons (NULL_TREE, otypes[adj->base_index], - new_arg_types); - *link = parm; - link = &DECL_CHAIN (parm); - } - else if (adj->op != IPA_PARM_OP_REMOVE) - { - tree new_parm; - tree ptype; - - if (adj->by_ref) - ptype = build_pointer_type (adj->type); - else - { - ptype = adj->type; - if (is_gimple_reg_type (ptype) - && TYPE_MODE (ptype) != BLKmode) - { - unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ptype)); - if (TYPE_ALIGN (ptype) != malign) - ptype = build_aligned_type (ptype, malign); - } - } - - if (care_for_types) - new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types); - - new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE, - ptype); - const char *prefix = adj->arg_prefix ? adj->arg_prefix : "SYNTH"; - DECL_NAME (new_parm) = create_tmp_var_name (prefix); - DECL_ARTIFICIAL (new_parm) = 1; - DECL_ARG_TYPE (new_parm) = ptype; - DECL_CONTEXT (new_parm) = fndecl; - TREE_USED (new_parm) = 1; - DECL_IGNORED_P (new_parm) = 1; - layout_decl (new_parm, 0); - - if (adj->op == IPA_PARM_OP_NEW) - adj->base = NULL; - else - adj->base = parm; - adj->new_decl = new_parm; - - *link = new_parm; - link = &DECL_CHAIN (new_parm); - } - } - - *link = NULL_TREE; - - tree new_reversed = NULL; - if (care_for_types) - { - new_reversed = nreverse (new_arg_types); - if (last_parm_void) - { - if (new_reversed) - TREE_CHAIN (new_arg_types) = void_list_node; - else - new_reversed = void_list_node; - } - } - - /* Use copy_node to preserve as much as possible from original type - (debug info, attribute lists etc.) - Exception is METHOD_TYPEs must have THIS argument. - When we are asked to remove it, we need to build new FUNCTION_TYPE - instead. */ - tree new_type = NULL; - if (TREE_CODE (orig_type) != METHOD_TYPE - || (adjustments[0].op == IPA_PARM_OP_COPY - && adjustments[0].base_index == 0)) - { - new_type = build_distinct_type_copy (orig_type); - TYPE_ARG_TYPES (new_type) = new_reversed; - } - else - { - new_type - = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type), - new_reversed)); - TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type); - DECL_VINDEX (fndecl) = NULL_TREE; - } - - /* When signature changes, we need to clear builtin info. */ - if (DECL_BUILT_IN (fndecl)) - { - DECL_BUILT_IN_CLASS (fndecl) = NOT_BUILT_IN; - DECL_FUNCTION_CODE (fndecl) = (enum built_in_function) 0; - } - - TREE_TYPE (fndecl) = new_type; - DECL_VIRTUAL_P (fndecl) = 0; - DECL_LANG_SPECIFIC (fndecl) = NULL; - otypes.release (); - oparms.release (); -} - -/* Modify actual arguments of a function call CS as indicated in ADJUSTMENTS. - If this is a directly recursive call, CS must be NULL. Otherwise it must - contain the corresponding call graph edge. */ - -void -ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt, - ipa_parm_adjustment_vec adjustments) -{ - struct cgraph_node *current_node = cgraph_node::get (current_function_decl); - vec vargs; - vec **debug_args = NULL; - gcall *new_stmt; - gimple_stmt_iterator gsi, prev_gsi; - tree callee_decl; - int i, len; - - len = adjustments.length (); - vargs.create (len); - callee_decl = !cs ? gimple_call_fndecl (stmt) : cs->callee->decl; - current_node->remove_stmt_references (stmt); - - gsi = gsi_for_stmt (stmt); - prev_gsi = gsi; - gsi_prev (&prev_gsi); - for (i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - - adj = &adjustments[i]; - - if (adj->op == IPA_PARM_OP_COPY) - { - tree arg = gimple_call_arg (stmt, adj->base_index); - - vargs.quick_push (arg); - } - else if (adj->op != IPA_PARM_OP_REMOVE) - { - tree expr, base, off; - location_t loc; - unsigned int deref_align = 0; - bool deref_base = false; - - /* We create a new parameter out of the value of the old one, we can - do the following kind of transformations: - - - A scalar passed by reference is converted to a scalar passed by - value. (adj->by_ref is false and the type of the original - actual argument is a pointer to a scalar). - - - A part of an aggregate is passed instead of the whole aggregate. - The part can be passed either by value or by reference, this is - determined by value of adj->by_ref. Moreover, the code below - handles both situations when the original aggregate is passed by - value (its type is not a pointer) and when it is passed by - reference (it is a pointer to an aggregate). - - When the new argument is passed by reference (adj->by_ref is true) - it must be a part of an aggregate and therefore we form it by - simply taking the address of a reference inside the original - aggregate. */ - - gcc_checking_assert (adj->offset % BITS_PER_UNIT == 0); - base = gimple_call_arg (stmt, adj->base_index); - loc = DECL_P (base) ? DECL_SOURCE_LOCATION (base) - : EXPR_LOCATION (base); - - if (TREE_CODE (base) != ADDR_EXPR - && POINTER_TYPE_P (TREE_TYPE (base))) - off = build_int_cst (adj->alias_ptr_type, - adj->offset / BITS_PER_UNIT); - else - { - HOST_WIDE_INT base_offset; - tree prev_base; - bool addrof; - - if (TREE_CODE (base) == ADDR_EXPR) - { - base = TREE_OPERAND (base, 0); - addrof = true; - } - else - addrof = false; - prev_base = base; - base = get_addr_base_and_unit_offset (base, &base_offset); - /* Aggregate arguments can have non-invariant addresses. */ - if (!base) - { - base = build_fold_addr_expr (prev_base); - off = build_int_cst (adj->alias_ptr_type, - adj->offset / BITS_PER_UNIT); - } - else if (TREE_CODE (base) == MEM_REF) - { - if (!addrof) - { - deref_base = true; - deref_align = TYPE_ALIGN (TREE_TYPE (base)); - } - off = build_int_cst (adj->alias_ptr_type, - base_offset - + adj->offset / BITS_PER_UNIT); - off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), - off); - base = TREE_OPERAND (base, 0); - } - else - { - off = build_int_cst (adj->alias_ptr_type, - base_offset - + adj->offset / BITS_PER_UNIT); - base = build_fold_addr_expr (base); - } - } - - if (!adj->by_ref) - { - tree type = adj->type; - unsigned int align; - unsigned HOST_WIDE_INT misalign; - - if (deref_base) - { - align = deref_align; - misalign = 0; - } - else - { - get_pointer_alignment_1 (base, &align, &misalign); - if (TYPE_ALIGN (type) > align) - align = TYPE_ALIGN (type); - } - misalign += (offset_int::from (wi::to_wide (off), - SIGNED).to_short_addr () - * BITS_PER_UNIT); - misalign = misalign & (align - 1); - if (misalign != 0) - align = least_bit_hwi (misalign); - if (align < TYPE_ALIGN (type)) - type = build_aligned_type (type, align); - base = force_gimple_operand_gsi (&gsi, base, - true, NULL, true, GSI_SAME_STMT); - expr = fold_build2_loc (loc, MEM_REF, type, base, off); - REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse; - /* If expr is not a valid gimple call argument emit - a load into a temporary. */ - if (is_gimple_reg_type (TREE_TYPE (expr))) - { - gimple *tem = gimple_build_assign (NULL_TREE, expr); - if (gimple_in_ssa_p (cfun)) - { - gimple_set_vuse (tem, gimple_vuse (stmt)); - expr = make_ssa_name (TREE_TYPE (expr), tem); - } - else - expr = create_tmp_reg (TREE_TYPE (expr)); - gimple_assign_set_lhs (tem, expr); - gsi_insert_before (&gsi, tem, GSI_SAME_STMT); - } - } - else - { - expr = fold_build2_loc (loc, MEM_REF, adj->type, base, off); - REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse; - expr = build_fold_addr_expr (expr); - expr = force_gimple_operand_gsi (&gsi, expr, - true, NULL, true, GSI_SAME_STMT); - } - vargs.quick_push (expr); - } - if (adj->op != IPA_PARM_OP_COPY && MAY_HAVE_DEBUG_STMTS) - { - unsigned int ix; - tree ddecl = NULL_TREE, origin = DECL_ORIGIN (adj->base), arg; - gimple *def_temp; - - arg = gimple_call_arg (stmt, adj->base_index); - if (!useless_type_conversion_p (TREE_TYPE (origin), TREE_TYPE (arg))) - { - if (!fold_convertible_p (TREE_TYPE (origin), arg)) - continue; - arg = fold_convert_loc (gimple_location (stmt), - TREE_TYPE (origin), arg); - } - if (debug_args == NULL) - debug_args = decl_debug_args_insert (callee_decl); - for (ix = 0; vec_safe_iterate (*debug_args, ix, &ddecl); ix += 2) - if (ddecl == origin) - { - ddecl = (**debug_args)[ix + 1]; - break; - } - if (ddecl == NULL) - { - ddecl = make_node (DEBUG_EXPR_DECL); - DECL_ARTIFICIAL (ddecl) = 1; - TREE_TYPE (ddecl) = TREE_TYPE (origin); - SET_DECL_MODE (ddecl, DECL_MODE (origin)); - - vec_safe_push (*debug_args, origin); - vec_safe_push (*debug_args, ddecl); - } - def_temp = gimple_build_debug_bind (ddecl, unshare_expr (arg), stmt); - gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT); - } - } - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "replacing stmt:"); - print_gimple_stmt (dump_file, gsi_stmt (gsi), 0); - } - - new_stmt = gimple_build_call_vec (callee_decl, vargs); - vargs.release (); - if (gimple_call_lhs (stmt)) - gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); - - gimple_set_block (new_stmt, gimple_block (stmt)); - if (gimple_has_location (stmt)) - gimple_set_location (new_stmt, gimple_location (stmt)); - gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); - gimple_call_copy_flags (new_stmt, stmt); - if (gimple_in_ssa_p (cfun)) - { - gimple_set_vuse (new_stmt, gimple_vuse (stmt)); - if (gimple_vdef (stmt)) - { - gimple_set_vdef (new_stmt, gimple_vdef (stmt)); - SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt; - } - } - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "with stmt:"); - print_gimple_stmt (dump_file, new_stmt, 0); - fprintf (dump_file, "\n"); - } - gsi_replace (&gsi, new_stmt, true); - if (cs) - cs->set_call_stmt (new_stmt); - do - { - current_node->record_stmt_references (gsi_stmt (gsi)); - gsi_prev (&gsi); - } - while (gsi_stmt (gsi) != gsi_stmt (prev_gsi)); -} - -/* If the expression *EXPR should be replaced by a reduction of a parameter, do - so. ADJUSTMENTS is a pointer to a vector of adjustments. CONVERT - specifies whether the function should care about type incompatibility the - current and new expressions. If it is false, the function will leave - incompatibility issues to the caller. Return true iff the expression - was modified. */ - -bool -ipa_modify_expr (tree *expr, bool convert, - ipa_parm_adjustment_vec adjustments) -{ - struct ipa_parm_adjustment *cand - = ipa_get_adjustment_candidate (&expr, &convert, adjustments, false); - if (!cand) - return false; - - tree src; - if (cand->by_ref) - { - src = build_simple_mem_ref (cand->new_decl); - REF_REVERSE_STORAGE_ORDER (src) = cand->reverse; - } - else - src = cand->new_decl; - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "About to replace expr "); - print_generic_expr (dump_file, *expr); - fprintf (dump_file, " with "); - print_generic_expr (dump_file, src); - fprintf (dump_file, "\n"); - } - - if (convert && !useless_type_conversion_p (TREE_TYPE (*expr), cand->type)) - { - tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src); - *expr = vce; - } - else - *expr = src; - return true; -} - -/* If T is an SSA_NAME, return NULL if it is not a default def or - return its base variable if it is. If IGNORE_DEFAULT_DEF is true, - the base variable is always returned, regardless if it is a default - def. Return T if it is not an SSA_NAME. */ - -static tree -get_ssa_base_param (tree t, bool ignore_default_def) -{ - if (TREE_CODE (t) == SSA_NAME) - { - if (ignore_default_def || SSA_NAME_IS_DEFAULT_DEF (t)) - return SSA_NAME_VAR (t); - else - return NULL_TREE; - } - return t; -} - -/* Given an expression, return an adjustment entry specifying the - transformation to be done on EXPR. If no suitable adjustment entry - was found, returns NULL. - - If IGNORE_DEFAULT_DEF is set, consider SSA_NAMEs which are not a - default def, otherwise bail on them. - - If CONVERT is non-NULL, this function will set *CONVERT if the - expression provided is a component reference. ADJUSTMENTS is the - adjustments vector. */ - -ipa_parm_adjustment * -ipa_get_adjustment_candidate (tree **expr, bool *convert, - ipa_parm_adjustment_vec adjustments, - bool ignore_default_def) -{ - if (TREE_CODE (**expr) == BIT_FIELD_REF - || TREE_CODE (**expr) == IMAGPART_EXPR - || TREE_CODE (**expr) == REALPART_EXPR) - { - *expr = &TREE_OPERAND (**expr, 0); - if (convert) - *convert = true; - } - - HOST_WIDE_INT offset, size, max_size; - bool reverse; - tree base - = get_ref_base_and_extent (**expr, &offset, &size, &max_size, &reverse); - if (!base || size == -1 || max_size == -1) - return NULL; - - if (TREE_CODE (base) == MEM_REF) - { - offset += mem_ref_offset (base).to_short_addr () * BITS_PER_UNIT; - base = TREE_OPERAND (base, 0); - } - - base = get_ssa_base_param (base, ignore_default_def); - if (!base || TREE_CODE (base) != PARM_DECL) - return NULL; - - struct ipa_parm_adjustment *cand = NULL; - unsigned int len = adjustments.length (); - for (unsigned i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj = &adjustments[i]; - - if (adj->base == base - && (adj->offset == offset || adj->op == IPA_PARM_OP_REMOVE)) - { - cand = adj; - break; - } - } - - if (!cand || cand->op == IPA_PARM_OP_COPY || cand->op == IPA_PARM_OP_REMOVE) - return NULL; - return cand; -} - -/* Return true iff BASE_INDEX is in ADJUSTMENTS more than once. */ - -static bool -index_in_adjustments_multiple_times_p (int base_index, - ipa_parm_adjustment_vec adjustments) -{ - int i, len = adjustments.length (); - bool one = false; - - for (i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - adj = &adjustments[i]; - - if (adj->base_index == base_index) - { - if (one) - return true; - else - one = true; - } - } - return false; -} - - -/* Return adjustments that should have the same effect on function parameters - and call arguments as if they were first changed according to adjustments in - INNER and then by adjustments in OUTER. */ - -ipa_parm_adjustment_vec -ipa_combine_adjustments (ipa_parm_adjustment_vec inner, - ipa_parm_adjustment_vec outer) -{ - int i, outlen = outer.length (); - int inlen = inner.length (); - int removals = 0; - ipa_parm_adjustment_vec adjustments, tmp; - - tmp.create (inlen); - for (i = 0; i < inlen; i++) - { - struct ipa_parm_adjustment *n; - n = &inner[i]; - - if (n->op == IPA_PARM_OP_REMOVE) - removals++; - else - { - /* FIXME: Handling of new arguments are not implemented yet. */ - gcc_assert (n->op != IPA_PARM_OP_NEW); - tmp.quick_push (*n); - } - } - - adjustments.create (outlen + removals); - for (i = 0; i < outlen; i++) - { - struct ipa_parm_adjustment r; - struct ipa_parm_adjustment *out = &outer[i]; - struct ipa_parm_adjustment *in = &tmp[out->base_index]; - - memset (&r, 0, sizeof (r)); - gcc_assert (in->op != IPA_PARM_OP_REMOVE); - if (out->op == IPA_PARM_OP_REMOVE) - { - if (!index_in_adjustments_multiple_times_p (in->base_index, tmp)) - { - r.op = IPA_PARM_OP_REMOVE; - adjustments.quick_push (r); - } - continue; - } - else - { - /* FIXME: Handling of new arguments are not implemented yet. */ - gcc_assert (out->op != IPA_PARM_OP_NEW); - } - - r.base_index = in->base_index; - r.type = out->type; - - /* FIXME: Create nonlocal value too. */ - - if (in->op == IPA_PARM_OP_COPY && out->op == IPA_PARM_OP_COPY) - r.op = IPA_PARM_OP_COPY; - else if (in->op == IPA_PARM_OP_COPY) - r.offset = out->offset; - else if (out->op == IPA_PARM_OP_COPY) - r.offset = in->offset; - else - r.offset = in->offset + out->offset; - adjustments.quick_push (r); - } - - for (i = 0; i < inlen; i++) - { - struct ipa_parm_adjustment *n = &inner[i]; - - if (n->op == IPA_PARM_OP_REMOVE) - adjustments.quick_push (*n); - } - - tmp.release (); - return adjustments; -} - -/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human - friendly way, assuming they are meant to be applied to FNDECL. */ - -void -ipa_dump_param_adjustments (FILE *file, ipa_parm_adjustment_vec adjustments, - tree fndecl) -{ - int i, len = adjustments.length (); - bool first = true; - vec parms = ipa_get_vector_of_formal_parms (fndecl); - - fprintf (file, "IPA param adjustments: "); - for (i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - adj = &adjustments[i]; - - if (!first) - fprintf (file, " "); - else - first = false; - - fprintf (file, "%i. base_index: %i - ", i, adj->base_index); - print_generic_expr (file, parms[adj->base_index]); - if (adj->base) - { - fprintf (file, ", base: "); - print_generic_expr (file, adj->base); - } - if (adj->new_decl) - { - fprintf (file, ", new_decl: "); - print_generic_expr (file, adj->new_decl); - } - if (adj->new_ssa_base) - { - fprintf (file, ", new_ssa_base: "); - print_generic_expr (file, adj->new_ssa_base); - } - - if (adj->op == IPA_PARM_OP_COPY) - fprintf (file, ", copy_param"); - else if (adj->op == IPA_PARM_OP_REMOVE) - fprintf (file, ", remove_param"); - else - fprintf (file, ", offset %li", (long) adj->offset); - if (adj->by_ref) - fprintf (file, ", by_ref"); - print_node_brief (file, ", type: ", adj->type, 0); - fprintf (file, "\n"); - } - parms.release (); -} - /* Dump the AV linked list. */ void diff --git a/gcc/ipa-prop.h b/gcc/ipa-prop.h index fa5bed49ee0..bd8ae3e8dae 100644 --- a/gcc/ipa-prop.h +++ b/gcc/ipa-prop.h @@ -765,96 +765,6 @@ class ipcp_agg_lattice; extern object_allocator ipcp_agg_lattice_pool; -/* Operation to be performed for the parameter in ipa_parm_adjustment - below. */ -enum ipa_parm_op { - IPA_PARM_OP_NONE, - - /* This describes a brand new parameter. - - The field `type' should be set to the new type, `arg_prefix' - should be set to the string prefix for the new DECL_NAME, and - `new_decl' will ultimately hold the newly created argument. */ - IPA_PARM_OP_NEW, - - /* This new parameter is an unmodified parameter at index base_index. */ - IPA_PARM_OP_COPY, - - /* This adjustment describes a parameter that is about to be removed - completely. Most users will probably need to book keep those so that they - don't leave behinfd any non default def ssa names belonging to them. */ - IPA_PARM_OP_REMOVE -}; - -/* Structure to describe transformations of formal parameters and actual - arguments. Each instance describes one new parameter and they are meant to - be stored in a vector. Additionally, most users will probably want to store - adjustments about parameters that are being removed altogether so that SSA - names belonging to them can be replaced by SSA names of an artificial - variable. */ -struct ipa_parm_adjustment -{ - /* The original PARM_DECL itself, helpful for processing of the body of the - function itself. Intended for traversing function bodies. - ipa_modify_formal_parameters, ipa_modify_call_arguments and - ipa_combine_adjustments ignore this and use base_index. - ipa_modify_formal_parameters actually sets this. */ - tree base; - - /* Type of the new parameter. However, if by_ref is true, the real type will - be a pointer to this type. */ - tree type; - - /* Alias refrerence type to be used in MEM_REFs when adjusting caller - arguments. */ - tree alias_ptr_type; - - /* The new declaration when creating/replacing a parameter. Created - by ipa_modify_formal_parameters, useful for functions modifying - the body accordingly. For brand new arguments, this is the newly - created argument. */ - tree new_decl; - - /* New declaration of a substitute variable that we may use to replace all - non-default-def ssa names when a parm decl is going away. */ - tree new_ssa_base; - - /* If non-NULL and the original parameter is to be removed (copy_param below - is NULL), this is going to be its nonlocalized vars value. */ - tree nonlocal_value; - - /* This holds the prefix to be used for the new DECL_NAME. */ - const char *arg_prefix; - - /* Offset into the original parameter (for the cases when the new parameter - is a component of an original one). */ - HOST_WIDE_INT offset; - - /* Zero based index of the original parameter this one is based on. */ - int base_index; - - /* Whether this parameter is a new parameter, a copy of an old one, - or one about to be removed. */ - enum ipa_parm_op op; - - /* Storage order of the original parameter (for the cases when the new - parameter is a component of an original one). */ - unsigned reverse : 1; - - /* The parameter is to be passed by reference. */ - unsigned by_ref : 1; -}; - -typedef vec ipa_parm_adjustment_vec; - -vec ipa_get_vector_of_formal_parms (tree fndecl); -vec ipa_get_vector_of_formal_parm_types (tree fntype); -void ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec); -void ipa_modify_call_arguments (struct cgraph_edge *, gcall *, - ipa_parm_adjustment_vec); -ipa_parm_adjustment_vec ipa_combine_adjustments (ipa_parm_adjustment_vec, - ipa_parm_adjustment_vec); -void ipa_dump_param_adjustments (FILE *, ipa_parm_adjustment_vec, tree); void ipa_dump_agg_replacement_values (FILE *f, struct ipa_agg_replacement_value *av); void ipa_prop_write_jump_functions (void); @@ -870,10 +780,6 @@ ipa_polymorphic_call_context ipa_context_from_jfunc (ipa_node_params *, int, ipa_jump_func *); void ipa_dump_param (FILE *, struct ipa_node_params *info, int i); -bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec); -ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *, - ipa_parm_adjustment_vec, - bool); void ipa_release_body_info (struct ipa_func_body_info *); tree ipa_get_callee_param_type (struct cgraph_edge *e, int i); diff --git a/gcc/omp-simd-clone.c b/gcc/omp-simd-clone.c index 37a5f64a549..b8386037dfd 100644 --- a/gcc/omp-simd-clone.c +++ b/gcc/omp-simd-clone.c @@ -45,7 +45,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-dfa.h" #include "cfgloop.h" #include "symbol-summary.h" -#include "ipa-prop.h" +#include "ipa-param-manipulation.h" #include "tree-eh.h" #include "varasm.h" #include "stringpool.h" diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index bac593951e7..db490b20c3e 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -97,6 +97,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-dfa.h" #include "tree-ssa.h" #include "symbol-summary.h" +#include "ipa-param-manipulation.h" #include "ipa-prop.h" #include "params.h" #include "dbgcnt.h" From patchwork Wed Nov 8 20:54:51 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Jambor X-Patchwork-Id: 835986 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-466331-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="QXenqWU4"; dkim-atps=neutral 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 3yXJTq24NBz9rxj for ; Thu, 9 Nov 2017 07:55:26 +1100 (AEDT) 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=YYvGe9864aE1wiqrOwUuPIOlXwZtb/t15N908kR+iUWlDn02ka hYaRs8IjM7RU7wQxyJUOC0XtIojvgUASAQr/WUObxThDq9v0vllpsgmzM97YIcuT POwZd9iZrxBc6Q7Kxfk2Upg+3ThrEdMlEKWefW026YgX7ZURsQdS0sRQk= 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=56XJeeC/daBw/aLa9QBrL5zb4wM=; b=QXenqWU4hB0ildl7eriT dDvyEqDGsxA/REK/Io3hIONk+ezP9DmWWy8sFaVMA/tgD0wpfn6BYJQ0j+X+fODZ BTCsSX7zEhsi1t6xAY03Z3xKZhyl3z8l7Xnd5W+DHYmNfePjOzyaR5oRbHKBL0S3 DbcTqgPcx6UImL9xJSj+swo= Received: (qmail 27855 invoked by alias); 8 Nov 2017 20:55:08 -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 27760 invoked by uid 89); 8 Nov 2017 20:55:06 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, SPF_PASS autolearn=ham version=3.3.2 spammy=relating 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 ESMTP; Wed, 08 Nov 2017 20:54:55 +0000 Received: from relay1.suse.de (charybdis-ext.suse.de [195.135.220.254]) by mx2.suse.de (Postfix) with ESMTP id 16E3DAD09 for ; Wed, 8 Nov 2017 20:54:52 +0000 (UTC) Date: Wed, 8 Nov 2017 21:54:51 +0100 From: Martin Jambor To: GCC Patches Cc: Jan Hubicka Subject: [PATCH 2/2] Reimplementation of param-manipulation Message-ID: <20171108205451.tofv6d52zvd322i3@virgil.suse.cz> MIME-Version: 1.0 Content-Disposition: inline User-Agent: NeoMutt/20170912 (1.9.0) X-IsSubscribed: yes Hi, this patch is a substantial rewrite of function parameter manipulations currently used by IPA-SRA and OpenMP SIMD cloning. I started it with the aim to cleanup the code but also to make the data structures as small as possible so that, for real IPA-SRA, I can attach them to call graph nodes instead of args_to_skip bitmaps. That is what the ipa_param_adjustments is intended to be, although it is not attached to a cgraph node just yet. This class is also capable of modifying call-sites. Additionally, there is a new class ipa_param_body_adjustments for modifying signatures, types and bodies of callees, which is not expected to live long and so code clarity was emphasized over data structure compactness. Furthermore, the interface is cleaner, the user simply describes what new parameters should look like and does not need to care about various data fields required to make the changes happen like today. Also, some functionality like SSA name remapping is now shared among the two users while now both implement it themselves. Although I think the patch is a clear improvement, there may be reasons to wait with committing. one drawback is that ipa_param_adjustments et. al. are now in GC memory, which will be necessary once they are attached to call graph but now it is just useless. It is not a lot of memory though. In any event, this patch also passes bootstrap and testing on x86_64-linux. Thanks, Martin 2017-11-07 Martin Jambor * ipa-param-manipulation.h (IPA_PARAM_PREFIX_SYNTH): New. (IPA_PARAM_PREFIX_ISRA): Likewise. (IPA_PARAM_PREFIX_SIMD): Likewise. (IPA_PARAM_PREFIX_MASK): Likewise. (IPA_PARAM_MAX_INDEX_BITS): Likewise. (ipa_parm_op): Changed element prefixes to IPA_PARAM_OP, renamed IPA_PARM_OP_NONE to IPA_PARAM_OP_UNDEFINED, reordered elements. (ipa_parm_adjustment): Removed. (ipa_parm_adjustment_vec): Likewise. (ipa_modify_formal_parameters): Removed declaration. (ipa_modify_call_arguments): Likewise. (ipa_combine_adjustments): Likewise. (ipa_dump_param_adjustments): Likewise. (ipa_modify_expr): Likewise. (ipa_get_adjustment_candidate): Likewise. (ipa_adjusted_param): Likewise. (ipa_param_adjustments): Likewise. (ipa_param_body_replacement): Likewise. (ipa_param_body_adjustments): Likewise. * Makefile.in (GTFILES): Added ipa-param-manipulation.h. * ipa-param-manipulation.c: Included tree-eh.h and tree-cfg.h. (ipa_modify_formal_parameters): Removed. (ipa_modify_call_arguments): Likewise. (index_in_adjustments_multiple_times_p): Likewise. (ipa_combine_adjustments): Likewise. (ipa_get_adjustment_candidate): Likewise. (ipa_modify_expr): Likewise. (ipa_dump_param_adjustments): Likewise. (ipa_param_prefixes): New. (ipa_param_adjustments::ipa_param_adjustments): Likewise. (ipa_param_adjustments::init): Likewise. (ipa_param_adjustments::modify_call_arguments): Likewise. (modify_call_arguments): Likewise. (ipa_param_op_names): Likewise. (ipa_dump_adjusted_parameters): Likewise. (ipa_param_body_adjustments::ipa_param_body_adjustments): Likewise. (register_replacement): Likewise. (modify_formal_parameters): Likewise. (lookup_replacement): Likewise. (get_expr_replacement): Likewise. (get_replacement_ssa_base): Likewise. (replace_removed_params_ssa_names): Likewise. (modify_expr): Likewise. (modify_assignment): Likewise. (modify_cfun_body): Likewise. (reset_debug_stmts): Likewise. (perform_cfun_body_modifications): Likewise. * omp-simd-clone.c (simd_clone_adjust_argument_types): Adjusted to use new parameter manipulation structures. (simd_clone_init_simd_arrays): Likewise. (modify_stmt_info): Likewise. (ipa_simd_modify_stmt_ops): Likewise. (ipa_simd_modify_function_body): Likewise. (simd_clone_adjust): Likewise. * tree-sra.c (turn_representatives_into_adjustments): Likewise. (analyze_all_param_acesses): Likewise. (replace_removed_params_ssa_names): Removed. (get_adjustment_for_base): Likewise. (replace_removed_params_ssa_names): Likewise. (sra_ipa_modify_assign): Likewise. (ipa_sra_modify_function_body): Likewise. (sra_ipa_reset_debug_stmts): Likewise. (convert_callers_data): New type. (convert_callers_for_node): Adjusted to use new parameter manipulation structures. (convert_callers): Likewise. (modify_function): Likewise. Flip order of modifying callers and callees. (ipa_early_sra): Adjusted to use new parameter manipulation structures. * testsuite/gcc.dg/ipa/ipa-sra-3.c: Modified dump scan. --- gcc/Makefile.in | 2 +- gcc/ipa-param-manipulation.c | 1283 ++++++++++++++++++++++------------ gcc/ipa-param-manipulation.h | 191 +++-- gcc/omp-simd-clone.c | 193 ++--- gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c | 5 +- gcc/tree-sra.c | 503 ++----------- 6 files changed, 1116 insertions(+), 1061 deletions(-) diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 1bb1d6ec0ff..3751aa3a218 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -2510,7 +2510,7 @@ GTFILES = $(CPP_ID_DATA_H) $(srcdir)/input.h $(srcdir)/coretypes.h \ $(srcdir)/reload.h $(srcdir)/caller-save.c $(srcdir)/symtab.c \ $(srcdir)/alias.c $(srcdir)/bitmap.c $(srcdir)/cselib.c $(srcdir)/cgraph.c \ $(srcdir)/ipa-prop.c $(srcdir)/ipa-cp.c $(srcdir)/ipa-utils.h \ - $(srcdir)/dbxout.c \ + $(srcdir)/ipa-param-manipulation.h $(srcdir)/dbxout.c \ $(srcdir)/signop.h \ $(srcdir)/dwarf2out.h \ $(srcdir)/dwarf2asm.c \ diff --git a/gcc/ipa-param-manipulation.c b/gcc/ipa-param-manipulation.c index bedd201f6dd..1b5e28e5859 100644 --- a/gcc/ipa-param-manipulation.c +++ b/gcc/ipa-param-manipulation.c @@ -28,15 +28,23 @@ along with GCC; see the file COPYING3. If not see #include "ssa.h" #include "cgraph.h" #include "fold-const.h" +#include "tree-eh.h" #include "stor-layout.h" #include "gimplify.h" #include "gimple-iterator.h" #include "gimplify-me.h" +#include "tree-cfg.h" #include "tree-dfa.h" #include "ipa-param-manipulation.h" #include "print-tree.h" #include "gimple-pretty-print.h" #include "builtins.h" +#include "tree-ssa.h" + +const char *ipa_param_prefixes[] = {"SYNTH", + "ISRA", + "simd", + "mask"}; /* Return a heap allocated vector containing formal parameters of FNDECL. */ @@ -79,336 +87,236 @@ ipa_get_vector_of_formal_parm_types (tree fntype) return types; } -/* Modify the function declaration FNDECL and its type according to the plan in - ADJUSTMENTS. It also sets base fields of individual adjustments structures - to reflect the actual parameters being modified which are determined by the - base_index field. */ +/* Constructor from NEW_PARAMS showing how new parameters should look like and + CUR_PARAMS, which is either known vector of current parameter declarations + or NULL if no debug information for removed parameters should or can be + recorded. */ -void -ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec adjustments) +ipa_param_adjustments +::ipa_param_adjustments (vec *new_params, + vec *cur_params) + : m_adj_params (new_params), m_vanishing_decls (NULL), + m_vanishing_indices (NULL) { - vec oparms = ipa_get_vector_of_formal_parms (fndecl); - tree orig_type = TREE_TYPE (fndecl); - tree old_arg_types = TYPE_ARG_TYPES (orig_type); - - /* The following test is an ugly hack, some functions simply don't have any - arguments in their type. This is probably a bug but well... */ - bool care_for_types = (old_arg_types != NULL_TREE); - bool last_parm_void; - vec otypes; - if (care_for_types) - { - last_parm_void = (TREE_VALUE (tree_last (old_arg_types)) - == void_type_node); - otypes = ipa_get_vector_of_formal_parm_types (orig_type); - if (last_parm_void) - gcc_assert (oparms.length () + 1 == otypes.length ()); - else - gcc_assert (oparms.length () == otypes.length ()); - } - else - { - last_parm_void = false; - otypes.create (0); - } - - int len = adjustments.length (); - tree *link = &DECL_ARGUMENTS (fndecl); - tree new_arg_types = NULL; - for (int i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - gcc_assert (link); - - adj = &adjustments[i]; - tree parm; - if (adj->op == IPA_PARM_OP_NEW) - parm = NULL; - else - parm = oparms[adj->base_index]; - adj->base = parm; - - if (adj->op == IPA_PARM_OP_COPY) - { - if (care_for_types) - new_arg_types = tree_cons (NULL_TREE, otypes[adj->base_index], - new_arg_types); - *link = parm; - link = &DECL_CHAIN (parm); - } - else if (adj->op != IPA_PARM_OP_REMOVE) - { - tree new_parm; - tree ptype; - - if (adj->by_ref) - ptype = build_pointer_type (adj->type); - else - { - ptype = adj->type; - if (is_gimple_reg_type (ptype) - && TYPE_MODE (ptype) != BLKmode) - { - unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (ptype)); - if (TYPE_ALIGN (ptype) != malign) - ptype = build_aligned_type (ptype, malign); - } - } - - if (care_for_types) - new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types); - - new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE, - ptype); - const char *prefix = adj->arg_prefix ? adj->arg_prefix : "SYNTH"; - DECL_NAME (new_parm) = create_tmp_var_name (prefix); - DECL_ARTIFICIAL (new_parm) = 1; - DECL_ARG_TYPE (new_parm) = ptype; - DECL_CONTEXT (new_parm) = fndecl; - TREE_USED (new_parm) = 1; - DECL_IGNORED_P (new_parm) = 1; - layout_decl (new_parm, 0); + if (cur_params) + init (cur_params); +} - if (adj->op == IPA_PARM_OP_NEW) - adj->base = NULL; - else - adj->base = parm; - adj->new_decl = new_parm; +/* Constructor from NEW_PARAMS showing how new parameters should look like + and a FNDECL of the function they should be applied to. */ - *link = new_parm; - link = &DECL_CHAIN (new_parm); - } - } +ipa_param_adjustments +::ipa_param_adjustments (vec *new_params, + tree fndecl) + : m_adj_params (new_params), m_vanishing_decls (NULL), + m_vanishing_indices (NULL) +{ + vec cur_params = ipa_get_vector_of_formal_parms (fndecl); + init (&cur_params); + cur_params.release (); +} - *link = NULL_TREE; - tree new_reversed = NULL; - if (care_for_types) - { - new_reversed = nreverse (new_arg_types); - if (last_parm_void) - { - if (new_reversed) - TREE_CHAIN (new_arg_types) = void_list_node; - else - new_reversed = void_list_node; - } - } +/* Initialize m_vanishing_decls and m_vanishing_decls based on m_adj_params and + CUR_PARAMS. */ - /* Use copy_node to preserve as much as possible from original type - (debug info, attribute lists etc.) - Exception is METHOD_TYPEs must have THIS argument. - When we are asked to remove it, we need to build new FUNCTION_TYPE - instead. */ - tree new_type = NULL; - if (TREE_CODE (orig_type) != METHOD_TYPE - || (adjustments[0].op == IPA_PARM_OP_COPY - && adjustments[0].base_index == 0)) - { - new_type = build_distinct_type_copy (orig_type); - TYPE_ARG_TYPES (new_type) = new_reversed; - } - else - { - new_type - = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type), - new_reversed)); - TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type); - DECL_VINDEX (fndecl) = NULL_TREE; - } - - /* When signature changes, we need to clear builtin info. */ - if (DECL_BUILT_IN (fndecl)) +void +ipa_param_adjustments::init (vec *cur_params) +{ + unsigned cp_len = cur_params ? cur_params->length() : 0; + auto_vec kept (cp_len); + kept.quick_grow_cleared (cp_len); + unsigned adj_len = vec_safe_length (m_adj_params); + for (unsigned i = 0; i < adj_len ; i++) { - DECL_BUILT_IN_CLASS (fndecl) = NOT_BUILT_IN; - DECL_FUNCTION_CODE (fndecl) = (enum built_in_function) 0; + ipa_adjusted_param *apm = &(*m_adj_params)[i]; + gcc_assert (apm->op != IPA_PARAM_OP_UNDEFINED); + if (apm->op == IPA_PARAM_OP_COPY) + kept[apm->base_index] = true; } - - TREE_TYPE (fndecl) = new_type; - DECL_VIRTUAL_P (fndecl) = 0; - DECL_LANG_SPECIFIC (fndecl) = NULL; - otypes.release (); - oparms.release (); + for (unsigned i = 0; i < cp_len; i++) + if (!kept[i]) + { + vec_safe_push (m_vanishing_decls, (*cur_params)[i]); + vec_safe_push (m_vanishing_indices, i); + } } -/* Modify actual arguments of a function call CS as indicated in ADJUSTMENTS. - If this is a directly recursive call, CS must be NULL. Otherwise it must - contain the corresponding call graph edge. */ +/* Modify actual arguments of a function call in statement STMT, assuming it + calls CALLEE_DECL. Return the new statement that replaced the old one. */ -void -ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt, - ipa_parm_adjustment_vec adjustments) +gcall * +ipa_param_adjustments::modify_call_arguments (gcall *stmt, tree callee_decl) { - struct cgraph_node *current_node = cgraph_node::get (current_function_decl); - vec vargs; - vec **debug_args = NULL; - gcall *new_stmt; - gimple_stmt_iterator gsi, prev_gsi; - tree callee_decl; - int i, len; + unsigned len = vec_safe_length (m_adj_params); + auto_vec vargs (len); - len = adjustments.length (); - vargs.create (len); - callee_decl = !cs ? gimple_call_fndecl (stmt) : cs->callee->decl; + cgraph_node *current_node = cgraph_node::get (current_function_decl); current_node->remove_stmt_references (stmt); - gsi = gsi_for_stmt (stmt); - prev_gsi = gsi; + gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + gimple_stmt_iterator prev_gsi = gsi; gsi_prev (&prev_gsi); - for (i = 0; i < len; i++) + for (unsigned i = 0; i < len; i++) { - struct ipa_parm_adjustment *adj; + ipa_adjusted_param *apm = &(*m_adj_params)[i]; - adj = &adjustments[i]; + gcc_assert (apm->op != IPA_PARAM_OP_UNDEFINED); + /* Any transformation that introduces unspecified new parameters needs + to transform actual arguments itself. */ + gcc_assert (apm->op != IPA_PARAM_OP_NEW); - if (adj->op == IPA_PARM_OP_COPY) + if (apm->op == IPA_PARAM_OP_COPY) { - tree arg = gimple_call_arg (stmt, adj->base_index); + tree arg = gimple_call_arg (stmt, apm->base_index); vargs.quick_push (arg); + continue; } - else if (adj->op != IPA_PARM_OP_REMOVE) + gcc_assert (apm->op == IPA_PARAM_OP_SPLIT); + + /* We create a new parameter out of the value of the old one, we can + do the following kind of transformations: + + - A scalar passed by reference is converted to a scalar passed by + value. (apm->by_ref is false and the type of the original + actual argument is a pointer to a scalar). + + - A part of an aggregate is passed instead of the whole aggregate. + The part can be passed either by value or by reference, this is + determined by value of apm->by_ref. Moreover, the code below + handles both situations when the original aggregate is passed by + value (its type is not a pointer) and when it is passed by + reference (it is a pointer to an aggregate). + + When the new argument is passed by reference (apm->by_ref is true) + it must be a part of an aggregate and therefore we form it by + simply taking the address of a reference inside the original + aggregate. */ + + tree base = gimple_call_arg (stmt, apm->base_index); + location_t loc = DECL_P (base) ? DECL_SOURCE_LOCATION (base) + : EXPR_LOCATION (base); + + tree off; + bool deref_base = false; + unsigned int deref_align = 0; + if (TREE_CODE (base) != ADDR_EXPR && POINTER_TYPE_P (TREE_TYPE (base))) + off = build_int_cst (apm->alias_ptr_type, apm->unit_offset); + else { - tree expr, base, off; - location_t loc; - unsigned int deref_align = 0; - bool deref_base = false; - - /* We create a new parameter out of the value of the old one, we can - do the following kind of transformations: - - - A scalar passed by reference is converted to a scalar passed by - value. (adj->by_ref is false and the type of the original - actual argument is a pointer to a scalar). - - - A part of an aggregate is passed instead of the whole aggregate. - The part can be passed either by value or by reference, this is - determined by value of adj->by_ref. Moreover, the code below - handles both situations when the original aggregate is passed by - value (its type is not a pointer) and when it is passed by - reference (it is a pointer to an aggregate). - - When the new argument is passed by reference (adj->by_ref is true) - it must be a part of an aggregate and therefore we form it by - simply taking the address of a reference inside the original - aggregate. */ - - gcc_checking_assert (adj->offset % BITS_PER_UNIT == 0); - base = gimple_call_arg (stmt, adj->base_index); - loc = DECL_P (base) ? DECL_SOURCE_LOCATION (base) - : EXPR_LOCATION (base); - - if (TREE_CODE (base) != ADDR_EXPR - && POINTER_TYPE_P (TREE_TYPE (base))) - off = build_int_cst (adj->alias_ptr_type, - adj->offset / BITS_PER_UNIT); - else + bool addrof; + if (TREE_CODE (base) == ADDR_EXPR) { - HOST_WIDE_INT base_offset; - tree prev_base; - bool addrof; + base = TREE_OPERAND (base, 0); + addrof = true; + } + else + addrof = false; - if (TREE_CODE (base) == ADDR_EXPR) - { - base = TREE_OPERAND (base, 0); - addrof = true; - } - else - addrof = false; - prev_base = base; - base = get_addr_base_and_unit_offset (base, &base_offset); - /* Aggregate arguments can have non-invariant addresses. */ - if (!base) - { - base = build_fold_addr_expr (prev_base); - off = build_int_cst (adj->alias_ptr_type, - adj->offset / BITS_PER_UNIT); - } - else if (TREE_CODE (base) == MEM_REF) - { - if (!addrof) - { - deref_base = true; - deref_align = TYPE_ALIGN (TREE_TYPE (base)); - } - off = build_int_cst (adj->alias_ptr_type, - base_offset - + adj->offset / BITS_PER_UNIT); - off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), - off); - base = TREE_OPERAND (base, 0); - } - else + tree prev_base = base; + HOST_WIDE_INT base_offset; + base = get_addr_base_and_unit_offset (base, &base_offset); + + /* Aggregate arguments can have non-invariant addresses. */ + if (!base) + { + base = build_fold_addr_expr (prev_base); + off = build_int_cst (apm->alias_ptr_type, apm->unit_offset); + } + else if (TREE_CODE (base) == MEM_REF) + { + if (!addrof) { - off = build_int_cst (adj->alias_ptr_type, - base_offset - + adj->offset / BITS_PER_UNIT); - base = build_fold_addr_expr (base); + deref_base = true; + deref_align = TYPE_ALIGN (TREE_TYPE (base)); } + off = build_int_cst (apm->alias_ptr_type, + base_offset + apm->unit_offset); + off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), + off); + base = TREE_OPERAND (base, 0); } - - if (!adj->by_ref) + else { - tree type = adj->type; - unsigned int align; - unsigned HOST_WIDE_INT misalign; + off = build_int_cst (apm->alias_ptr_type, + base_offset + apm->unit_offset); + base = build_fold_addr_expr (base); + } + } - if (deref_base) - { - align = deref_align; - misalign = 0; - } - else - { - get_pointer_alignment_1 (base, &align, &misalign); - if (TYPE_ALIGN (type) > align) - align = TYPE_ALIGN (type); - } - misalign += (offset_int::from (wi::to_wide (off), - SIGNED).to_short_addr () - * BITS_PER_UNIT); - misalign = misalign & (align - 1); - if (misalign != 0) - align = least_bit_hwi (misalign); - if (align < TYPE_ALIGN (type)) - type = build_aligned_type (type, align); - base = force_gimple_operand_gsi (&gsi, base, - true, NULL, true, GSI_SAME_STMT); - expr = fold_build2_loc (loc, MEM_REF, type, base, off); - REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse; - /* If expr is not a valid gimple call argument emit - a load into a temporary. */ - if (is_gimple_reg_type (TREE_TYPE (expr))) - { - gimple *tem = gimple_build_assign (NULL_TREE, expr); - if (gimple_in_ssa_p (cfun)) - { - gimple_set_vuse (tem, gimple_vuse (stmt)); - expr = make_ssa_name (TREE_TYPE (expr), tem); - } - else - expr = create_tmp_reg (TREE_TYPE (expr)); - gimple_assign_set_lhs (tem, expr); - gsi_insert_before (&gsi, tem, GSI_SAME_STMT); - } + tree expr; + if (!apm->by_ref) + { + tree type = apm->type; + unsigned int align; + unsigned HOST_WIDE_INT misalign; + + if (deref_base) + { + align = deref_align; + misalign = 0; } else { - expr = fold_build2_loc (loc, MEM_REF, adj->type, base, off); - REF_REVERSE_STORAGE_ORDER (expr) = adj->reverse; - expr = build_fold_addr_expr (expr); - expr = force_gimple_operand_gsi (&gsi, expr, - true, NULL, true, GSI_SAME_STMT); + get_pointer_alignment_1 (base, &align, &misalign); + /* All users must make sure that we can be optimistic when it + comes to alignment in this case (by inspecting the final users + of these new parameters). */ + if (TYPE_ALIGN (type) > align) + align = TYPE_ALIGN (type); + } + misalign += (offset_int::from (wi::to_wide (off), + SIGNED).to_short_addr () + * BITS_PER_UNIT); + misalign = misalign & (align - 1); + if (misalign != 0) + align = least_bit_hwi (misalign); + if (align < TYPE_ALIGN (type)) + type = build_aligned_type (type, align); + base = force_gimple_operand_gsi (&gsi, base, + true, NULL, true, GSI_SAME_STMT); + expr = fold_build2_loc (loc, MEM_REF, type, base, off); + REF_REVERSE_STORAGE_ORDER (expr) = apm->reverse; + /* If expr is not a valid gimple call argument emit + a load into a temporary. */ + if (is_gimple_reg_type (TREE_TYPE (expr))) + { + gimple *tem = gimple_build_assign (NULL_TREE, expr); + if (gimple_in_ssa_p (cfun)) + { + gimple_set_vuse (tem, gimple_vuse (stmt)); + expr = make_ssa_name (TREE_TYPE (expr), tem); + } + else + expr = create_tmp_reg (TREE_TYPE (expr)); + gimple_assign_set_lhs (tem, expr); + gsi_insert_before (&gsi, tem, GSI_SAME_STMT); } - vargs.quick_push (expr); } - if (adj->op != IPA_PARM_OP_COPY && MAY_HAVE_DEBUG_STMTS) + else { - unsigned int ix; - tree ddecl = NULL_TREE, origin = DECL_ORIGIN (adj->base), arg; - gimple *def_temp; + expr = fold_build2_loc (loc, MEM_REF, apm->type, base, off); + REF_REVERSE_STORAGE_ORDER (expr) = apm->reverse; + expr = build_fold_addr_expr (expr); + expr = force_gimple_operand_gsi (&gsi, expr, + true, NULL, true, GSI_SAME_STMT); + } + vargs.quick_push (expr); + } + + if (callee_decl && MAY_HAVE_DEBUG_STMTS && m_vanishing_decls) + { + vec **debug_args = NULL; + unsigned van_len = vec_safe_length (m_vanishing_decls); + for (unsigned i = 0; i < van_len; i++) + { + tree vanished_decl = (*m_vanishing_decls)[i]; + if (!vanished_decl) + continue; + tree origin = DECL_ORIGIN (vanished_decl); + tree arg = gimple_call_arg (stmt, (*m_vanishing_indices)[i]); - arg = gimple_call_arg (stmt, adj->base_index); if (!useless_type_conversion_p (TREE_TYPE (origin), TREE_TYPE (arg))) { if (!fold_convertible_p (TREE_TYPE (origin), arg)) @@ -418,6 +326,8 @@ ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt, } if (debug_args == NULL) debug_args = decl_debug_args_insert (callee_decl); + unsigned int ix; + tree ddecl = NULL_TREE; for (ix = 0; vec_safe_iterate (*debug_args, ix, &ddecl); ix += 2) if (ddecl == origin) { @@ -434,7 +344,8 @@ ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt, vec_safe_push (*debug_args, origin); vec_safe_push (*debug_args, ddecl); } - def_temp = gimple_build_debug_bind (ddecl, unshare_expr (arg), stmt); + gimple *def_temp = gimple_build_debug_bind (ddecl, + unshare_expr (arg), stmt); gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT); } } @@ -445,8 +356,8 @@ ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt, print_gimple_stmt (dump_file, gsi_stmt (gsi), 0); } - new_stmt = gimple_build_call_vec (callee_decl, vargs); - vargs.release (); + gcall *new_stmt = gimple_build_call_vec (callee_decl, vargs); + if (gimple_call_lhs (stmt)) gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); @@ -472,120 +383,264 @@ ipa_modify_call_arguments (struct cgraph_edge *cs, gcall *stmt, fprintf (dump_file, "\n"); } gsi_replace (&gsi, new_stmt, true); - if (cs) - cs->set_call_stmt (new_stmt); do { current_node->record_stmt_references (gsi_stmt (gsi)); gsi_prev (&gsi); } while (gsi_stmt (gsi) != gsi_stmt (prev_gsi)); + return new_stmt; } -/* Return true iff BASE_INDEX is in ADJUSTMENTS more than once. */ +void +ipa_param_adjustments::modify_call_arguments (struct cgraph_edge *cs) +{ + gcall *old_call = cs->call_stmt; + tree callee_decl = cs->callee ? cs->callee->decl : NULL; + /* TODO: Optionally set current_function_decl */ + gcall *new_call = modify_call_arguments (old_call, callee_decl); + cs->set_call_stmt (new_call); +} + +/* Names of parameters for dumping. Keep in sync with enum ipa_parm_op. */ + +static const char *ipa_param_op_names[] = {"IPA_PARAM_OP_UNDEFINED", + "IPA_PARAM_OP_COPY", + "IPA_PARAM_OP_NEW", + "IPA_PARAM_OP_SPLIT"}; + +/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human + friendly way, assuming they are meant to be applied to FNDECL. */ -static bool -index_in_adjustments_multiple_times_p (int base_index, - ipa_parm_adjustment_vec adjustments) +void +ipa_dump_adjusted_parameters (FILE *f, + vec *adj_params) { - int i, len = adjustments.length (); - bool one = false; + unsigned i, len = vec_safe_length (adj_params); + bool first = true; + fprintf (f, "IPA adjusted parameters: "); for (i = 0; i < len; i++) { - struct ipa_parm_adjustment *adj; - adj = &adjustments[i]; + struct ipa_adjusted_param *apm; + apm = &(*adj_params)[i]; + + if (!first) + fprintf (f, " "); + else + first = false; - if (adj->base_index == base_index) + fprintf (f, "%i. %s", i, ipa_param_op_names[apm->op]); + switch (apm->op) { - if (one) - return true; - else - one = true; + case IPA_PARAM_OP_UNDEFINED: + break; + + case IPA_PARAM_OP_COPY: + fprintf (f, ", base_index: %u", apm->base_index); + break; + + case IPA_PARAM_OP_SPLIT: + fprintf (f, ", offset: %u", apm->unit_offset); + /* fall-through */ + case IPA_PARAM_OP_NEW: + fprintf (f, ", base_index: %u", apm->base_index); + print_node_brief (f, ", type: ", apm->type, 0); + print_node_brief (f, ", type: ", apm->alias_ptr_type, 0); + fprintf (f, " prefix: %s, reverse: %u, by_ref: %u", + ipa_param_prefixes[apm->param_prefix_index], + apm->reverse, apm->by_ref); + break; } + fprintf (f, "\n"); } - return false; } -/* Return adjustments that should have the same effect on function parameters - and call arguments as if they were first changed according to adjustments in - INNER and then by adjustments in OUTER. */ -ipa_parm_adjustment_vec -ipa_combine_adjustments (ipa_parm_adjustment_vec inner, - ipa_parm_adjustment_vec outer) +/* Constructor of ipa_param_body_adjustments performing all necessary + initializations. */ + +ipa_param_body_adjustments +::ipa_param_body_adjustments (vec *adj_params, + tree fndecl) + : m_adj_params (adj_params), m_fndecl (fndecl), m_oparms (), + m_replacements (), m_new_decls (vec_safe_length (adj_params)), + m_new_types (vec_safe_length (adj_params)), m_reset_debug_decls (), + m_removed_decls (), m_removed_map (), m_method2func () { - int i, outlen = outer.length (); - int inlen = inner.length (); - int removals = 0; - ipa_parm_adjustment_vec adjustments, tmp; + for (tree parm = DECL_ARGUMENTS (fndecl); parm; parm = DECL_CHAIN (parm)) + m_oparms.safe_push (parm); - tmp.create (inlen); - for (i = 0; i < inlen; i++) - { - struct ipa_parm_adjustment *n; - n = &inner[i]; + bool care_for_types = (TYPE_ARG_TYPES (TREE_TYPE (m_fndecl)) != NULL_TREE); + vec otypes; + if (care_for_types) + otypes = ipa_get_vector_of_formal_parm_types (TREE_TYPE (m_fndecl)); + else + otypes = vNULL; - if (n->op == IPA_PARM_OP_REMOVE) - removals++; - else - { - /* FIXME: Handling of new arguments are not implemented yet. */ - gcc_assert (n->op != IPA_PARM_OP_NEW); - tmp.quick_push (*n); - } - } + auto_vec kept (m_oparms.length ()); + kept.quick_grow_cleared (m_oparms.length ()); + m_method2func = (TREE_CODE (TREE_TYPE (m_fndecl)) == METHOD_TYPE); - adjustments.create (outlen + removals); - for (i = 0; i < outlen; i++) + unsigned adj_len = vec_safe_length (m_adj_params); + for (unsigned i = 0; i < adj_len ; i++) { - struct ipa_parm_adjustment r; - struct ipa_parm_adjustment *out = &outer[i]; - struct ipa_parm_adjustment *in = &tmp[out->base_index]; - - memset (&r, 0, sizeof (r)); - gcc_assert (in->op != IPA_PARM_OP_REMOVE); - if (out->op == IPA_PARM_OP_REMOVE) + ipa_adjusted_param *apm = &(*m_adj_params)[i]; + tree new_parm, new_type; + if (apm->op == IPA_PARAM_OP_COPY) + { + kept[apm->base_index] = true; + if (care_for_types) + new_type = otypes[apm->base_index]; + else + new_type = NULL; + new_parm = m_oparms[apm->base_index]; + if (apm->base_index == 0) + m_method2func = false; + } + else if (apm->op == IPA_PARAM_OP_NEW + || apm->op == IPA_PARAM_OP_SPLIT) { - if (!index_in_adjustments_multiple_times_p (in->base_index, tmp)) + if (apm->by_ref) { - r.op = IPA_PARM_OP_REMOVE; - adjustments.quick_push (r); + new_type = build_pointer_type (apm->type); + if (is_gimple_reg_type (new_type) + && TYPE_MODE (new_type) != BLKmode) + { + unsigned malign = GET_MODE_ALIGNMENT (TYPE_MODE (new_type)); + if (TYPE_ALIGN (new_type) != malign) + new_type = build_aligned_type (new_type, malign); + } } - continue; - } + else + new_type = apm->type; + new_parm = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL_TREE, + new_type); + const char *prefix = ipa_param_prefixes[apm->param_prefix_index]; + DECL_NAME (new_parm) = create_tmp_var_name (prefix); + DECL_ARTIFICIAL (new_parm) = 1; + DECL_ARG_TYPE (new_parm) = new_type; + DECL_CONTEXT (new_parm) = m_fndecl; + TREE_USED (new_parm) = 1; + DECL_IGNORED_P (new_parm) = 1; + layout_decl (new_parm, 0); + + if (apm->op == IPA_PARAM_OP_SPLIT) + register_replacement (apm, new_parm); + } else - { - /* FIXME: Handling of new arguments are not implemented yet. */ - gcc_assert (out->op != IPA_PARM_OP_NEW); - } + gcc_unreachable (); + m_new_decls.quick_push (new_parm); + if (care_for_types) + m_new_types.quick_push (new_type); + } - r.base_index = in->base_index; - r.type = out->type; + unsigned op_len = m_oparms.length (); + for (unsigned i = 0; i < op_len; i++) + if (!kept[i]) + { + if (is_gimple_reg (m_oparms[i])) + m_reset_debug_decls.safe_push (m_oparms[i]); + m_removed_decls.safe_push(m_oparms[i]); + m_removed_map.put (m_oparms[i], m_removed_decls.length () - 1); + } - /* FIXME: Create nonlocal value too. */ + otypes.release (); +} - if (in->op == IPA_PARM_OP_COPY && out->op == IPA_PARM_OP_COPY) - r.op = IPA_PARM_OP_COPY; - else if (in->op == IPA_PARM_OP_COPY) - r.offset = out->offset; - else if (out->op == IPA_PARM_OP_COPY) - r.offset = in->offset; - else - r.offset = in->offset + out->offset; - adjustments.quick_push (r); +/* Register that REPLACEMENT should replace parameter described in APM. */ + +void +ipa_param_body_adjustments::register_replacement (ipa_adjusted_param *apm, + tree replacement) +{ + gcc_checking_assert (apm->op == IPA_PARAM_OP_SPLIT + || apm->op == IPA_PARAM_OP_NEW); + ipa_param_body_replacement psr; + psr.base = m_oparms[apm->base_index]; + psr.unit_offset = apm->unit_offset; + psr.repl = replacement; + psr.by_ref = apm->by_ref; + psr.reverse = apm->reverse; + m_replacements.safe_push (psr); +} + +/* Modify the function declaration FNDECL and its type according to the plan in + ADJUSTMENTS. It also sets base fields of individual adjustments structures + to reflect the actual parameters being modified which are determined by the + base_index field. */ + +void +ipa_param_body_adjustments::modify_formal_parameters () +{ + tree orig_type = TREE_TYPE (m_fndecl); + tree old_arg_types = TYPE_ARG_TYPES (orig_type); + + bool care_for_types = (old_arg_types != NULL_TREE); + bool last_parm_void = care_for_types + && (TREE_VALUE (tree_last (old_arg_types)) == void_type_node);; + + unsigned len = vec_safe_length (m_adj_params); + gcc_assert (len == m_new_decls.length()); + gcc_assert (len == m_new_types.length() || !care_for_types); + tree *link = &DECL_ARGUMENTS (m_fndecl); + tree new_arg_types = NULL; + + for (unsigned i = 0; i < len; i++) + { + tree new_decl = m_new_decls[i]; + + if (care_for_types) + new_arg_types = tree_cons (NULL_TREE, m_new_types[i], new_arg_types); + *link = new_decl; + link = &DECL_CHAIN (new_decl); } + *link = NULL_TREE; - for (i = 0; i < inlen; i++) + tree new_reversed; + if (care_for_types) + { + new_reversed = nreverse (new_arg_types); + if (last_parm_void) + { + if (new_reversed) + TREE_CHAIN (new_arg_types) = void_list_node; + else + new_reversed = void_list_node; + } + } + else + new_reversed = NULL; + + /* Use copy_node to preserve as much as possible from original type + (debug info, attribute lists etc.) + Exception is METHOD_TYPEs which must have THIS argument and when we are + asked to remove it, we need to build new FUNCTION_TYPE instead. */ + tree new_type = NULL; + if (m_method2func) + { + new_type + = build_distinct_type_copy (build_function_type (TREE_TYPE (orig_type), + new_reversed)); + TYPE_CONTEXT (new_type) = TYPE_CONTEXT (orig_type); + DECL_VINDEX (m_fndecl) = NULL_TREE; + } + else { - struct ipa_parm_adjustment *n = &inner[i]; + new_type = build_distinct_type_copy (orig_type); + TYPE_ARG_TYPES (new_type) = new_reversed; + } - if (n->op == IPA_PARM_OP_REMOVE) - adjustments.quick_push (*n); + /* When signature changes, we need to clear builtin info. */ + if (DECL_BUILT_IN (m_fndecl)) + { + DECL_BUILT_IN_CLASS (m_fndecl) = NOT_BUILT_IN; + DECL_FUNCTION_CODE (m_fndecl) = (enum built_in_function) 0; } - tmp.release (); - return adjustments; + TREE_TYPE (m_fndecl) = new_type; + DECL_VIRTUAL_P (m_fndecl) = 0; + DECL_LANG_SPECIFIC (m_fndecl) = NULL; } /* If T is an SSA_NAME, return NULL if it is not a default def or @@ -606,162 +661,460 @@ get_ssa_base_param (tree t, bool ignore_default_def) return t; } -/* Given an expression, return an adjustment entry specifying the - transformation to be done on EXPR. If no suitable adjustment entry - was found, returns NULL. +/* Given BASE and UNIT_OFFSET, find the corresponding record among replacement + structures. */ - If IGNORE_DEFAULT_DEF is set, consider SSA_NAMEs which are not a - default def, otherwise bail on them. - - If CONVERT is non-NULL, this function will set *CONVERT if the - expression provided is a component reference. ADJUSTMENTS is the - adjustments vector. */ - -ipa_parm_adjustment * -ipa_get_adjustment_candidate (tree **expr, bool *convert, - ipa_parm_adjustment_vec adjustments, - bool ignore_default_def) +ipa_param_body_replacement * +ipa_param_body_adjustments::lookup_replacement (tree base, unsigned unit_offset) { - if (TREE_CODE (**expr) == BIT_FIELD_REF - || TREE_CODE (**expr) == IMAGPART_EXPR - || TREE_CODE (**expr) == REALPART_EXPR) + unsigned int len = m_replacements.length (); + for (unsigned i = 0; i < len; i++) { - *expr = &TREE_OPERAND (**expr, 0); - if (convert) - *convert = true; + ipa_param_body_replacement *pbr = &m_replacements[i]; + + if (pbr->base == base + && (pbr->unit_offset == unit_offset)) + return pbr; } + return NULL; +} + +/* Given an expression, return the structure describing how it should be + replaced if it accesses a part of a split parameter or NULL otherwise. + + Do not free the result, it will be deallocated when the object is destroyed. + + If IGNORE_DEFAULT_DEF is cleared, consider only SSA_NAMEs of PARM_DECLs + which are default definitions, if set, consider all SSA_NAMEs of + PARM_DECLs. */ + +ipa_param_body_replacement * +ipa_param_body_adjustments::get_expr_replacement (tree expr, + bool ignore_default_def) +{ HOST_WIDE_INT offset, size, max_size; bool reverse; tree base - = get_ref_base_and_extent (**expr, &offset, &size, &max_size, &reverse); + = get_ref_base_and_extent (expr, &offset, &size, &max_size, &reverse); if (!base || size == -1 || max_size == -1) return NULL; + if ((offset % BITS_PER_UNIT) != 0) + return NULL; + if (TREE_CODE (base) == MEM_REF) { offset += mem_ref_offset (base).to_short_addr () * BITS_PER_UNIT; base = TREE_OPERAND (base, 0); } + if (offset < 0 || (offset / BITS_PER_UNIT) > UINT_MAX) + return NULL; + unsigned unit_offset = offset / BITS_PER_UNIT; + base = get_ssa_base_param (base, ignore_default_def); if (!base || TREE_CODE (base) != PARM_DECL) return NULL; - struct ipa_parm_adjustment *cand = NULL; - unsigned int len = adjustments.length (); - for (unsigned i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj = &adjustments[i]; + return lookup_replacement (base, unit_offset); +} - if (adj->base == base - && (adj->offset == offset || adj->op == IPA_PARM_OP_REMOVE)) - { - cand = adj; - break; - } +/* Given OLD_DECL, which is a PARM_DECL of a parameter that is being removed + (which includes it being split or replaced), return a new variable that + should be used for any SSA names that will remain in the function that + previously belonged to OLD_DECL. */ + +tree +ipa_param_body_adjustments::get_replacement_ssa_base (tree old_decl) +{ + unsigned *idx = m_removed_map.get (old_decl); + if (!idx) + return NULL; + + tree repl; + if (TREE_CODE (m_removed_decls [*idx]) == PARM_DECL) + { + gcc_assert (m_removed_decls [*idx] == old_decl); + repl = copy_var_decl (old_decl, DECL_NAME (old_decl), + TREE_TYPE (old_decl)); + m_removed_decls [*idx] = repl; } + else + repl = m_removed_decls [*idx]; + return repl; +} + +/* If OLD_NAME, which is being defined by statement STMT, is an SSA_NAME of a + parameter which is to be removed because its value is not used, create a new + SSA_NAME relating to a replacement VAR_DECL, replace all uses of the + original with it and return it. If there is no need to re-map, return NULL. + ADJUSTMENTS is a pointer to a vector of IPA-SRA adjustments. */ - if (!cand || cand->op == IPA_PARM_OP_COPY || cand->op == IPA_PARM_OP_REMOVE) +tree +ipa_param_body_adjustments::replace_removed_params_ssa_names (tree old_name, + gimple *stmt) +{ + if (TREE_CODE (old_name) != SSA_NAME) + return NULL; + + tree decl = SSA_NAME_VAR (old_name); + if (decl == NULL_TREE + || TREE_CODE (decl) != PARM_DECL) return NULL; - return cand; + + tree repl = get_replacement_ssa_base (decl); + if (!repl) + return NULL; + + tree new_name = make_ssa_name (repl, stmt); + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_name) + = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (old_name); + + if (dump_file) + { + fprintf (dump_file, "replacing an SSA name of a removed param "); + print_generic_expr (dump_file, old_name); + fprintf (dump_file, " with "); + print_generic_expr (dump_file, new_name); + fprintf (dump_file, "\n"); + } + + replace_uses_by (old_name, new_name); + return new_name; } -/* If the expression *EXPR should be replaced by a reduction of a parameter, do - so. ADJUSTMENTS is a pointer to a vector of adjustments. CONVERT - specifies whether the function should care about type incompatibility the - current and new expressions. If it is false, the function will leave - incompatibility issues to the caller. Return true iff the expression - was modified. */ +/* If the expression *EXPR_P should be replaced by a reduction of a parameter, + do so. CONVERT specifies whether the function should care about type + incompatibility of the current and new expressions. If it is false, the + function will leave incompatibility issues to the caller, but it will be + overridden if BIT_FIELD_REF, IMAGPART_EXPR or REALPART_EXPR is encountered. + Return true iff the expression was modified. */ bool -ipa_modify_expr (tree *expr, bool convert, - ipa_parm_adjustment_vec adjustments) +ipa_param_body_adjustments::modify_expr (tree *expr_p, bool convert) { - struct ipa_parm_adjustment *cand - = ipa_get_adjustment_candidate (&expr, &convert, adjustments, false); - if (!cand) + tree expr = *expr_p; + + if (TREE_CODE (expr) == BIT_FIELD_REF + || TREE_CODE (expr) == IMAGPART_EXPR + || TREE_CODE (expr) == REALPART_EXPR) + { + expr_p = &TREE_OPERAND (expr, 0); + expr = *expr_p; + convert = true; + } + + ipa_param_body_replacement *pbr = get_expr_replacement (expr, false); + if (!pbr) return false; - tree src; - if (cand->by_ref) + tree repl; + if (pbr->by_ref) { - src = build_simple_mem_ref (cand->new_decl); - REF_REVERSE_STORAGE_ORDER (src) = cand->reverse; + repl = build_simple_mem_ref (pbr->repl); + REF_REVERSE_STORAGE_ORDER (repl) = pbr->reverse; } else - src = cand->new_decl; + repl = pbr->repl; if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "About to replace expr "); - print_generic_expr (dump_file, *expr); + print_generic_expr (dump_file, expr); fprintf (dump_file, " with "); - print_generic_expr (dump_file, src); + print_generic_expr (dump_file, repl); fprintf (dump_file, "\n"); } - if (convert && !useless_type_conversion_p (TREE_TYPE (*expr), cand->type)) + if (convert && !useless_type_conversion_p (TREE_TYPE (expr), + TREE_TYPE (repl))) { - tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (*expr), src); - *expr = vce; + tree vce = build1 (VIEW_CONVERT_EXPR, TREE_TYPE (expr), repl); + *expr_p = vce; } else - *expr = src; + *expr_p = repl; return true; } -/* Dump the adjustments in the vector ADJUSTMENTS to dump_file in a human - friendly way, assuming they are meant to be applied to FNDECL. */ +/* If the statement STMT contains any expressions that need to replaced with a + different one as noted by ADJUSTMENTS, do so. Handle any potential type + incompatibilities (GSI is used to accommodate conversion statements and must + point to the statement). Return true iff the statement was modified. */ -void -ipa_dump_param_adjustments (FILE *file, ipa_parm_adjustment_vec adjustments, - tree fndecl) +bool +ipa_param_body_adjustments::modify_assignment (gimple *stmt, + gimple_stmt_iterator *gsi) { - int i, len = adjustments.length (); - bool first = true; - vec parms = ipa_get_vector_of_formal_parms (fndecl); + tree *lhs_p, *rhs_p; + bool any; - fprintf (file, "IPA param adjustments: "); - for (i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - adj = &adjustments[i]; + if (!gimple_assign_single_p (stmt)) + return false; - if (!first) - fprintf (file, " "); - else - first = false; + rhs_p = gimple_assign_rhs1_ptr (stmt); + lhs_p = gimple_assign_lhs_ptr (stmt); + + any = modify_expr (lhs_p, false); + any |= modify_expr (rhs_p, false); + if (any) + { + tree new_rhs = NULL_TREE; - fprintf (file, "%i. base_index: %i - ", i, adj->base_index); - print_generic_expr (file, parms[adj->base_index]); - if (adj->base) + if (!useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p))) { - fprintf (file, ", base: "); - print_generic_expr (file, adj->base); + if (TREE_CODE (*rhs_p) == CONSTRUCTOR) + { + /* V_C_Es of constructors can cause trouble (PR 42714). */ + if (is_gimple_reg_type (TREE_TYPE (*lhs_p))) + *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p)); + else + *rhs_p = build_constructor (TREE_TYPE (*lhs_p), + NULL); + } + else + new_rhs = fold_build1_loc (gimple_location (stmt), + VIEW_CONVERT_EXPR, TREE_TYPE (*lhs_p), + *rhs_p); } - if (adj->new_decl) + else if (REFERENCE_CLASS_P (*rhs_p) + && is_gimple_reg_type (TREE_TYPE (*lhs_p)) + && !is_gimple_reg (*lhs_p)) + /* This can happen when an assignment in between two single field + structures is turned into an assignment in between two pointers to + scalars (PR 42237). */ + new_rhs = *rhs_p; + + if (new_rhs) { - fprintf (file, ", new_decl: "); - print_generic_expr (file, adj->new_decl); + tree tmp = force_gimple_operand_gsi (gsi, new_rhs, true, NULL_TREE, + true, GSI_SAME_STMT); + + gimple_assign_set_rhs_from_tree (gsi, tmp); } - if (adj->new_ssa_base) + + return true; + } + + return any; +} + +/* Traverse body of the current function and perform the requested adjustments. + Return true iff the CFG has been changed. */ + +bool +ipa_param_body_adjustments::modify_cfun_body () +{ + bool cfg_changed = false; + basic_block bb; + + FOR_EACH_BB_FN (bb, cfun) + { + gimple_stmt_iterator gsi; + + for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) { - fprintf (file, ", new_ssa_base: "); - print_generic_expr (file, adj->new_ssa_base); + gphi *phi = as_a (gsi_stmt (gsi)); + tree new_lhs, old_lhs = gimple_phi_result (phi); + new_lhs = replace_removed_params_ssa_names (old_lhs, phi); + if (new_lhs) + { + gimple_phi_set_result (phi, new_lhs); + release_ssa_name (old_lhs); + } } - if (adj->op == IPA_PARM_OP_COPY) - fprintf (file, ", copy_param"); - else if (adj->op == IPA_PARM_OP_REMOVE) - fprintf (file, ", remove_param"); - else - fprintf (file, ", offset %li", (long) adj->offset); - if (adj->by_ref) - fprintf (file, ", by_ref"); - print_node_brief (file, ", type: ", adj->type, 0); - fprintf (file, "\n"); + gsi = gsi_start_bb (bb); + while (!gsi_end_p (gsi)) + { + gimple *stmt = gsi_stmt (gsi); + bool modified = false; + tree *t; + + switch (gimple_code (stmt)) + { + case GIMPLE_RETURN: + t = gimple_return_retval_ptr (as_a (stmt)); + if (*t != NULL_TREE) + modified |= modify_expr (t, true); + break; + + case GIMPLE_ASSIGN: + modified |= modify_assignment (stmt, &gsi); + break; + + case GIMPLE_CALL: + /* Operands must be processed before the lhs. */ + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + { + t = gimple_call_arg_ptr (stmt, i); + modified |= modify_expr (t, true); + } + + if (gimple_call_lhs (stmt)) + { + t = gimple_call_lhs_ptr (stmt); + modified |= modify_expr (t, false); + } + break; + + case GIMPLE_ASM: + { + gasm *asm_stmt = as_a (stmt); + for (unsigned i = 0; i < gimple_asm_ninputs (asm_stmt); i++) + { + t = &TREE_VALUE (gimple_asm_input_op (asm_stmt, i)); + modified |= modify_expr (t, true); + } + for (unsigned i = 0; i < gimple_asm_noutputs (asm_stmt); i++) + { + t = &TREE_VALUE (gimple_asm_output_op (asm_stmt, i)); + modified |= modify_expr (t, false); + } + } + break; + + default: + break; + } + + def_operand_p defp; + ssa_op_iter iter; + FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF) + { + tree old_def = DEF_FROM_PTR (defp); + if (tree new_def = replace_removed_params_ssa_names (old_def, + stmt)) + { + SET_DEF (defp, new_def); + release_ssa_name (old_def); + modified = true; + } + } + + if (modified) + { + update_stmt (stmt); + if (maybe_clean_eh_stmt (stmt) + && gimple_purge_dead_eh_edges (gimple_bb (stmt))) + cfg_changed = true; + } + gsi_next (&gsi); + } } - parms.release (); + + return cfg_changed; +} + +/* Call gimple_debug_bind_reset_value on all debug statements describing + gimple register parameters that are being removed or replaced. */ + +void +ipa_param_body_adjustments::reset_debug_stmts () +{ + int i, len; + gimple_stmt_iterator *gsip = NULL, gsi; + + if (MAY_HAVE_DEBUG_STMTS && single_succ_p (ENTRY_BLOCK_PTR_FOR_FN (cfun))) + { + gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun))); + gsip = &gsi; + } + len = m_reset_debug_decls.length (); + for (i = 0; i < len; i++) + { + imm_use_iterator ui; + gimple *stmt; + gdebug *def_temp; + tree name, vexpr, copy = NULL_TREE; + use_operand_p use_p; + tree decl = m_reset_debug_decls[i]; + + gcc_checking_assert (is_gimple_reg (decl)); + name = ssa_default_def (cfun, decl); + vexpr = NULL; + if (name) + FOR_EACH_IMM_USE_STMT (stmt, ui, name) + { + if (gimple_clobber_p (stmt)) + { + gimple_stmt_iterator cgsi = gsi_for_stmt (stmt); + unlink_stmt_vdef (stmt); + gsi_remove (&cgsi, true); + release_defs (stmt); + continue; + } + /* All other users must have been removed by function body + modification. */ + gcc_assert (is_gimple_debug (stmt)); + if (vexpr == NULL && gsip != NULL) + { + vexpr = make_node (DEBUG_EXPR_DECL); + def_temp = gimple_build_debug_source_bind (vexpr, decl, NULL); + DECL_ARTIFICIAL (vexpr) = 1; + TREE_TYPE (vexpr) = TREE_TYPE (name); + SET_DECL_MODE (vexpr, DECL_MODE (decl)); + gsi_insert_before (gsip, def_temp, GSI_SAME_STMT); + } + if (vexpr) + { + FOR_EACH_IMM_USE_ON_STMT (use_p, ui) + SET_USE (use_p, vexpr); + } + else + gimple_debug_bind_reset_value (stmt); + update_stmt (stmt); + } + /* Create a VAR_DECL for debug info purposes. */ + if (!DECL_IGNORED_P (decl)) + { + copy = build_decl (DECL_SOURCE_LOCATION (current_function_decl), + VAR_DECL, DECL_NAME (decl), + TREE_TYPE (decl)); + if (DECL_PT_UID_SET_P (decl)) + SET_DECL_PT_UID (copy, DECL_PT_UID (decl)); + TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl); + TREE_READONLY (copy) = TREE_READONLY (decl); + TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl); + DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl); + DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl); + DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl); + DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl); + DECL_SEEN_IN_BIND_EXPR_P (copy) = 1; + SET_DECL_RTL (copy, 0); + TREE_USED (copy) = 1; + DECL_CONTEXT (copy) = current_function_decl; + add_local_decl (cfun, copy); + DECL_CHAIN (copy) = + BLOCK_VARS (DECL_INITIAL (current_function_decl)); + BLOCK_VARS (DECL_INITIAL (current_function_decl)) = copy; + } + if (gsip != NULL && copy && target_for_debug_bind (decl)) + { + gcc_assert (TREE_CODE (decl) == PARM_DECL); + if (vexpr) + def_temp = gimple_build_debug_bind (copy, vexpr, NULL); + else + def_temp = gimple_build_debug_source_bind (copy, decl, + NULL); + gsi_insert_before (gsip, def_temp, GSI_SAME_STMT); + } + } +} + +/* Perform all necessary body changes to change signature, body and debug info + of fun according to adjustments passed at construction. Return true if CFG + was changed in any way. */ + +bool +ipa_param_body_adjustments::perform_cfun_body_modifications () +{ + bool cfg_changed; + modify_formal_parameters (); + cfg_changed = modify_cfun_body (); + reset_debug_stmts (); + + return cfg_changed; } diff --git a/gcc/ipa-param-manipulation.h b/gcc/ipa-param-manipulation.h index 364e4489c29..1f64dd99201 100644 --- a/gcc/ipa-param-manipulation.h +++ b/gcc/ipa-param-manipulation.h @@ -21,100 +21,157 @@ along with GCC; see the file COPYING3. If not see #ifndef IPA_PARAM_MANIPULATION_H #define IPA_PARAM_MANIPULATION_H +/* Indices into ipa_param_prefixes to identify a human-readable prefix for newly + synthesized parameters. Keep in sync with the array. */ +#define IPA_PARAM_PREFIX_SYNTH 0 +#define IPA_PARAM_PREFIX_ISRA 1 +#define IPA_PARAM_PREFIX_SIMD 2 +#define IPA_PARAM_PREFIX_MASK 3 + +/* We do not support manipulating functions with more than + 1< ipa_parm_adjustment_vec; +void ipa_dump_adjusted_parameters (FILE *f, + vec *adj_params); + +/* Class used to record planned modifications to parameters of a function and + also to perform necessary modifications at the caller side at the gimple + level. */ + +class GTY(()) ipa_param_adjustments +{ +public: + ipa_param_adjustments (vec *new_params, + tree fndecl); + ipa_param_adjustments (vec *new_params, + vec *cur_params = NULL); + + gcall *modify_call_arguments (gcall *stmt, tree callee_decl); + void modify_call_arguments (struct cgraph_edge *cs); + + vec *m_adj_params; + vec *m_vanishing_decls; + vec *m_vanishing_indices; +private: + ipa_param_adjustments () {} + + void init (vec *cur_params); +}; + +/* Structure used to map expressions accessing split or replaced parameters to + new PARM_DECLs. TODO: Even though there usually be only few, but should we + use a hash? */ + +struct ipa_param_body_replacement +{ + tree base, repl; + unsigned unit_offset; + bool by_ref; + bool reverse; +}; + + +/* Class used when actually performing adjustments to formal parameters of + a function to map accesses that need to be replaced to replacements. */ + +class ipa_param_body_adjustments +{ +public: + ipa_param_body_adjustments (vec *adj_params, + tree fndecl); + + bool perform_cfun_body_modifications (); + void modify_formal_parameters (); + void register_replacement (ipa_adjusted_param *apm, tree replacement); + ipa_param_body_replacement *lookup_replacement (tree base, + unsigned unit_offset); + ipa_param_body_replacement *get_expr_replacement (tree expr, + bool ignore_default_def); + tree get_replacement_ssa_base (tree old_decl); + + vec *m_adj_params; +private: + tree replace_removed_params_ssa_names (tree old_name, gimple *stmt); + bool modify_expr (tree *expr_p, bool convert); + bool modify_assignment (gimple *stmt, gimple_stmt_iterator *gsi); + bool modify_cfun_body (); + void reset_debug_stmts (); + + tree m_fndecl; + auto_vec m_oparms; + auto_vec m_replacements; + auto_vec m_new_decls; + auto_vec m_new_types; + auto_vec m_reset_debug_decls; + auto_vec m_removed_decls; + hash_map m_removed_map; + bool m_method2func; +}; vec ipa_get_vector_of_formal_parms (tree fndecl); vec ipa_get_vector_of_formal_parm_types (tree fntype); -void ipa_modify_formal_parameters (tree fndecl, ipa_parm_adjustment_vec); -void ipa_modify_call_arguments (struct cgraph_edge *, gcall *, - ipa_parm_adjustment_vec); -ipa_parm_adjustment_vec ipa_combine_adjustments (ipa_parm_adjustment_vec, - ipa_parm_adjustment_vec); -void ipa_dump_param_adjustments (FILE *, ipa_parm_adjustment_vec, tree); - -bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec); -ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *, - ipa_parm_adjustment_vec, - bool); #endif /* IPA_PARAM_MANIPULATION_H */ diff --git a/gcc/omp-simd-clone.c b/gcc/omp-simd-clone.c index b8386037dfd..760cf6b5161 100644 --- a/gcc/omp-simd-clone.c +++ b/gcc/omp-simd-clone.c @@ -558,28 +558,27 @@ create_tmp_simd_array (const char *prefix, tree type, int simdlen) Returns an adjustment vector that will be filled describing how the argument types will be adjusted. */ -static ipa_parm_adjustment_vec +static ipa_param_body_adjustments * simd_clone_adjust_argument_types (struct cgraph_node *node) { vec args; - ipa_parm_adjustment_vec adjustments; if (node->definition) args = ipa_get_vector_of_formal_parms (node->decl); else args = simd_clone_vector_of_formal_parm_types (node->decl); - adjustments.create (args.length ()); + vec *new_params = NULL; + vec_safe_reserve_exact (new_params, args.length ()); unsigned i, j, veclen; - struct ipa_parm_adjustment adj; struct cgraph_simd_clone *sc = node->simdclone; for (i = 0; i < sc->nargs; ++i) { - memset (&adj, 0, sizeof (adj)); + ipa_adjusted_param apm; + memset (&apm, 0, sizeof (apm)); tree parm = args[i]; tree parm_type = node->definition ? TREE_TYPE (parm) : parm; - adj.base_index = i; - adj.base = parm; + apm.base_index = i; sc->args[i].orig_arg = node->definition ? parm : NULL_TREE; sc->args[i].orig_type = parm_type; @@ -588,7 +587,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) { default: /* No adjustment necessary for scalar arguments. */ - adj.op = IPA_PARM_OP_COPY; + apm.op = IPA_PARAM_OP_COPY; break; case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_CONSTANT_STEP: case SIMD_CLONE_ARG_TYPE_LINEAR_UVAL_VARIABLE_STEP: @@ -597,7 +596,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) = create_tmp_simd_array (IDENTIFIER_POINTER (DECL_NAME (parm)), TREE_TYPE (parm_type), sc->simdlen); - adj.op = IPA_PARM_OP_COPY; + apm.op = IPA_PARAM_OP_COPY; break; case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_CONSTANT_STEP: case SIMD_CLONE_ARG_TYPE_LINEAR_VAL_VARIABLE_STEP: @@ -609,22 +608,24 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) veclen /= GET_MODE_BITSIZE (SCALAR_TYPE_MODE (parm_type)); if (veclen > sc->simdlen) veclen = sc->simdlen; - adj.arg_prefix = "simd"; + apm.op = IPA_PARAM_OP_NEW; + apm.param_prefix_index = IPA_PARAM_PREFIX_SIMD; if (POINTER_TYPE_P (parm_type)) - adj.type = build_vector_type (pointer_sized_int_node, veclen); + apm.type = build_vector_type (pointer_sized_int_node, veclen); else - adj.type = build_vector_type (parm_type, veclen); - sc->args[i].vector_type = adj.type; + apm.type = build_vector_type (parm_type, veclen); + sc->args[i].vector_type = apm.type; for (j = veclen; j < sc->simdlen; j += veclen) { - adjustments.safe_push (adj); + vec_safe_push (new_params, apm); if (j == veclen) { - memset (&adj, 0, sizeof (adj)); - adj.op = IPA_PARM_OP_NEW; - adj.arg_prefix = "simd"; - adj.base_index = i; - adj.type = sc->args[i].vector_type; + memset (&apm, 0, sizeof (apm)); + apm.op = IPA_PARAM_OP_NEW; + apm.user_flag = 1; + apm.param_prefix_index = IPA_PARAM_PREFIX_SIMD; + apm.base_index = i; + apm.type = sc->args[i].vector_type; } } @@ -634,18 +635,19 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) ? IDENTIFIER_POINTER (DECL_NAME (parm)) : NULL, parm_type, sc->simdlen); } - adjustments.safe_push (adj); + vec_safe_push (new_params, apm); } if (sc->inbranch) { tree base_type = simd_clone_compute_base_data_type (sc->origin, sc); + ipa_adjusted_param apm; + memset (&apm, 0, sizeof (apm)); + apm.op = IPA_PARAM_OP_NEW; + apm.user_flag = 1; + apm.param_prefix_index = IPA_PARAM_PREFIX_MASK; - memset (&adj, 0, sizeof (adj)); - adj.op = IPA_PARM_OP_NEW; - adj.arg_prefix = "mask"; - - adj.base_index = i; + apm.base_index = i; if (INTEGRAL_TYPE_P (base_type) || POINTER_TYPE_P (base_type)) veclen = sc->vecsize_int; else @@ -654,16 +656,16 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) if (veclen > sc->simdlen) veclen = sc->simdlen; if (sc->mask_mode != VOIDmode) - adj.type + apm.type = lang_hooks.types.type_for_mode (sc->mask_mode, 1); else if (POINTER_TYPE_P (base_type)) - adj.type = build_vector_type (pointer_sized_int_node, veclen); + apm.type = build_vector_type (pointer_sized_int_node, veclen); else - adj.type = build_vector_type (base_type, veclen); - adjustments.safe_push (adj); + apm.type = build_vector_type (base_type, veclen); + vec_safe_push (new_params, apm); for (j = veclen; j < sc->simdlen; j += veclen) - adjustments.safe_push (adj); + vec_safe_push (new_params, apm); /* We have previously allocated one extra entry for the mask. Use it and fill it. */ @@ -679,7 +681,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) = create_tmp_simd_array ("mask", base_type, sc->simdlen); else if (veclen < sc->simdlen) sc->args[i].simd_array - = create_tmp_simd_array ("mask", adj.type, sc->simdlen / veclen); + = create_tmp_simd_array ("mask", apm.type, sc->simdlen / veclen); else sc->args[i].simd_array = NULL_TREE; } @@ -688,7 +690,14 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) } if (node->definition) - ipa_modify_formal_parameters (node->decl, adjustments); + { + args.release (); + ipa_param_body_adjustments *adjustments + = new ipa_param_body_adjustments (new_params, node->decl); + + adjustments->modify_formal_parameters (); + return adjustments; + } else { tree new_arg_types = NULL_TREE, new_reversed; @@ -697,15 +706,15 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) last_parm_void = true; gcc_assert (TYPE_ARG_TYPES (TREE_TYPE (node->decl))); - j = adjustments.length (); + j = vec_safe_length (new_params); for (i = 0; i < j; i++) { - struct ipa_parm_adjustment *adj = &adjustments[i]; + struct ipa_adjusted_param *apm = &(*new_params)[i]; tree ptype; - if (adj->op == IPA_PARM_OP_COPY) - ptype = args[adj->base_index]; + if (apm->op == IPA_PARAM_OP_COPY) + ptype = args[apm->base_index]; else - ptype = adj->type; + ptype = apm->type; new_arg_types = tree_cons (NULL_TREE, ptype, new_arg_types); } new_reversed = nreverse (new_arg_types); @@ -720,11 +729,9 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) tree new_type = build_distinct_type_copy (TREE_TYPE (node->decl)); TYPE_ARG_TYPES (new_type) = new_reversed; TREE_TYPE (node->decl) = new_type; - - adjustments.release (); + args.release (); + return NULL; } - args.release (); - return adjustments; } /* Initialize and copy the function arguments in NODE to their @@ -733,7 +740,7 @@ simd_clone_adjust_argument_types (struct cgraph_node *node) static gimple_seq simd_clone_init_simd_arrays (struct cgraph_node *node, - ipa_parm_adjustment_vec adjustments) + ipa_param_body_adjustments *adjustments) { gimple_seq seq = NULL; unsigned i = 0, j = 0, k; @@ -742,7 +749,7 @@ simd_clone_init_simd_arrays (struct cgraph_node *node, arg; arg = DECL_CHAIN (arg), i++, j++) { - if (adjustments[j].op == IPA_PARM_OP_COPY + if ((*adjustments->m_adj_params)[j].op == IPA_PARAM_OP_COPY || POINTER_TYPE_P (TREE_TYPE (arg))) continue; @@ -807,7 +814,7 @@ simd_clone_init_simd_arrays (struct cgraph_node *node, /* Callback info for ipa_simd_modify_stmt_ops below. */ struct modify_stmt_info { - ipa_parm_adjustment_vec adjustments; + ipa_param_body_adjustments *adjustments; gimple *stmt; /* True if the parent statement was modified by ipa_simd_modify_stmt_ops. */ @@ -827,9 +834,15 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data) tree *orig_tp = tp; if (TREE_CODE (*tp) == ADDR_EXPR) tp = &TREE_OPERAND (*tp, 0); - struct ipa_parm_adjustment *cand = NULL; + + if (TREE_CODE (*tp) == BIT_FIELD_REF + || TREE_CODE (*tp) == IMAGPART_EXPR + || TREE_CODE (*tp) == REALPART_EXPR) + tp = &TREE_OPERAND (*tp, 0); + + ipa_param_body_replacement *pbr = NULL; if (TREE_CODE (*tp) == PARM_DECL) - cand = ipa_get_adjustment_candidate (&tp, NULL, info->adjustments, true); + pbr = info->adjustments->get_expr_replacement (*tp, true); else { if (TYPE_P (*tp)) @@ -837,8 +850,8 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data) } tree repl = NULL_TREE; - if (cand) - repl = unshare_expr (cand->new_decl); + if (pbr) + repl = unshare_expr (pbr->repl); else { if (tp != orig_tp) @@ -904,70 +917,57 @@ ipa_simd_modify_stmt_ops (tree *tp, int *walk_subtrees, void *data) static void ipa_simd_modify_function_body (struct cgraph_node *node, - ipa_parm_adjustment_vec adjustments, + ipa_param_body_adjustments *adjustments, tree retval_array, tree iter) { basic_block bb; - unsigned int i, j, l; + unsigned int i, j; + - /* Re-use the adjustments array, but this time use it to replace - every function argument use to an offset into the corresponding - simd_array. */ + /* Register replacements for every function argument use to an offset into + the corresponding simd_array. */ for (i = 0, j = 0; i < node->simdclone->nargs; ++i, ++j) { - if (!node->simdclone->args[i].vector_arg) + if (!node->simdclone->args[i].vector_arg + || (*adjustments->m_adj_params)[j].user_flag) continue; tree basetype = TREE_TYPE (node->simdclone->args[i].orig_arg); tree vectype = TREE_TYPE (node->simdclone->args[i].vector_arg); - adjustments[j].new_decl - = build4 (ARRAY_REF, - basetype, - node->simdclone->args[i].simd_array, - iter, - NULL_TREE, NULL_TREE); - if (adjustments[j].op == IPA_PARM_OP_NONE - && TYPE_VECTOR_SUBPARTS (vectype) < node->simdclone->simdlen) + tree r = build4 (ARRAY_REF, basetype, node->simdclone->args[i].simd_array, + iter, NULL_TREE, NULL_TREE); + adjustments->register_replacement (&(*adjustments->m_adj_params)[j], r); + + if (TYPE_VECTOR_SUBPARTS (vectype) < node->simdclone->simdlen) j += node->simdclone->simdlen / TYPE_VECTOR_SUBPARTS (vectype) - 1; } - l = adjustments.length (); tree name; - FOR_EACH_SSA_NAME (i, name, cfun) { + tree base_var; if (SSA_NAME_VAR (name) - && TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL) + && TREE_CODE (SSA_NAME_VAR (name)) == PARM_DECL + && (base_var + = adjustments->get_replacement_ssa_base (SSA_NAME_VAR (name)))) { - for (j = 0; j < l; j++) - if (SSA_NAME_VAR (name) == adjustments[j].base - && adjustments[j].new_decl) - { - tree base_var; - if (adjustments[j].new_ssa_base == NULL_TREE) - { - base_var - = copy_var_decl (adjustments[j].base, - DECL_NAME (adjustments[j].base), - TREE_TYPE (adjustments[j].base)); - adjustments[j].new_ssa_base = base_var; - } - else - base_var = adjustments[j].new_ssa_base; - if (SSA_NAME_IS_DEFAULT_DEF (name)) - { - bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); - gimple_stmt_iterator gsi = gsi_after_labels (bb); - tree new_decl = unshare_expr (adjustments[j].new_decl); - set_ssa_default_def (cfun, adjustments[j].base, NULL_TREE); - SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var); - SSA_NAME_IS_DEFAULT_DEF (name) = 0; - gimple *stmt = gimple_build_assign (name, new_decl); - gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); - } - else - SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var); - } + if (SSA_NAME_IS_DEFAULT_DEF (name)) + { + tree old_decl = SSA_NAME_VAR (name); + bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); + gimple_stmt_iterator gsi = gsi_after_labels (bb); + ipa_param_body_replacement *pbr + = adjustments->lookup_replacement (old_decl, 0); + gcc_checking_assert (pbr && !pbr->by_ref && !pbr->reverse); + tree repl = unshare_expr (pbr->repl); + set_ssa_default_def (cfun, old_decl, NULL_TREE); + SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var); + SSA_NAME_IS_DEFAULT_DEF (name) = 0; + gimple *stmt = gimple_build_assign (name, repl); + gsi_insert_before (&gsi, stmt, GSI_SAME_STMT); + } + else + SET_SSA_NAME_VAR_OR_IDENTIFIER (name, base_var); } } @@ -1097,8 +1097,9 @@ simd_clone_adjust (struct cgraph_node *node) targetm.simd_clone.adjust (node); tree retval = simd_clone_adjust_return_type (node); - ipa_parm_adjustment_vec adjustments + ipa_param_body_adjustments *adjustments = simd_clone_adjust_argument_types (node); + gcc_assert (adjustments); push_gimplify_context (); @@ -1110,7 +1111,7 @@ simd_clone_adjust (struct cgraph_node *node) tree iter1 = make_ssa_name (iter); tree iter2 = NULL_TREE; ipa_simd_modify_function_body (node, adjustments, retval, iter1); - adjustments.release (); + delete adjustments; /* Initialize the iteration variable. */ basic_block entry_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); diff --git a/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c b/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c index 23dec2a661e..62d128acffd 100644 --- a/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c +++ b/gcc/testsuite/gcc.dg/ipa/ipa-sra-3.c @@ -34,5 +34,6 @@ void caller (void) return; } -/* { dg-final { scan-tree-dump "base: z, remove_param" "eipa_sra" } } */ -/* { dg-final { scan-tree-dump "base: calf, remove_param" "eipa_sra" } } */ +/* { dg-final { scan-tree-dump "ox.isra" "eipa_sra" } } */ +/* { dg-final { scan-tree-dump-not "\\nox.isra\[^;)]*z" "eipa_sra" } } */ +/* { dg-final { scan-tree-dump-not "\\nox.isra\[^;)]*calf" "eipa_sra" } } */ diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index db490b20c3e..01447295597 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -4697,84 +4697,81 @@ get_param_index (tree base, vec parms) gcc_unreachable (); } -/* Convert the decisions made at the representative level into compact - parameter adjustments. REPRESENTATIVES are pointers to first - representatives of each param accesses, ADJUSTMENTS_COUNT is the expected - final number of adjustments. */ +/* Convert the decisions made at the representative level into description of + new arguments for parameter manipulation. REPRESENTATIVES are pointers to + first representatives of each param accesses, EXPECTED_COUNT is the expected + final number of new number of parameters. */ -static ipa_parm_adjustment_vec +static vec * turn_representatives_into_adjustments (vec representatives, - int adjustments_count) + int expected_count) { vec parms; - ipa_parm_adjustment_vec adjustments; + vec *adj_params = NULL; tree parm; int i; - gcc_assert (adjustments_count > 0); parms = ipa_get_vector_of_formal_parms (current_function_decl); - adjustments.create (adjustments_count); + vec_safe_reserve_exact (adj_params, expected_count); parm = DECL_ARGUMENTS (current_function_decl); for (i = 0; i < func_param_count; i++, parm = DECL_CHAIN (parm)) { struct access *repr = representatives[i]; - if (!repr || no_accesses_p (repr)) + if (!repr) { - struct ipa_parm_adjustment adj; + struct ipa_adjusted_param apm; - memset (&adj, 0, sizeof (adj)); - adj.base_index = get_param_index (parm, parms); - adj.base = parm; - if (!repr) - adj.op = IPA_PARM_OP_COPY; - else - adj.op = IPA_PARM_OP_REMOVE; - adj.arg_prefix = "ISRA"; - adjustments.quick_push (adj); + memset (&apm, 0, sizeof (apm)); + apm.op = IPA_PARAM_OP_COPY; + apm.base_index = get_param_index (parm, parms); + apm.param_prefix_index = IPA_PARAM_PREFIX_ISRA; + adj_params->quick_push (apm); } + else if (no_accesses_p (repr)) + continue; else { - struct ipa_parm_adjustment adj; int index = get_param_index (parm, parms); for (; repr; repr = repr->next_grp) { - memset (&adj, 0, sizeof (adj)); + struct ipa_adjusted_param apm; + memset (&apm, 0, sizeof (apm)); gcc_assert (repr->base == parm); - adj.base_index = index; - adj.base = repr->base; - adj.type = repr->type; - adj.alias_ptr_type = reference_alias_ptr_type (repr->expr); - adj.offset = repr->offset; - adj.reverse = repr->reverse; - adj.by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base)) + apm.op = IPA_PARAM_OP_SPLIT; + apm.base_index = index; + apm.type = repr->type; + apm.alias_ptr_type = reference_alias_ptr_type (repr->expr); + apm.unit_offset = repr->offset / BITS_PER_UNIT; + apm.reverse = repr->reverse; + apm.by_ref = (POINTER_TYPE_P (TREE_TYPE (repr->base)) && (repr->grp_maybe_modified || repr->grp_not_necessarilly_dereferenced)); - adj.arg_prefix = "ISRA"; - adjustments.quick_push (adj); + apm.param_prefix_index = IPA_PARAM_PREFIX_ISRA; + adj_params->quick_push (apm); } } } parms.release (); - return adjustments; + return adj_params; } /* Analyze the collected accesses and produce a plan what to do with the - parameters in the form of adjustments, NULL meaning nothing. */ + parameters in the form of a vector of adjusted parameters and store it to + ADJ_PARAMS_P or return NULL if no adjustments are to be made. */ -static ipa_parm_adjustment_vec -analyze_all_param_acesses (void) +static bool +analyze_all_param_acesses (vec **adj_params_p) { enum ipa_splicing_result repr_state; bool proceed = false; - int i, adjustments_count = 0; + int i, expected_count = 0; vec representatives; - ipa_parm_adjustment_vec adjustments; repr_state = splice_all_param_accesses (representatives); if (repr_state == NO_GOOD_ACCESS) - return ipa_parm_adjustment_vec (); + return NULL;; /* If there are any parameters passed by reference which are not modified directly, we need to check whether they can be modified indirectly. */ @@ -4792,7 +4789,7 @@ analyze_all_param_acesses (void) { if (repr->grp_scalar_ptr) { - adjustments_count++; + expected_count++; if (repr->grp_not_necessarilly_dereferenced || repr->grp_maybe_modified) representatives[i] = NULL; @@ -4809,11 +4806,11 @@ analyze_all_param_acesses (void) if (new_components == 0) { representatives[i] = NULL; - adjustments_count++; + expected_count++; } else { - adjustments_count += new_components; + expected_count += new_components; sra_stats.aggregate_params_reduced++; sra_stats.param_reductions_created += new_components; proceed = true; @@ -4827,7 +4824,8 @@ analyze_all_param_acesses (void) proceed = true; sra_stats.deleted_unused_parameters++; } - adjustments_count++; + else + expected_count++; } } @@ -4835,372 +4833,10 @@ analyze_all_param_acesses (void) fprintf (dump_file, "NOT proceeding to change params.\n"); if (proceed) - adjustments = turn_representatives_into_adjustments (representatives, - adjustments_count); - else - adjustments = ipa_parm_adjustment_vec (); - + *adj_params_p = turn_representatives_into_adjustments (representatives, + expected_count); representatives.release (); - return adjustments; -} - -/* If a parameter replacement identified by ADJ does not yet exist in the form - of declaration, create it and record it, otherwise return the previously - created one. */ - -static tree -get_replaced_param_substitute (struct ipa_parm_adjustment *adj) -{ - tree repl; - if (!adj->new_ssa_base) - { - char *pretty_name = make_fancy_name (adj->base); - - repl = create_tmp_reg (TREE_TYPE (adj->base), "ISR"); - DECL_NAME (repl) = get_identifier (pretty_name); - DECL_NAMELESS (repl) = 1; - obstack_free (&name_obstack, pretty_name); - - adj->new_ssa_base = repl; - } - else - repl = adj->new_ssa_base; - return repl; -} - -/* Find the first adjustment for a particular parameter BASE in a vector of - ADJUSTMENTS which is not a copy_param. Return NULL if there is no such - adjustment. */ - -static struct ipa_parm_adjustment * -get_adjustment_for_base (ipa_parm_adjustment_vec adjustments, tree base) -{ - int i, len; - - len = adjustments.length (); - for (i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - - adj = &adjustments[i]; - if (adj->op != IPA_PARM_OP_COPY && adj->base == base) - return adj; - } - - return NULL; -} - -/* If OLD_NAME, which is being defined by statement STMT, is an SSA_NAME of a - parameter which is to be removed because its value is not used, create a new - SSA_NAME relating to a replacement VAR_DECL, replace all uses of the - original with it and return it. If there is no need to re-map, return NULL. - ADJUSTMENTS is a pointer to a vector of IPA-SRA adjustments. */ - -static tree -replace_removed_params_ssa_names (tree old_name, gimple *stmt, - ipa_parm_adjustment_vec adjustments) -{ - struct ipa_parm_adjustment *adj; - tree decl, repl, new_name; - - if (TREE_CODE (old_name) != SSA_NAME) - return NULL; - - decl = SSA_NAME_VAR (old_name); - if (decl == NULL_TREE - || TREE_CODE (decl) != PARM_DECL) - return NULL; - - adj = get_adjustment_for_base (adjustments, decl); - if (!adj) - return NULL; - - repl = get_replaced_param_substitute (adj); - new_name = make_ssa_name (repl, stmt); - SSA_NAME_OCCURS_IN_ABNORMAL_PHI (new_name) - = SSA_NAME_OCCURS_IN_ABNORMAL_PHI (old_name); - - if (dump_file) - { - fprintf (dump_file, "replacing an SSA name of a removed param "); - print_generic_expr (dump_file, old_name); - fprintf (dump_file, " with "); - print_generic_expr (dump_file, new_name); - fprintf (dump_file, "\n"); - } - - replace_uses_by (old_name, new_name); - return new_name; -} - -/* If the statement STMT contains any expressions that need to replaced with a - different one as noted by ADJUSTMENTS, do so. Handle any potential type - incompatibilities (GSI is used to accommodate conversion statements and must - point to the statement). Return true iff the statement was modified. */ - -static bool -sra_ipa_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi, - ipa_parm_adjustment_vec adjustments) -{ - tree *lhs_p, *rhs_p; - bool any; - - if (!gimple_assign_single_p (stmt)) - return false; - - rhs_p = gimple_assign_rhs1_ptr (stmt); - lhs_p = gimple_assign_lhs_ptr (stmt); - - any = ipa_modify_expr (rhs_p, false, adjustments); - any |= ipa_modify_expr (lhs_p, false, adjustments); - if (any) - { - tree new_rhs = NULL_TREE; - - if (!useless_type_conversion_p (TREE_TYPE (*lhs_p), TREE_TYPE (*rhs_p))) - { - if (TREE_CODE (*rhs_p) == CONSTRUCTOR) - { - /* V_C_Es of constructors can cause trouble (PR 42714). */ - if (is_gimple_reg_type (TREE_TYPE (*lhs_p))) - *rhs_p = build_zero_cst (TREE_TYPE (*lhs_p)); - else - *rhs_p = build_constructor (TREE_TYPE (*lhs_p), - NULL); - } - else - new_rhs = fold_build1_loc (gimple_location (stmt), - VIEW_CONVERT_EXPR, TREE_TYPE (*lhs_p), - *rhs_p); - } - else if (REFERENCE_CLASS_P (*rhs_p) - && is_gimple_reg_type (TREE_TYPE (*lhs_p)) - && !is_gimple_reg (*lhs_p)) - /* This can happen when an assignment in between two single field - structures is turned into an assignment in between two pointers to - scalars (PR 42237). */ - new_rhs = *rhs_p; - - if (new_rhs) - { - tree tmp = force_gimple_operand_gsi (gsi, new_rhs, true, NULL_TREE, - true, GSI_SAME_STMT); - - gimple_assign_set_rhs_from_tree (gsi, tmp); - } - - return true; - } - - return false; -} - -/* Traverse the function body and all modifications as described in - ADJUSTMENTS. Return true iff the CFG has been changed. */ - -bool -ipa_sra_modify_function_body (ipa_parm_adjustment_vec adjustments) -{ - bool cfg_changed = false; - basic_block bb; - - FOR_EACH_BB_FN (bb, cfun) - { - gimple_stmt_iterator gsi; - - for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) - { - gphi *phi = as_a (gsi_stmt (gsi)); - tree new_lhs, old_lhs = gimple_phi_result (phi); - new_lhs = replace_removed_params_ssa_names (old_lhs, phi, adjustments); - if (new_lhs) - { - gimple_phi_set_result (phi, new_lhs); - release_ssa_name (old_lhs); - } - } - - gsi = gsi_start_bb (bb); - while (!gsi_end_p (gsi)) - { - gimple *stmt = gsi_stmt (gsi); - bool modified = false; - tree *t; - unsigned i; - - switch (gimple_code (stmt)) - { - case GIMPLE_RETURN: - t = gimple_return_retval_ptr (as_a (stmt)); - if (*t != NULL_TREE) - modified |= ipa_modify_expr (t, true, adjustments); - break; - - case GIMPLE_ASSIGN: - modified |= sra_ipa_modify_assign (stmt, &gsi, adjustments); - break; - - case GIMPLE_CALL: - /* Operands must be processed before the lhs. */ - for (i = 0; i < gimple_call_num_args (stmt); i++) - { - t = gimple_call_arg_ptr (stmt, i); - modified |= ipa_modify_expr (t, true, adjustments); - } - - if (gimple_call_lhs (stmt)) - { - t = gimple_call_lhs_ptr (stmt); - modified |= ipa_modify_expr (t, false, adjustments); - } - break; - - case GIMPLE_ASM: - { - gasm *asm_stmt = as_a (stmt); - for (i = 0; i < gimple_asm_ninputs (asm_stmt); i++) - { - t = &TREE_VALUE (gimple_asm_input_op (asm_stmt, i)); - modified |= ipa_modify_expr (t, true, adjustments); - } - for (i = 0; i < gimple_asm_noutputs (asm_stmt); i++) - { - t = &TREE_VALUE (gimple_asm_output_op (asm_stmt, i)); - modified |= ipa_modify_expr (t, false, adjustments); - } - } - break; - - default: - break; - } - - def_operand_p defp; - ssa_op_iter iter; - FOR_EACH_SSA_DEF_OPERAND (defp, stmt, iter, SSA_OP_DEF) - { - tree old_def = DEF_FROM_PTR (defp); - if (tree new_def = replace_removed_params_ssa_names (old_def, stmt, - adjustments)) - { - SET_DEF (defp, new_def); - release_ssa_name (old_def); - modified = true; - } - } - - if (modified) - { - update_stmt (stmt); - if (maybe_clean_eh_stmt (stmt) - && gimple_purge_dead_eh_edges (gimple_bb (stmt))) - cfg_changed = true; - } - gsi_next (&gsi); - } - } - - return cfg_changed; -} - -/* Call gimple_debug_bind_reset_value on all debug statements describing - gimple register parameters that are being removed or replaced. */ - -static void -sra_ipa_reset_debug_stmts (ipa_parm_adjustment_vec adjustments) -{ - int i, len; - gimple_stmt_iterator *gsip = NULL, gsi; - - if (MAY_HAVE_DEBUG_STMTS && single_succ_p (ENTRY_BLOCK_PTR_FOR_FN (cfun))) - { - gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun))); - gsip = &gsi; - } - len = adjustments.length (); - for (i = 0; i < len; i++) - { - struct ipa_parm_adjustment *adj; - imm_use_iterator ui; - gimple *stmt; - gdebug *def_temp; - tree name, vexpr, copy = NULL_TREE; - use_operand_p use_p; - - adj = &adjustments[i]; - if (adj->op == IPA_PARM_OP_COPY || !is_gimple_reg (adj->base)) - continue; - name = ssa_default_def (cfun, adj->base); - vexpr = NULL; - if (name) - FOR_EACH_IMM_USE_STMT (stmt, ui, name) - { - if (gimple_clobber_p (stmt)) - { - gimple_stmt_iterator cgsi = gsi_for_stmt (stmt); - unlink_stmt_vdef (stmt); - gsi_remove (&cgsi, true); - release_defs (stmt); - continue; - } - /* All other users must have been removed by - ipa_sra_modify_function_body. */ - gcc_assert (is_gimple_debug (stmt)); - if (vexpr == NULL && gsip != NULL) - { - gcc_assert (TREE_CODE (adj->base) == PARM_DECL); - vexpr = make_node (DEBUG_EXPR_DECL); - def_temp = gimple_build_debug_source_bind (vexpr, adj->base, - NULL); - DECL_ARTIFICIAL (vexpr) = 1; - TREE_TYPE (vexpr) = TREE_TYPE (name); - SET_DECL_MODE (vexpr, DECL_MODE (adj->base)); - gsi_insert_before (gsip, def_temp, GSI_SAME_STMT); - } - if (vexpr) - { - FOR_EACH_IMM_USE_ON_STMT (use_p, ui) - SET_USE (use_p, vexpr); - } - else - gimple_debug_bind_reset_value (stmt); - update_stmt (stmt); - } - /* Create a VAR_DECL for debug info purposes. */ - if (!DECL_IGNORED_P (adj->base)) - { - copy = build_decl (DECL_SOURCE_LOCATION (current_function_decl), - VAR_DECL, DECL_NAME (adj->base), - TREE_TYPE (adj->base)); - if (DECL_PT_UID_SET_P (adj->base)) - SET_DECL_PT_UID (copy, DECL_PT_UID (adj->base)); - TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (adj->base); - TREE_READONLY (copy) = TREE_READONLY (adj->base); - TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (adj->base); - DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (adj->base); - DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (adj->base); - DECL_IGNORED_P (copy) = DECL_IGNORED_P (adj->base); - DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (adj->base); - DECL_SEEN_IN_BIND_EXPR_P (copy) = 1; - SET_DECL_RTL (copy, 0); - TREE_USED (copy) = 1; - DECL_CONTEXT (copy) = current_function_decl; - add_local_decl (cfun, copy); - DECL_CHAIN (copy) = - BLOCK_VARS (DECL_INITIAL (current_function_decl)); - BLOCK_VARS (DECL_INITIAL (current_function_decl)) = copy; - } - if (gsip != NULL && copy && target_for_debug_bind (adj->base)) - { - gcc_assert (TREE_CODE (adj->base) == PARM_DECL); - if (vexpr) - def_temp = gimple_build_debug_bind (copy, vexpr, NULL); - else - def_temp = gimple_build_debug_source_bind (copy, adj->base, - NULL); - gsi_insert_before (gsip, def_temp, GSI_SAME_STMT); - } - } + return proceed; } /* Return false if all callers have at least as many actual arguments as there @@ -5233,13 +4869,21 @@ some_callers_have_no_vuse_p (struct cgraph_node *node, return false; } +struct convert_callers_data +{ + /* Declaration of the callee. */ + tree callee_fndecl; + /* List of adjusted parameters together with their operations. */ + vec *adj_params; +}; + /* Convert all callers of NODE. */ static bool convert_callers_for_node (struct cgraph_node *node, void *data) { - ipa_parm_adjustment_vec *adjustments = (ipa_parm_adjustment_vec *) data; + struct convert_callers_data *ccd = (struct convert_callers_data *) data; bitmap recomputed_callers = BITMAP_ALLOC (NULL); struct cgraph_edge *cs; @@ -5251,8 +4895,8 @@ convert_callers_for_node (struct cgraph_node *node, fprintf (dump_file, "Adjusting call %s -> %s\n", cs->caller->dump_name (), cs->callee->dump_name ()); - ipa_modify_call_arguments (cs, cs->call_stmt, *adjustments); - + ipa_param_adjustments adjustments (ccd->adj_params, ccd->callee_fndecl); + adjustments.modify_call_arguments (cs); pop_cfun (); } @@ -5265,16 +4909,17 @@ convert_callers_for_node (struct cgraph_node *node, return true; } -/* Convert all callers of NODE to pass parameters as given in ADJUSTMENTS. */ +/* Convert all callers of NODE to pass parameters as given in ADJ_PARAMS. */ static void convert_callers (struct cgraph_node *node, tree old_decl, - ipa_parm_adjustment_vec adjustments) + vec *adj_params) { basic_block this_block; - - node->call_for_symbol_and_aliases (convert_callers_for_node, - &adjustments, false); + struct convert_callers_data ccd; + ccd.callee_fndecl = node->decl; + ccd.adj_params = adj_params; + node->call_for_symbol_and_aliases (convert_callers_for_node, &ccd, false); if (!encountered_recursive_call) return; @@ -5295,8 +4940,8 @@ convert_callers (struct cgraph_node *node, tree old_decl, { if (dump_file) fprintf (dump_file, "Adjusting recursive call"); - gimple_call_set_fndecl (stmt, node->decl); - ipa_modify_call_arguments (NULL, stmt, adjustments); + ipa_param_adjustments adjustments (adj_params); + adjustments.modify_call_arguments (stmt, node->decl); } } } @@ -5305,10 +4950,11 @@ convert_callers (struct cgraph_node *node, tree old_decl, } /* Perform all the modification required in IPA-SRA for NODE to have parameters - as given in ADJUSTMENTS. Return true iff the CFG has been changed. */ + as given in ADJ_PARAMS. Return true iff the CFG has been changed. */ static bool -modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments) +modify_function (struct cgraph_node *node, + vec *adj_params) { struct cgraph_node *new_node; bool cfg_changed; @@ -5325,13 +4971,12 @@ modify_function (struct cgraph_node *node, ipa_parm_adjustment_vec adjustments) NULL, false, NULL, NULL, "isra"); redirect_callers.release (); + new_node->make_local (); push_cfun (DECL_STRUCT_FUNCTION (new_node->decl)); - ipa_modify_formal_parameters (current_function_decl, adjustments); - cfg_changed = ipa_sra_modify_function_body (adjustments); - sra_ipa_reset_debug_stmts (adjustments); - convert_callers (new_node, node->decl, adjustments); - new_node->make_local (); + convert_callers (new_node, node->decl, adj_params); + ipa_param_body_adjustments body_adj (adj_params, new_node->decl); + cfg_changed = body_adj.perform_cfun_body_modifications (); return cfg_changed; } @@ -5497,7 +5142,7 @@ static unsigned int ipa_early_sra (void) { struct cgraph_node *node = cgraph_node::get (current_function_decl); - ipa_parm_adjustment_vec adjustments; + vec *adj_params; int ret = 0; if (!ipa_sra_preliminary_function_checks (node)) @@ -5552,17 +5197,15 @@ ipa_early_sra (void) goto out; } - adjustments = analyze_all_param_acesses (); - if (!adjustments.exists ()) + if (!analyze_all_param_acesses (&adj_params)) goto out; if (dump_file) - ipa_dump_param_adjustments (dump_file, adjustments, current_function_decl); + ipa_dump_adjusted_parameters (dump_file, adj_params); - if (modify_function (node, adjustments)) + if (modify_function (node, adj_params)) ret = TODO_update_ssa | TODO_cleanup_cfg; else ret = TODO_update_ssa; - adjustments.release (); statistics_counter_event (cfun, "Unused parameters deleted", sra_stats.deleted_unused_parameters);