446 lines
18 KiB
C
446 lines
18 KiB
C
|
/* Manipulation of formal and actual parameters of functions and function
|
||
|
calls.
|
||
|
Copyright (C) 2017-2023 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
|
||
|
<http://www.gnu.org/licenses/>.
|
||
|
|
||
|
|
||
|
|
||
|
This file defines classes and other data structures that are used to manipulate
|
||
|
the prototype of a function, especially to create, remove or split its formal
|
||
|
parameters, but also to remove its return value, and also its call statements
|
||
|
correspondingly.
|
||
|
|
||
|
The most basic one is a vector of structures ipa_adjusted_param. It is simply
|
||
|
a description how the new parameters should look like after the transformation
|
||
|
in what way they relate to the previous ones (if in any). Such relation to an
|
||
|
old parameter can be an outright copy or an IPA-SRA replacement. If an old
|
||
|
parameter is not listed or otherwise mentioned, it is removed as unused or at
|
||
|
least unnecessary. Note that this most basic structure does not work for
|
||
|
modifying calls of functions with variable number of arguments.
|
||
|
|
||
|
Class ipa_param_adjustments is only a little more than a thin encapsulation of
|
||
|
a vector of ipa_param_adjustments. Along with this vector it contains an index
|
||
|
of the first potential vararg argument and a boolean flag whether the return
|
||
|
value should be removed or not. Moreover, the class contains method
|
||
|
modify_call which can transform a call statement so that it correctly calls a
|
||
|
modified function. These two data structures were designed to have a small
|
||
|
memory footprint because they are allocated for each clone of a call graph node
|
||
|
that has its prototype changed and live until the end of IPA clone
|
||
|
materialization and call redirection phase.
|
||
|
|
||
|
On the other hand, class ipa_param_body_adjustments can afford to allocate more
|
||
|
data because its life span is much smaller, it is allocated and destroyed in
|
||
|
the course of materialization of each single clone that needs it or only when a
|
||
|
particular pass needs to change a function it is operating on. This class has
|
||
|
various methods required to change function declaration and the body of the
|
||
|
function according to instructions given either by class ipa_param_adjustments
|
||
|
or only a vector of ipa_adjusted_params.
|
||
|
|
||
|
When these classes are used in the context of call graph clone materialization
|
||
|
and subsequent call statement redirection - which is the point at which we
|
||
|
modify arguments in call statements - they need to cooperate with each other in
|
||
|
order to handle what we refer to as pass-through (IPA-SRA) splits. These are
|
||
|
situations when a formal parameter of one function is split into several
|
||
|
smaller ones and some of them are then passed on in a call to another function
|
||
|
because the formal parameter of this callee has also been split.
|
||
|
|
||
|
Consider a simple example:
|
||
|
|
||
|
struct S {int a, b, c;};
|
||
|
struct Z {int x; S s;};
|
||
|
|
||
|
foo (S s)
|
||
|
{
|
||
|
use (s.b);
|
||
|
}
|
||
|
|
||
|
bar (Z z)
|
||
|
{
|
||
|
use (z.s.a);
|
||
|
foo (z.s);
|
||
|
}
|
||
|
|
||
|
baz ()
|
||
|
{
|
||
|
bar (*global);
|
||
|
}
|
||
|
|
||
|
Both bar and foo would have their parameter split. Foo would receive one
|
||
|
replacement representing s.b. Function bar would see its parameter split into
|
||
|
one replacement representing z.s.a and another representing z.s.b which would
|
||
|
be passed on to foo. It would be a so called pass-through split IPA-SRA
|
||
|
replacement, one which is passed in a call as an actual argument to another
|
||
|
IPA-SRA replacement in another function.
|
||
|
|
||
|
Note that the call chain the example can be arbitrarily long and recursive and
|
||
|
that any function in it can be cloned by another IPA pass and any number of
|
||
|
adjacent functions in the call chain can be inlined into each other. Call
|
||
|
redirection takes place only after bodies of the function have been modified by
|
||
|
all of the above.
|
||
|
|
||
|
Call redirection has to be able to find the right decl or SSA_NAME that
|
||
|
corresponds to the transitive split in the caller. The SSA names are assigned
|
||
|
right after clone materialization/ modification and cannot be "added" to call
|
||
|
arguments at any later point. Moreover, if the caller has been inlined the
|
||
|
SSA_NAMEs in question no longer belong to PARM_DECLs but to VAR_DECLs,
|
||
|
indistinguishable from any others.
|
||
|
|
||
|
Therefore, when clone materialization finds a call statement which it knows is
|
||
|
a part of a transitive split, it will simply add as arguments all new "split"
|
||
|
replacements (that have grater or equal offset than the original call
|
||
|
argument):
|
||
|
|
||
|
foo (repl_for_a, repl_for_b, <rest of original arguments>);
|
||
|
|
||
|
It will also store into ipa_edge_modification_info (which is internal to
|
||
|
ipa-param-modification.c) information about which replacement is which and
|
||
|
where original arguments are. Call redirection will then invoke
|
||
|
ipa_param_adjustments::modify_call which will access this information and
|
||
|
eliminate all replacements which the callee does not expect (repl_for_a in our
|
||
|
example above). In between these two steps, however, a call statement might
|
||
|
have extraneous arguments. */
|
||
|
|
||
|
#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. */
|
||
|
enum ipa_param_name_prefix_indices
|
||
|
{
|
||
|
IPA_PARAM_PREFIX_SYNTH,
|
||
|
IPA_PARAM_PREFIX_ISRA,
|
||
|
IPA_PARAM_PREFIX_SIMD,
|
||
|
IPA_PARAM_PREFIX_MASK,
|
||
|
IPA_PARAM_PREFIX_COUNT
|
||
|
};
|
||
|
|
||
|
/* We do not support manipulating functions with more than
|
||
|
1<<IPA_PARAM_MAX_INDEX_BITS parameters. */
|
||
|
#define IPA_PARAM_MAX_INDEX_BITS 16
|
||
|
|
||
|
/* Operation to be performed for the parameter in ipa_parm_adjustment
|
||
|
below. */
|
||
|
|
||
|
enum ipa_parm_op
|
||
|
{
|
||
|
/* Do not use or you will trigger an assert. */
|
||
|
IPA_PARAM_OP_UNDEFINED,
|
||
|
|
||
|
/* This new parameter is an unmodified parameter at index base_index. */
|
||
|
IPA_PARAM_OP_COPY,
|
||
|
|
||
|
/* This describes a brand new parameter. If it somehow relates to any
|
||
|
original parameters, the user needs to manage the transition itself. */
|
||
|
IPA_PARAM_OP_NEW,
|
||
|
|
||
|
/* Split parameter as indicated by fields base_index, offset and type. */
|
||
|
IPA_PARAM_OP_SPLIT
|
||
|
};
|
||
|
|
||
|
/* Structure that describes one parameter of a function after transformation.
|
||
|
Omitted parameters will be removed. */
|
||
|
|
||
|
struct GTY(()) ipa_adjusted_param
|
||
|
{
|
||
|
/* Type of the new parameter. Required for all operations except
|
||
|
IPA_PARM_OP_COPY when the original type will be preserved. */
|
||
|
tree type;
|
||
|
|
||
|
/* Alias reference type to be used in MEM_REFs when adjusting caller
|
||
|
arguments. Required for IPA_PARM_OP_SPLIT operation. */
|
||
|
tree alias_ptr_type;
|
||
|
|
||
|
/* Offset into the original parameter (for the cases when the new parameter
|
||
|
is a component of an original one). Required for IPA_PARM_OP_SPLIT
|
||
|
operation. */
|
||
|
unsigned unit_offset;
|
||
|
|
||
|
/* Zero based index of the original parameter this one is based on. Required
|
||
|
for IPA_PARAM_OP_COPY and IPA_PARAM_OP_SPLIT, users of IPA_PARAM_OP_NEW
|
||
|
only need to specify it if they use replacement lookup provided by
|
||
|
ipa_param_body_adjustments. */
|
||
|
unsigned base_index : IPA_PARAM_MAX_INDEX_BITS;
|
||
|
|
||
|
/* Zero based index of the parameter this one is based on in the previous
|
||
|
clone. If there is no previous clone, it must be equal to base_index. */
|
||
|
unsigned prev_clone_index : IPA_PARAM_MAX_INDEX_BITS;
|
||
|
|
||
|
/* Specify the operation, if any, to be performed on the parameter. */
|
||
|
enum ipa_parm_op op : 2;
|
||
|
|
||
|
/* If set, this structure describes a parameter copied over from a previous
|
||
|
IPA clone, any transformations are thus not to be re-done. */
|
||
|
unsigned prev_clone_adjustment : 1;
|
||
|
|
||
|
/* Index into ipa_param_prefixes specifying a prefix to be used with
|
||
|
DECL_NAMEs of newly synthesized parameters. */
|
||
|
unsigned param_prefix_index : 2;
|
||
|
|
||
|
/* Storage order of the original parameter (for the cases when the new
|
||
|
parameter is a component of an original one). */
|
||
|
unsigned reverse : 1;
|
||
|
|
||
|
/* A bit free for the user. */
|
||
|
unsigned user_flag : 1;
|
||
|
};
|
||
|
|
||
|
void ipa_dump_adjusted_parameters (FILE *f,
|
||
|
vec<ipa_adjusted_param, va_gc> *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. Used to describe all cgraph node clones that have their parameters
|
||
|
changed, therefore the class should only have a small memory footprint. */
|
||
|
|
||
|
class GTY(()) ipa_param_adjustments
|
||
|
{
|
||
|
public:
|
||
|
/* Constructor from NEW_PARAMS showing how new parameters should look like
|
||
|
plus copying any pre-existing actual arguments starting from argument
|
||
|
with index ALWAYS_COPY_START (if non-negative, negative means do not copy
|
||
|
anything beyond what is described in NEW_PARAMS), and SKIP_RETURN, which
|
||
|
indicates that the function should return void after transformation. */
|
||
|
|
||
|
ipa_param_adjustments (vec<ipa_adjusted_param, va_gc> *new_params,
|
||
|
int always_copy_start, bool skip_return)
|
||
|
: m_adj_params (new_params), m_always_copy_start (always_copy_start),
|
||
|
m_skip_return (skip_return)
|
||
|
{}
|
||
|
|
||
|
/* Modify a call statement arguments (and possibly remove the return value)
|
||
|
as described in the data fields of this class. */
|
||
|
gcall *modify_call (cgraph_edge *cs, bool update_references);
|
||
|
/* Return if the first parameter is left intact. */
|
||
|
bool first_param_intact_p ();
|
||
|
/* Build a function type corresponding to the modified call. */
|
||
|
tree build_new_function_type (tree old_type, bool type_is_original_p);
|
||
|
/* Build a declaration corresponding to the target of the modified call. */
|
||
|
tree adjust_decl (tree orig_decl);
|
||
|
/* Fill a vector marking which parameters are intact by the described
|
||
|
modifications. */
|
||
|
void get_surviving_params (vec<bool> *surviving_params);
|
||
|
/* Fill a vector with new indices of surviving original parameters. */
|
||
|
void get_updated_indices (vec<int> *new_indices);
|
||
|
/* Return the original index for the given new parameter index. Return a
|
||
|
negative number if not available. */
|
||
|
int get_original_index (int newidx);
|
||
|
|
||
|
void dump (FILE *f);
|
||
|
void debug ();
|
||
|
|
||
|
/* How the known part of arguments should look like. */
|
||
|
vec<ipa_adjusted_param, va_gc> *m_adj_params;
|
||
|
|
||
|
/* If non-negative, copy any arguments starting at this offset without any
|
||
|
modifications so that functions with variable number of arguments can be
|
||
|
modified. This number should be equal to the number of original forma
|
||
|
parameters. */
|
||
|
int m_always_copy_start;
|
||
|
/* If true, make the function not return any value. */
|
||
|
bool m_skip_return;
|
||
|
|
||
|
static bool type_attribute_allowed_p (tree);
|
||
|
private:
|
||
|
ipa_param_adjustments () {}
|
||
|
|
||
|
void init (vec<tree> *cur_params);
|
||
|
int get_max_base_index ();
|
||
|
bool method2func_p (tree orig_type);
|
||
|
};
|
||
|
|
||
|
/* Structure used to map expressions accessing split or replaced parameters to
|
||
|
new PARM_DECLs. */
|
||
|
|
||
|
struct ipa_param_body_replacement
|
||
|
{
|
||
|
/* The old decl of the original parameter. */
|
||
|
tree base;
|
||
|
/* The new decl it should be replaced with. */
|
||
|
tree repl;
|
||
|
/* Users of ipa_param_body_adjustments that modify standalone functions
|
||
|
outside of IPA clone materialization can use the following field for their
|
||
|
internal purposes. */
|
||
|
tree dummy;
|
||
|
/* The offset within BASE that REPL represents. */
|
||
|
unsigned unit_offset;
|
||
|
};
|
||
|
|
||
|
struct ipa_replace_map;
|
||
|
|
||
|
/* Class used when actually performing adjustments to formal parameters of a
|
||
|
function to map accesses that need to be replaced to replacements. The
|
||
|
class attempts to work in two very different sets of circumstances: as a
|
||
|
part of tree-inine.c's tree_function_versioning machinery to clone functions
|
||
|
(when M_ID is not NULL) and in s standalone fashion, modifying an existing
|
||
|
function in place (when M_ID is NULL). While a lot of stuff handled in a
|
||
|
unified way in both modes, there are many aspects of the processs that
|
||
|
requires distinct paths. */
|
||
|
|
||
|
class ipa_param_body_adjustments
|
||
|
{
|
||
|
public:
|
||
|
/* Constructor to use from within tree-inline. */
|
||
|
ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
|
||
|
tree fndecl, tree old_fndecl,
|
||
|
struct copy_body_data *id, tree *vars,
|
||
|
vec<ipa_replace_map *, va_gc> *tree_map);
|
||
|
/* Constructor to use for modifying a function outside of tree-inline from an
|
||
|
instance of ipa_param_adjustments. */
|
||
|
ipa_param_body_adjustments (ipa_param_adjustments *adjustments,
|
||
|
tree fndecl);
|
||
|
/* Constructor to use for modifying a function outside of tree-inline from a
|
||
|
simple vector of desired parameter modification. */
|
||
|
ipa_param_body_adjustments (vec<ipa_adjusted_param, va_gc> *adj_params,
|
||
|
tree fndecl);
|
||
|
|
||
|
/* The do-it-all function for modifying a function outside of
|
||
|
tree-inline. */
|
||
|
bool perform_cfun_body_modifications ();
|
||
|
|
||
|
/* Change the PARM_DECLs. */
|
||
|
void modify_formal_parameters ();
|
||
|
/* Register a REPLACEMENT for accesses to BASE at UNIT_OFFSET. */
|
||
|
void register_replacement (tree base, unsigned unit_offset, tree replacement);
|
||
|
/* Register a replacement decl for the transformation done in APM. */
|
||
|
void register_replacement (ipa_adjusted_param *apm, tree replacement);
|
||
|
/* Sort m_replacements and set m_sorted_replacements_p to true. Users that
|
||
|
call register_replacement themselves must call the method before any
|
||
|
lookup and thus also any statement or expression modification. */
|
||
|
void sort_replacements ();
|
||
|
/* Lookup a replacement for a given offset within a given parameter. */
|
||
|
tree lookup_replacement (tree base, unsigned unit_offset);
|
||
|
/* Lookup a replacement for an expression, if there is one. */
|
||
|
ipa_param_body_replacement *get_expr_replacement (tree expr,
|
||
|
bool ignore_default_def);
|
||
|
/* Lookup the new base for surviving names previously belonging to a
|
||
|
parameter. */
|
||
|
tree get_replacement_ssa_base (tree old_decl);
|
||
|
/* Modify a statement. */
|
||
|
bool modify_gimple_stmt (gimple **stmt, gimple_seq *extra_stmts,
|
||
|
gimple *orig_stmt);
|
||
|
/* Return the new chain of parameters. */
|
||
|
tree get_new_param_chain ();
|
||
|
/* Replace all occurances of SSAs in m_dead_ssa_debug_equiv in t with what
|
||
|
they are mapped to. */
|
||
|
void remap_with_debug_expressions (tree *t);
|
||
|
|
||
|
/* If there are any initialization statements that need to be emitted into
|
||
|
the basic block BB right at ther start of the new function, do so. */
|
||
|
void append_init_stmts (basic_block bb);
|
||
|
|
||
|
/* Pointers to data structures defining how the function should be
|
||
|
modified. */
|
||
|
vec<ipa_adjusted_param, va_gc> *m_adj_params;
|
||
|
ipa_param_adjustments *m_adjustments;
|
||
|
|
||
|
/* Vector of old parameter declarations that must have their debug bind
|
||
|
statements re-mapped and debug decls created. */
|
||
|
|
||
|
auto_vec<tree, 16> m_reset_debug_decls;
|
||
|
|
||
|
/* Sets of statements and SSA_NAMEs that only manipulate data from parameters
|
||
|
removed because they are not necessary. */
|
||
|
hash_set<gimple *> m_dead_stmts;
|
||
|
hash_set<tree> m_dead_ssas;
|
||
|
|
||
|
/* Mapping from DCEd SSAs to what their potential debug_binds should be. */
|
||
|
hash_map<tree, tree> m_dead_ssa_debug_equiv;
|
||
|
/* Mapping from DCEd statements to debug expressions that will be placed on
|
||
|
the RHS of debug statement that will replace this one. */
|
||
|
hash_map<gimple *, tree> m_dead_stmt_debug_equiv;
|
||
|
|
||
|
private:
|
||
|
void common_initialization (tree old_fndecl, tree *vars,
|
||
|
vec<ipa_replace_map *, va_gc> *tree_map);
|
||
|
tree carry_over_param (tree t);
|
||
|
unsigned get_base_index (ipa_adjusted_param *apm);
|
||
|
ipa_param_body_replacement *lookup_replacement_1 (tree base,
|
||
|
unsigned unit_offset);
|
||
|
ipa_param_body_replacement *lookup_first_base_replacement (tree base);
|
||
|
tree replace_removed_params_ssa_names (tree old_name, gimple *stmt);
|
||
|
bool modify_expression (tree *expr_p, bool convert);
|
||
|
bool modify_assignment (gimple *stmt, gimple_seq *extra_stmts);
|
||
|
bool modify_call_stmt (gcall **stmt_p, gimple *orig_stmt);
|
||
|
bool modify_cfun_body ();
|
||
|
void reset_debug_stmts ();
|
||
|
void mark_dead_statements (tree dead_param, vec<tree> *debugstack);
|
||
|
bool prepare_debug_expressions (tree dead_ssa);
|
||
|
|
||
|
/* Declaration of the function that is being transformed. */
|
||
|
|
||
|
tree m_fndecl;
|
||
|
|
||
|
/* If non-NULL, the tree-inline master data structure guiding materialization
|
||
|
of the current clone. */
|
||
|
struct copy_body_data *m_id;
|
||
|
|
||
|
/* Vector of old parameter declarations (before changing them). */
|
||
|
|
||
|
auto_vec<tree, 16> m_oparms;
|
||
|
|
||
|
/* Vector of parameter declarations the function will have after
|
||
|
transformation. */
|
||
|
|
||
|
auto_vec<tree, 16> m_new_decls;
|
||
|
|
||
|
/* If the function type has non-NULL TYPE_ARG_TYPES, this is the vector of
|
||
|
these types after transformation, otherwise an empty one. */
|
||
|
|
||
|
auto_vec<tree, 16> m_new_types;
|
||
|
|
||
|
/* Vector of structures telling how to replace old parameters in the
|
||
|
function body. TODO: Even though there usually be only few, but should we
|
||
|
use a hash? */
|
||
|
|
||
|
auto_vec<ipa_param_body_replacement, 16> m_replacements;
|
||
|
|
||
|
/* List of initialization assignments to be put at the beginning of the
|
||
|
cloned function to deal with split aggregates which however have known
|
||
|
constant value and so their PARM_DECL disappears. */
|
||
|
|
||
|
auto_vec<gimple *, 8> m_split_agg_csts_inits;
|
||
|
|
||
|
/* Vector for remapping SSA_BASES from old parameter declarations that are
|
||
|
being removed as a part of the transformation. Before a new VAR_DECL is
|
||
|
created, it holds the old PARM_DECL, once the variable is built it is
|
||
|
stored here. */
|
||
|
|
||
|
auto_vec<tree> m_removed_decls;
|
||
|
|
||
|
/* Hash to quickly lookup the item in m_removed_decls given the old decl. */
|
||
|
|
||
|
hash_map<tree, unsigned> m_removed_map;
|
||
|
|
||
|
/* True iff the transformed function is a class method that is about to loose
|
||
|
its this pointer and must be converted to a normal function. */
|
||
|
|
||
|
bool m_method2func;
|
||
|
|
||
|
/* True if m_replacements have ben sorted since the last insertion. */
|
||
|
|
||
|
bool m_sorted_replacements_p;
|
||
|
};
|
||
|
|
||
|
void push_function_arg_decls (vec<tree> *args, tree fndecl);
|
||
|
void push_function_arg_types (vec<tree> *types, tree fntype);
|
||
|
void ipa_verify_edge_has_no_modifications (cgraph_edge *cs);
|
||
|
void ipa_edge_modifications_finalize ();
|
||
|
|
||
|
|
||
|
#endif /* IPA_PARAM_MANIPULATION_H */
|