Index: gcc/gcc/ipa-prop.c |
diff --git a/gcc/gcc/ipa-prop.c b/gcc/gcc/ipa-prop.c |
index c8db7d2bcd6b7b1267649f2e843ceca328932610..e6a829a3c80db0cfa17468ef0419edd50ba0fc41 100644 |
--- a/gcc/gcc/ipa-prop.c |
+++ b/gcc/gcc/ipa-prop.c |
@@ -1,5 +1,6 @@ |
/* Interprocedural analyses. |
- Copyright (C) 2005, 2007, 2008 Free Software Foundation, Inc. |
+ Copyright (C) 2005, 2007, 2008, 2009, 2010 |
+ Free Software Foundation, Inc. |
This file is part of GCC. |
@@ -33,11 +34,12 @@ along with GCC; see the file COPYING3. If not see |
#include "timevar.h" |
#include "flags.h" |
#include "diagnostic.h" |
+#include "lto-streamer.h" |
/* Vector where the parameter infos are actually stored. */ |
VEC (ipa_node_params_t, heap) *ipa_node_params_vector; |
/* Vector where the parameter infos are actually stored. */ |
-VEC (ipa_edge_args_t, heap) *ipa_edge_args_vector; |
+VEC (ipa_edge_args_t, gc) *ipa_edge_args_vector; |
/* Holders of ipa cgraph hooks: */ |
static struct cgraph_edge_hook_list *edge_removal_hook_holder; |
@@ -45,6 +47,24 @@ static struct cgraph_node_hook_list *node_removal_hook_holder; |
static struct cgraph_2edge_hook_list *edge_duplication_hook_holder; |
static struct cgraph_2node_hook_list *node_duplication_hook_holder; |
+/* Add cgraph NODE described by INFO to the worklist WL regardless of whether |
+ it is in one or not. It should almost never be used directly, as opposed to |
+ ipa_push_func_to_list. */ |
+ |
+void |
+ipa_push_func_to_list_1 (struct ipa_func_list **wl, |
+ struct cgraph_node *node, |
+ struct ipa_node_params *info) |
+{ |
+ struct ipa_func_list *temp; |
+ |
+ info->node_enqueued = 1; |
+ temp = XCNEW (struct ipa_func_list); |
+ temp->node = node; |
+ temp->next = *wl; |
+ *wl = temp; |
+} |
+ |
/* Initialize worklist to contain all functions. */ |
struct ipa_func_list * |
@@ -57,43 +77,33 @@ ipa_init_func_list (void) |
for (node = cgraph_nodes; node; node = node->next) |
if (node->analyzed) |
{ |
+ struct ipa_node_params *info = IPA_NODE_REF (node); |
/* Unreachable nodes should have been eliminated before ipcp and |
inlining. */ |
gcc_assert (node->needed || node->reachable); |
- ipa_push_func_to_list (&wl, node); |
+ ipa_push_func_to_list_1 (&wl, node, info); |
} |
return wl; |
} |
-/* Add cgraph node MT to the worklist. Set worklist element WL |
- to point to MT. */ |
- |
-void |
-ipa_push_func_to_list (struct ipa_func_list **wl, struct cgraph_node *mt) |
-{ |
- struct ipa_func_list *temp; |
- |
- temp = XCNEW (struct ipa_func_list); |
- temp->node = mt; |
- temp->next = *wl; |
- *wl = temp; |
-} |
- |
-/* Remove a function from the worklist. WL points to the first |
- element in the list, which is removed. */ |
+/* Remove a function from the worklist WL and return it. */ |
struct cgraph_node * |
-ipa_pop_func_from_list (struct ipa_func_list ** wl) |
+ipa_pop_func_from_list (struct ipa_func_list **wl) |
{ |
+ struct ipa_node_params *info; |
struct ipa_func_list *first; |
- struct cgraph_node *return_func; |
+ struct cgraph_node *node; |
first = *wl; |
*wl = (*wl)->next; |
- return_func = first->node; |
+ node = first->node; |
free (first); |
- return return_func; |
+ |
+ info = IPA_NODE_REF (node); |
+ info->node_enqueued = 0; |
+ return node; |
} |
/* Return index of the formal whose tree is PTREE in function which corresponds |
@@ -134,6 +144,20 @@ ipa_populate_param_decls (struct cgraph_node *node, |
} |
} |
+/* Return how many formal parameters FNDECL has. */ |
+ |
+static inline int |
+count_formal_params_1 (tree fndecl) |
+{ |
+ tree parm; |
+ int count = 0; |
+ |
+ for (parm = DECL_ARGUMENTS (fndecl); parm; parm = TREE_CHAIN (parm)) |
+ count++; |
+ |
+ return count; |
+} |
+ |
/* Count number of formal parameters in NOTE. Store the result to the |
appropriate field of INFO. */ |
@@ -141,16 +165,9 @@ static void |
ipa_count_formal_params (struct cgraph_node *node, |
struct ipa_node_params *info) |
{ |
- tree fndecl; |
- tree fnargs; |
- tree parm; |
int param_num; |
- fndecl = node->decl; |
- fnargs = DECL_ARGUMENTS (fndecl); |
- param_num = 0; |
- for (parm = fnargs; parm; parm = TREE_CHAIN (parm)) |
- param_num++; |
+ param_num = count_formal_params_1 (node->decl); |
ipa_set_param_count (info, param_num); |
} |
@@ -172,48 +189,30 @@ ipa_initialize_node_params (struct cgraph_node *node) |
} |
} |
-/* Check STMT to detect whether a formal parameter is directly modified within |
- STMT, the appropriate entry is updated in the modified flags of INFO. |
- Directly means that this function does not check for modifications through |
- pointers or escaping addresses because all TREE_ADDRESSABLE parameters are |
- considered modified anyway. */ |
+/* Callback of walk_stmt_load_store_addr_ops for the visit_store and visit_addr |
+ parameters. If OP is a parameter declaration, mark it as modified in the |
+ info structure passed in DATA. */ |
-static void |
-ipa_check_stmt_modifications (struct ipa_node_params *info, gimple stmt) |
+static bool |
+visit_store_addr_for_mod_analysis (gimple stmt ATTRIBUTE_UNUSED, |
+ tree op, void *data) |
{ |
- int j; |
- int index; |
- tree lhs; |
+ struct ipa_node_params *info = (struct ipa_node_params *) data; |
- switch (gimple_code (stmt)) |
+ if (TREE_CODE (op) == PARM_DECL) |
{ |
- case GIMPLE_ASSIGN: |
- lhs = gimple_assign_lhs (stmt); |
- |
- while (handled_component_p (lhs)) |
- lhs = TREE_OPERAND (lhs, 0); |
- if (TREE_CODE (lhs) == SSA_NAME) |
- lhs = SSA_NAME_VAR (lhs); |
- index = ipa_get_param_decl_index (info, lhs); |
- if (index >= 0) |
- info->params[index].modified = true; |
- break; |
- |
- case GIMPLE_ASM: |
- /* Asm code could modify any of the parameters. */ |
- for (j = 0; j < ipa_get_param_count (info); j++) |
- info->params[j].modified = true; |
- break; |
- |
- default: |
- break; |
+ int index = ipa_get_param_decl_index (info, op); |
+ gcc_assert (index >= 0); |
+ info->params[index].modified = true; |
} |
+ |
+ return false; |
} |
/* Compute which formal parameters of function associated with NODE are locally |
- modified. Parameters may be modified in NODE if they are TREE_ADDRESSABLE, |
- if they appear on the left hand side of an assignment or if there is an |
- ASM_EXPR in the function. */ |
+ modified or their address is taken. Note that this does not apply on |
+ parameters with SSA names but those can and should be analyzed |
+ differently. */ |
void |
ipa_detect_param_modifications (struct cgraph_node *node) |
@@ -222,27 +221,17 @@ ipa_detect_param_modifications (struct cgraph_node *node) |
basic_block bb; |
struct function *func; |
gimple_stmt_iterator gsi; |
- gimple stmt; |
struct ipa_node_params *info = IPA_NODE_REF (node); |
- int i, count; |
if (ipa_get_param_count (info) == 0 || info->modification_analysis_done) |
return; |
func = DECL_STRUCT_FUNCTION (decl); |
FOR_EACH_BB_FN (bb, func) |
- { |
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
- { |
- stmt = gsi_stmt (gsi); |
- ipa_check_stmt_modifications (info, stmt); |
- } |
- } |
- |
- count = ipa_get_param_count (info); |
- for (i = 0; i < count; i++) |
- if (TREE_ADDRESSABLE (ipa_get_param (info, i))) |
- info->params[i].modified = true; |
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) |
+ walk_stmt_load_store_addr_ops (gsi_stmt (gsi), info, NULL, |
+ visit_store_addr_for_mod_analysis, |
+ visit_store_addr_for_mod_analysis); |
info->modification_analysis_done = 1; |
} |
@@ -261,7 +250,7 @@ ipa_count_arguments (struct cgraph_edge *cs) |
arg_num = gimple_call_num_args (stmt); |
if (VEC_length (ipa_edge_args_t, ipa_edge_args_vector) |
<= (unsigned) cgraph_edge_max_uid) |
- VEC_safe_grow_cleared (ipa_edge_args_t, heap, |
+ VEC_safe_grow_cleared (ipa_edge_args_t, gc, |
ipa_edge_args_vector, cgraph_edge_max_uid + 1); |
ipa_set_cs_argument_count (IPA_EDGE_REF (cs), arg_num); |
} |
@@ -293,16 +282,16 @@ ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node) |
type = jump_func->type; |
fprintf (f, " param %d: ", i); |
- if (type == IPA_UNKNOWN) |
+ if (type == IPA_JF_UNKNOWN) |
fprintf (f, "UNKNOWN\n"); |
- else if (type == IPA_CONST) |
+ else if (type == IPA_JF_CONST) |
{ |
tree val = jump_func->value.constant; |
fprintf (f, "CONST: "); |
print_generic_expr (f, val, 0); |
fprintf (f, "\n"); |
} |
- else if (type == IPA_CONST_MEMBER_PTR) |
+ else if (type == IPA_JF_CONST_MEMBER_PTR) |
{ |
fprintf (f, "CONST MEMBER PTR: "); |
print_generic_expr (f, jump_func->value.member_cst.pfn, 0); |
@@ -310,11 +299,25 @@ ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node) |
print_generic_expr (f, jump_func->value.member_cst.delta, 0); |
fprintf (f, "\n"); |
} |
- else if (type == IPA_PASS_THROUGH) |
+ else if (type == IPA_JF_PASS_THROUGH) |
{ |
fprintf (f, "PASS THROUGH: "); |
- fprintf (f, "%d\n", jump_func->value.formal_id); |
+ fprintf (f, "%d, op %s ", |
+ jump_func->value.pass_through.formal_id, |
+ tree_code_name[(int) |
+ jump_func->value.pass_through.operation]); |
+ if (jump_func->value.pass_through.operation != NOP_EXPR) |
+ print_generic_expr (dump_file, |
+ jump_func->value.pass_through.operand, 0); |
+ fprintf (dump_file, "\n"); |
} |
+ else if (type == IPA_JF_ANCESTOR) |
+ { |
+ fprintf (f, "ANCESTOR: "); |
+ fprintf (f, "%d, offset "HOST_WIDE_INT_PRINT_DEC"\n", |
+ jump_func->value.ancestor.formal_id, |
+ jump_func->value.ancestor.offset); |
+ } |
} |
} |
} |
@@ -333,6 +336,73 @@ ipa_print_all_jump_functions (FILE *f) |
} |
} |
+/* Determine whether passing ssa name NAME constitutes a polynomial |
+ pass-through function or getting an address of an acestor and if so, write |
+ such a jump function to JFUNC. INFO describes the caller. */ |
+ |
+static void |
+compute_complex_pass_through (struct ipa_node_params *info, |
+ struct ipa_jump_func *jfunc, |
+ tree name) |
+{ |
+ HOST_WIDE_INT offset, size, max_size; |
+ tree op1, op2, type; |
+ int index; |
+ gimple stmt = SSA_NAME_DEF_STMT (name); |
+ |
+ if (!is_gimple_assign (stmt)) |
+ return; |
+ op1 = gimple_assign_rhs1 (stmt); |
+ op2 = gimple_assign_rhs2 (stmt); |
+ |
+ if (op2) |
+ { |
+ if (TREE_CODE (op1) != SSA_NAME |
+ || !SSA_NAME_IS_DEFAULT_DEF (op1) |
+ || (TREE_CODE_CLASS (gimple_expr_code (stmt)) != tcc_comparison |
+ && !useless_type_conversion_p (TREE_TYPE (name), |
+ TREE_TYPE (op1))) |
+ || !is_gimple_ip_invariant (op2)) |
+ return; |
+ |
+ index = ipa_get_param_decl_index (info, SSA_NAME_VAR (op1)); |
+ if (index >= 0) |
+ { |
+ jfunc->type = IPA_JF_PASS_THROUGH; |
+ jfunc->value.pass_through.formal_id = index; |
+ jfunc->value.pass_through.operation = gimple_assign_rhs_code (stmt); |
+ jfunc->value.pass_through.operand = op2; |
+ } |
+ return; |
+ } |
+ |
+ if (TREE_CODE (op1) != ADDR_EXPR) |
+ return; |
+ op1 = TREE_OPERAND (op1, 0); |
+ type = TREE_TYPE (op1); |
+ |
+ op1 = get_ref_base_and_extent (op1, &offset, &size, &max_size); |
+ if (TREE_CODE (op1) != INDIRECT_REF |
+ /* If this is a varying address, punt. */ |
+ || max_size == -1 |
+ || max_size != size) |
+ return; |
+ op1 = TREE_OPERAND (op1, 0); |
+ if (TREE_CODE (op1) != SSA_NAME |
+ || !SSA_NAME_IS_DEFAULT_DEF (op1)) |
+ return; |
+ |
+ index = ipa_get_param_decl_index (info, SSA_NAME_VAR (op1)); |
+ if (index >= 0) |
+ { |
+ jfunc->type = IPA_JF_ANCESTOR; |
+ jfunc->value.ancestor.formal_id = index; |
+ jfunc->value.ancestor.offset = offset; |
+ jfunc->value.ancestor.type = type; |
+ } |
+} |
+ |
+ |
/* Determine the jump functions of scalar arguments. Scalar means SSA names |
and constants of a number of selected types. INFO is the ipa_node_params |
structure associated with the caller, FUNCTIONS is a pointer to an array of |
@@ -353,18 +423,24 @@ compute_scalar_jump_functions (struct ipa_node_params *info, |
if (is_gimple_ip_invariant (arg)) |
{ |
- functions[num].type = IPA_CONST; |
+ functions[num].type = IPA_JF_CONST; |
functions[num].value.constant = arg; |
} |
- else if ((TREE_CODE (arg) == SSA_NAME) && SSA_NAME_IS_DEFAULT_DEF (arg)) |
+ else if (TREE_CODE (arg) == SSA_NAME) |
{ |
- int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg)); |
- |
- if (index >= 0) |
+ if (SSA_NAME_IS_DEFAULT_DEF (arg)) |
{ |
- functions[num].type = IPA_PASS_THROUGH; |
- functions[num].value.formal_id = index; |
+ int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg)); |
+ |
+ if (index >= 0) |
+ { |
+ functions[num].type = IPA_JF_PASS_THROUGH; |
+ functions[num].value.pass_through.formal_id = index; |
+ functions[num].value.pass_through.operation = NOP_EXPR; |
+ } |
} |
+ else |
+ compute_complex_pass_through (info, &functions[num], arg); |
} |
} |
} |
@@ -430,8 +506,9 @@ compute_pass_through_member_ptrs (struct ipa_node_params *info, |
gcc_assert (index >=0); |
if (!ipa_is_param_modified (info, index)) |
{ |
- functions[num].type = IPA_PASS_THROUGH; |
- functions[num].value.formal_id = index; |
+ functions[num].type = IPA_JF_PASS_THROUGH; |
+ functions[num].value.pass_through.formal_id = index; |
+ functions[num].value.pass_through.operation = NOP_EXPR; |
} |
else |
undecided_members = true; |
@@ -451,11 +528,29 @@ static void |
fill_member_ptr_cst_jump_function (struct ipa_jump_func *jfunc, |
tree pfn, tree delta) |
{ |
- jfunc->type = IPA_CONST_MEMBER_PTR; |
+ jfunc->type = IPA_JF_CONST_MEMBER_PTR; |
jfunc->value.member_cst.pfn = pfn; |
jfunc->value.member_cst.delta = delta; |
} |
+/* If RHS is an SSA_NAMe and it is defined by a simple copy assign statement, |
+ return the rhs of its defining statement. */ |
+ |
+static inline tree |
+get_ssa_def_if_simple_copy (tree rhs) |
+{ |
+ while (TREE_CODE (rhs) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (rhs)) |
+ { |
+ gimple def_stmt = SSA_NAME_DEF_STMT (rhs); |
+ |
+ if (gimple_assign_single_p (def_stmt)) |
+ rhs = gimple_assign_rhs1 (def_stmt); |
+ else |
+ break; |
+ } |
+ return rhs; |
+} |
+ |
/* Traverse statements from CALL backwards, scanning whether the argument ARG |
which is a member pointer is filled in with constant values. If it is, fill |
the jump function JFUNC in appropriately. METHOD_FIELD and DELTA_FIELD are |
@@ -482,7 +577,7 @@ determine_cst_member_ptr (gimple call, tree arg, tree method_field, |
gimple stmt = gsi_stmt (gsi); |
tree lhs, rhs, fld; |
- if (!is_gimple_assign (stmt) || gimple_num_ops (stmt) != 2) |
+ if (!gimple_assign_single_p (stmt)) |
return; |
lhs = gimple_assign_lhs (stmt); |
@@ -495,6 +590,7 @@ determine_cst_member_ptr (gimple call, tree arg, tree method_field, |
fld = TREE_OPERAND (lhs, 1); |
if (!method && fld == method_field) |
{ |
+ rhs = get_ssa_def_if_simple_copy (rhs); |
if (TREE_CODE (rhs) == ADDR_EXPR |
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == FUNCTION_DECL |
&& TREE_CODE (TREE_TYPE (TREE_OPERAND (rhs, 0))) == METHOD_TYPE) |
@@ -512,6 +608,7 @@ determine_cst_member_ptr (gimple call, tree arg, tree method_field, |
if (!delta && fld == delta_field) |
{ |
+ rhs = get_ssa_def_if_simple_copy (rhs); |
if (TREE_CODE (rhs) == INTEGER_CST) |
{ |
delta = rhs; |
@@ -545,7 +642,7 @@ compute_cst_member_ptr_arguments (struct ipa_jump_func *functions, |
{ |
arg = gimple_call_arg (call, num); |
- if (functions[num].type == IPA_UNKNOWN |
+ if (functions[num].type == IPA_JF_UNKNOWN |
&& type_like_member_ptr_p (TREE_TYPE (arg), &method_field, |
&delta_field)) |
determine_cst_member_ptr (call, arg, method_field, delta_field, |
@@ -566,8 +663,8 @@ ipa_compute_jump_functions (struct cgraph_edge *cs) |
if (ipa_get_cs_argument_count (arguments) == 0 || arguments->jump_functions) |
return; |
- arguments->jump_functions = XCNEWVEC (struct ipa_jump_func, |
- ipa_get_cs_argument_count (arguments)); |
+ arguments->jump_functions = GGC_CNEWVEC (struct ipa_jump_func, |
+ ipa_get_cs_argument_count (arguments)); |
call = cs->call_stmt; |
gcc_assert (is_gimple_call (call)); |
@@ -585,25 +682,28 @@ ipa_compute_jump_functions (struct cgraph_edge *cs) |
compute_cst_member_ptr_arguments (arguments->jump_functions, call); |
} |
-/* If RHS looks like a rhs of a statement loading pfn from a member pointer |
- formal parameter, return the parameter, otherwise return NULL. */ |
+/* If RHS looks like a rhs of a statement loading pfn from a member |
+ pointer formal parameter, return the parameter, otherwise return |
+ NULL. If USE_DELTA, then we look for a use of the delta field |
+ rather than the pfn. */ |
static tree |
-ipa_get_member_ptr_load_param (tree rhs) |
+ipa_get_member_ptr_load_param (tree rhs, bool use_delta) |
{ |
tree rec, fld; |
tree ptr_field; |
+ tree delta_field; |
if (TREE_CODE (rhs) != COMPONENT_REF) |
return NULL_TREE; |
rec = TREE_OPERAND (rhs, 0); |
if (TREE_CODE (rec) != PARM_DECL |
- || !type_like_member_ptr_p (TREE_TYPE (rec), &ptr_field, NULL)) |
+ || !type_like_member_ptr_p (TREE_TYPE (rec), &ptr_field, &delta_field)) |
return NULL_TREE; |
fld = TREE_OPERAND (rhs, 1); |
- if (fld == ptr_field) |
+ if (use_delta ? (fld == delta_field) : (fld == ptr_field)) |
return rec; |
else |
return NULL_TREE; |
@@ -613,15 +713,15 @@ ipa_get_member_ptr_load_param (tree rhs) |
parameter, this function returns that parameter. */ |
static tree |
-ipa_get_stmt_member_ptr_load_param (gimple stmt) |
+ipa_get_stmt_member_ptr_load_param (gimple stmt, bool use_delta) |
{ |
tree rhs; |
- if (!is_gimple_assign (stmt) || gimple_num_ops (stmt) != 2) |
+ if (!gimple_assign_single_p (stmt)) |
return NULL_TREE; |
rhs = gimple_assign_rhs1 (stmt); |
- return ipa_get_member_ptr_load_param (rhs); |
+ return ipa_get_member_ptr_load_param (rhs, use_delta); |
} |
/* Returns true iff T is an SSA_NAME defined by a statement. */ |
@@ -647,13 +747,13 @@ ipa_note_param_call (struct ipa_node_params *info, int formal_id, |
struct ipa_param_call_note *note; |
basic_block bb = gimple_bb (stmt); |
- info->params[formal_id].called = 1; |
- |
note = XCNEW (struct ipa_param_call_note); |
note->formal_id = formal_id; |
note->stmt = stmt; |
+ note->lto_stmt_uid = gimple_uid (stmt); |
note->count = bb->count; |
- note->frequency = compute_call_stmt_bb_frequency (bb); |
+ note->frequency = compute_call_stmt_bb_frequency (current_function_decl, bb); |
+ note->loop_nest = bb->loop_depth; |
note->next = info->param_calls; |
info->param_calls = note; |
@@ -756,15 +856,15 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call) |
d1 = SSA_NAME_DEF_STMT (n1); |
d2 = SSA_NAME_DEF_STMT (n2); |
- if ((rec = ipa_get_stmt_member_ptr_load_param (d1))) |
+ if ((rec = ipa_get_stmt_member_ptr_load_param (d1, false))) |
{ |
- if (ipa_get_stmt_member_ptr_load_param (d2)) |
+ if (ipa_get_stmt_member_ptr_load_param (d2, false)) |
return; |
bb = gimple_bb (d1); |
virt_bb = gimple_bb (d2); |
} |
- else if ((rec = ipa_get_stmt_member_ptr_load_param (d2))) |
+ else if ((rec = ipa_get_stmt_member_ptr_load_param (d2, false))) |
{ |
bb = gimple_bb (d2); |
virt_bb = gimple_bb (d1); |
@@ -797,7 +897,7 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call) |
return; |
def = SSA_NAME_DEF_STMT (cond); |
- if (!is_gimple_assign (def) || gimple_num_ops (def) != 3 |
+ if (!is_gimple_assign (def) |
|| gimple_assign_rhs_code (def) != BIT_AND_EXPR |
|| !integer_onep (gimple_assign_rhs2 (def))) |
return; |
@@ -808,8 +908,8 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call) |
def = SSA_NAME_DEF_STMT (cond); |
- if (is_gimple_assign (def) && gimple_num_ops (def) == 2 |
- && gimple_assign_rhs_code (def) == NOP_EXPR) |
+ if (is_gimple_assign (def) |
+ && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def))) |
{ |
cond = gimple_assign_rhs1 (def); |
if (!ipa_is_ssa_with_stmt_def (cond)) |
@@ -817,7 +917,10 @@ ipa_analyze_call_uses (struct ipa_node_params *info, gimple call) |
def = SSA_NAME_DEF_STMT (cond); |
} |
- rec2 = ipa_get_stmt_member_ptr_load_param (def); |
+ rec2 = ipa_get_stmt_member_ptr_load_param (def, |
+ (TARGET_PTRMEMFUNC_VBIT_LOCATION |
+ == ptrmemfunc_vbit_in_delta)); |
+ |
if (rec != rec2) |
return; |
@@ -870,7 +973,10 @@ ipa_analyze_params_uses (struct cgraph_node *node) |
/* Update the jump functions associated with call graph edge E when the call |
graph edge CS is being inlined, assuming that E->caller is already (possibly |
- indirectly) inlined into CS->callee and that E has not been inlined. */ |
+ indirectly) inlined into CS->callee and that E has not been inlined. |
+ |
+ We keep pass through functions only if they do not contain any operation. |
+ This is sufficient for inlining and greately simplifies things. */ |
static void |
update_jump_functions_after_inlining (struct cgraph_edge *cs, |
@@ -885,17 +991,26 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs, |
{ |
struct ipa_jump_func *src, *dst = ipa_get_ith_jump_func (args, i); |
- if (dst->type != IPA_PASS_THROUGH) |
+ if (dst->type == IPA_JF_ANCESTOR) |
+ { |
+ dst->type = IPA_JF_UNKNOWN; |
+ continue; |
+ } |
+ |
+ if (dst->type != IPA_JF_PASS_THROUGH) |
continue; |
- /* We must check range due to calls with variable number of arguments: */ |
- if (dst->value.formal_id >= (unsigned) ipa_get_cs_argument_count (top)) |
+ /* We must check range due to calls with variable number of arguments and |
+ we cannot combine jump functions with operations. */ |
+ if (dst->value.pass_through.operation != NOP_EXPR |
+ || (dst->value.pass_through.formal_id |
+ >= ipa_get_cs_argument_count (top))) |
{ |
- dst->type = IPA_BOTTOM; |
+ dst->type = IPA_JF_UNKNOWN; |
continue; |
} |
- src = ipa_get_ith_jump_func (top, dst->value.formal_id); |
+ src = ipa_get_ith_jump_func (top, dst->value.pass_through.formal_id); |
*dst = *src; |
} |
} |
@@ -910,7 +1025,7 @@ print_edge_addition_message (FILE *f, struct ipa_param_call_note *nt, |
struct cgraph_node *node) |
{ |
fprintf (f, "ipa-prop: Discovered an indirect call to a known target ("); |
- if (jfunc->type == IPA_CONST_MEMBER_PTR) |
+ if (jfunc->type == IPA_JF_CONST_MEMBER_PTR) |
{ |
print_node_brief (f, "", jfunc->value.member_cst.pfn, 0); |
print_node_brief (f, ", ", jfunc->value.member_cst.delta, 0); |
@@ -946,23 +1061,25 @@ update_call_notes_after_inlining (struct cgraph_edge *cs, |
continue; |
/* We must check range due to calls with variable number of arguments: */ |
- if (nt->formal_id >= (unsigned) ipa_get_cs_argument_count (top)) |
+ if (nt->formal_id >= ipa_get_cs_argument_count (top)) |
{ |
nt->processed = true; |
continue; |
} |
jfunc = ipa_get_ith_jump_func (top, nt->formal_id); |
- if (jfunc->type == IPA_PASS_THROUGH) |
- nt->formal_id = jfunc->value.formal_id; |
- else if (jfunc->type == IPA_CONST || jfunc->type == IPA_CONST_MEMBER_PTR) |
+ if (jfunc->type == IPA_JF_PASS_THROUGH |
+ && jfunc->value.pass_through.operation == NOP_EXPR) |
+ nt->formal_id = jfunc->value.pass_through.formal_id; |
+ else if (jfunc->type == IPA_JF_CONST |
+ || jfunc->type == IPA_JF_CONST_MEMBER_PTR) |
{ |
struct cgraph_node *callee; |
struct cgraph_edge *new_indirect_edge; |
tree decl; |
nt->processed = true; |
- if (jfunc->type == IPA_CONST_MEMBER_PTR) |
+ if (jfunc->type == IPA_JF_CONST_MEMBER_PTR) |
decl = jfunc->value.member_cst.pfn; |
else |
decl = jfunc->value.constant; |
@@ -984,12 +1101,20 @@ update_call_notes_after_inlining (struct cgraph_edge *cs, |
new_indirect_edge = cgraph_create_edge (node, callee, nt->stmt, |
nt->count, nt->frequency, |
nt->loop_nest); |
+ new_indirect_edge->lto_stmt_uid = nt->lto_stmt_uid; |
new_indirect_edge->indirect_call = 1; |
ipa_check_create_edge_args (); |
if (new_edges) |
VEC_safe_push (cgraph_edge_p, heap, *new_edges, new_indirect_edge); |
top = IPA_EDGE_REF (cs); |
} |
+ else |
+ { |
+ /* Ancestor jum functions and pass theoughs with operations should |
+ not be used on parameters that then get called. */ |
+ gcc_assert (jfunc->type == IPA_JF_UNKNOWN); |
+ nt->processed = true; |
+ } |
} |
return res; |
} |
@@ -1031,6 +1156,10 @@ bool |
ipa_propagate_indirect_call_infos (struct cgraph_edge *cs, |
VEC (cgraph_edge_p, heap) **new_edges) |
{ |
+ /* FIXME lto: We do not stream out indirect call information. */ |
+ if (flag_wpa) |
+ return false; |
+ |
/* Do nothing if the preparation phase has not been carried out yet |
(i.e. during early inlining). */ |
if (!ipa_node_params_vector) |
@@ -1047,7 +1176,7 @@ void |
ipa_free_edge_args_substructures (struct ipa_edge_args *args) |
{ |
if (args->jump_functions) |
- free (args->jump_functions); |
+ ggc_free (args->jump_functions); |
memset (args, 0, sizeof (*args)); |
} |
@@ -1065,7 +1194,7 @@ ipa_free_all_edge_args (void) |
i++) |
ipa_free_edge_args_substructures (args); |
- VEC_free (ipa_edge_args_t, heap, ipa_edge_args_vector); |
+ VEC_free (ipa_edge_args_t, gc, ipa_edge_args_vector); |
ipa_edge_args_vector = NULL; |
} |
@@ -1136,7 +1265,22 @@ duplicate_array (void *src, size_t n) |
if (!src) |
return NULL; |
- p = xcalloc (1, n); |
+ p = xmalloc (n); |
+ memcpy (p, src, n); |
+ return p; |
+} |
+ |
+/* Like duplicate_array byt in GGC memory. */ |
+ |
+static void * |
+duplicate_ggc_array (void *src, size_t n) |
+{ |
+ void *p; |
+ |
+ if (!src) |
+ return NULL; |
+ |
+ p = ggc_alloc (n); |
memcpy (p, src, n); |
return p; |
} |
@@ -1158,8 +1302,8 @@ ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst, |
arg_count = ipa_get_cs_argument_count (old_args); |
ipa_set_cs_argument_count (new_args, arg_count); |
new_args->jump_functions = (struct ipa_jump_func *) |
- duplicate_array (old_args->jump_functions, |
- sizeof (struct ipa_jump_func) * arg_count); |
+ duplicate_ggc_array (old_args->jump_functions, |
+ sizeof (struct ipa_jump_func) * arg_count); |
} |
/* Hook that is called by cgraph.c when a node is duplicated. */ |
@@ -1275,11 +1419,11 @@ ipa_print_node_params (FILE * f, struct cgraph_node *node) |
temp = ipa_get_param (info, i); |
if (TREE_CODE (temp) == PARM_DECL) |
fprintf (f, " param %d : %s", i, |
- (*lang_hooks.decl_printable_name) (temp, 2)); |
+ (DECL_NAME (temp) |
+ ? (*lang_hooks.decl_printable_name) (temp, 2) |
+ : "(unnamed)")); |
if (ipa_is_param_modified (info, i)) |
fprintf (f, " modified"); |
- if (ipa_is_param_called (info, i)) |
- fprintf (f, " called"); |
fprintf (f, "\n"); |
} |
} |
@@ -1296,3 +1440,804 @@ ipa_print_all_params (FILE * f) |
for (node = cgraph_nodes; node; node = node->next) |
ipa_print_node_params (f, node); |
} |
+ |
+/* Return a heap allocated vector containing formal parameters of FNDECL. */ |
+ |
+VEC(tree, heap) * |
+ipa_get_vector_of_formal_parms (tree fndecl) |
+{ |
+ VEC(tree, heap) *args; |
+ int count; |
+ tree parm; |
+ |
+ count = count_formal_params_1 (fndecl); |
+ args = VEC_alloc (tree, heap, count); |
+ for (parm = DECL_ARGUMENTS (fndecl); parm; parm = TREE_CHAIN (parm)) |
+ VEC_quick_push (tree, args, parm); |
+ |
+ return args; |
+} |
+ |
+/* Return a heap allocated vector containing types of formal parameters of |
+ function type FNTYPE. */ |
+ |
+static inline VEC(tree, heap) * |
+get_vector_of_formal_parm_types (tree fntype) |
+{ |
+ VEC(tree, heap) *types; |
+ int count = 0; |
+ tree t; |
+ |
+ for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t)) |
+ count++; |
+ |
+ types = VEC_alloc (tree, heap, count); |
+ for (t = TYPE_ARG_TYPES (fntype); t; t = TREE_CHAIN (t)) |
+ VEC_quick_push (tree, types, 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, |
+ const char *synth_parm_prefix) |
+{ |
+ VEC(tree, heap) *oparms, *otypes; |
+ tree orig_type, new_type = NULL; |
+ tree old_arg_types, t, new_arg_types = NULL; |
+ tree parm, *link = &DECL_ARGUMENTS (fndecl); |
+ int i, len = VEC_length (ipa_parm_adjustment_t, adjustments); |
+ tree new_reversed = NULL; |
+ bool care_for_types, last_parm_void; |
+ |
+ if (!synth_parm_prefix) |
+ synth_parm_prefix = "SYNTH"; |
+ |
+ oparms = ipa_get_vector_of_formal_parms (fndecl); |
+ orig_type = TREE_TYPE (fndecl); |
+ 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... */ |
+ care_for_types = (old_arg_types != NULL_TREE); |
+ if (care_for_types) |
+ { |
+ last_parm_void = (TREE_VALUE (tree_last (old_arg_types)) |
+ == void_type_node); |
+ otypes = get_vector_of_formal_parm_types (orig_type); |
+ if (last_parm_void) |
+ gcc_assert (VEC_length (tree, oparms) + 1 == VEC_length (tree, otypes)); |
+ else |
+ gcc_assert (VEC_length (tree, oparms) == VEC_length (tree, otypes)); |
+ } |
+ else |
+ { |
+ last_parm_void = false; |
+ otypes = NULL; |
+ } |
+ |
+ for (i = 0; i < len; i++) |
+ { |
+ struct ipa_parm_adjustment *adj; |
+ gcc_assert (link); |
+ |
+ adj = VEC_index (ipa_parm_adjustment_t, adjustments, i); |
+ parm = VEC_index (tree, oparms, adj->base_index); |
+ adj->base = parm; |
+ |
+ if (adj->copy_param) |
+ { |
+ if (care_for_types) |
+ new_arg_types = tree_cons (NULL_TREE, VEC_index (tree, otypes, |
+ adj->base_index), |
+ new_arg_types); |
+ *link = parm; |
+ link = &TREE_CHAIN (parm); |
+ } |
+ else if (!adj->remove_param) |
+ { |
+ tree new_parm; |
+ tree ptype; |
+ |
+ if (adj->by_ref) |
+ ptype = build_pointer_type (adj->type); |
+ else |
+ ptype = adj->type; |
+ |
+ 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); |
+ DECL_NAME (new_parm) = create_tmp_var_name (synth_parm_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); |
+ |
+ add_referenced_var (new_parm); |
+ mark_sym_for_renaming (new_parm); |
+ adj->base = parm; |
+ adj->reduction = new_parm; |
+ |
+ *link = new_parm; |
+ |
+ link = &TREE_CHAIN (new_parm); |
+ } |
+ } |
+ |
+ *link = NULL_TREE; |
+ |
+ 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. */ |
+ if (TREE_CODE (orig_type) != METHOD_TYPE |
+ || (VEC_index (ipa_parm_adjustment_t, adjustments, 0)->copy_param |
+ && VEC_index (ipa_parm_adjustment_t, adjustments, 0)->base_index == 0)) |
+ { |
+ new_type = copy_node (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; |
+ } |
+ |
+ /* This is a new type, not a copy of an old type. Need to reassociate |
+ variants. We can handle everything except the main variant lazily. */ |
+ t = TYPE_MAIN_VARIANT (orig_type); |
+ if (orig_type != t) |
+ { |
+ TYPE_MAIN_VARIANT (new_type) = t; |
+ TYPE_NEXT_VARIANT (new_type) = TYPE_NEXT_VARIANT (t); |
+ TYPE_NEXT_VARIANT (t) = new_type; |
+ } |
+ else |
+ { |
+ TYPE_MAIN_VARIANT (new_type) = new_type; |
+ TYPE_NEXT_VARIANT (new_type) = NULL; |
+ } |
+ |
+ TREE_TYPE (fndecl) = new_type; |
+ if (otypes) |
+ VEC_free (tree, heap, otypes); |
+ VEC_free (tree, heap, oparms); |
+} |
+ |
+/* 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, gimple stmt, |
+ ipa_parm_adjustment_vec adjustments) |
+{ |
+ VEC(tree, heap) *vargs; |
+ gimple new_stmt; |
+ gimple_stmt_iterator gsi; |
+ tree callee_decl; |
+ int i, len; |
+ |
+ len = VEC_length (ipa_parm_adjustment_t, adjustments); |
+ vargs = VEC_alloc (tree, heap, len); |
+ |
+ gsi = gsi_for_stmt (stmt); |
+ for (i = 0; i < len; i++) |
+ { |
+ struct ipa_parm_adjustment *adj; |
+ |
+ adj = VEC_index (ipa_parm_adjustment_t, adjustments, i); |
+ |
+ if (adj->copy_param) |
+ { |
+ tree arg = gimple_call_arg (stmt, adj->base_index); |
+ |
+ VEC_quick_push (tree, vargs, arg); |
+ } |
+ else if (!adj->remove_param) |
+ { |
+ tree expr, orig_expr; |
+ bool allow_ptr, repl_found; |
+ |
+ orig_expr = expr = gimple_call_arg (stmt, adj->base_index); |
+ if (TREE_CODE (expr) == ADDR_EXPR) |
+ { |
+ allow_ptr = false; |
+ expr = TREE_OPERAND (expr, 0); |
+ } |
+ else |
+ allow_ptr = true; |
+ |
+ repl_found = build_ref_for_offset (&expr, TREE_TYPE (expr), |
+ adj->offset, adj->type, |
+ allow_ptr); |
+ if (repl_found) |
+ { |
+ if (adj->by_ref) |
+ expr = build_fold_addr_expr (expr); |
+ } |
+ else |
+ { |
+ tree ptrtype = build_pointer_type (adj->type); |
+ expr = orig_expr; |
+ if (!POINTER_TYPE_P (TREE_TYPE (expr))) |
+ expr = build_fold_addr_expr (expr); |
+ if (!useless_type_conversion_p (ptrtype, TREE_TYPE (expr))) |
+ expr = fold_convert (ptrtype, expr); |
+ expr = fold_build2 (POINTER_PLUS_EXPR, ptrtype, expr, |
+ build_int_cst (size_type_node, |
+ adj->offset / BITS_PER_UNIT)); |
+ if (!adj->by_ref) |
+ expr = fold_build1 (INDIRECT_REF, adj->type, expr); |
+ } |
+ expr = force_gimple_operand_gsi (&gsi, expr, |
+ adj->by_ref |
+ || is_gimple_reg_type (adj->type), |
+ NULL, true, GSI_SAME_STMT); |
+ VEC_quick_push (tree, vargs, expr); |
+ } |
+ } |
+ |
+ if (dump_file && (dump_flags & TDF_DETAILS)) |
+ { |
+ fprintf (dump_file, "replacing stmt:"); |
+ print_gimple_stmt (dump_file, gsi_stmt (gsi), 0, 0); |
+ } |
+ |
+ callee_decl = !cs ? gimple_call_fndecl (stmt) : cs->callee->decl; |
+ new_stmt = gimple_build_call_vec (callee_decl, vargs); |
+ VEC_free (tree, heap, vargs); |
+ 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_copy_flags (new_stmt, stmt); |
+ gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); |
+ |
+ if (dump_file && (dump_flags & TDF_DETAILS)) |
+ { |
+ fprintf (dump_file, "with stmt:"); |
+ print_gimple_stmt (dump_file, new_stmt, 0, 0); |
+ fprintf (dump_file, "\n"); |
+ } |
+ gsi_replace (&gsi, new_stmt, true); |
+ if (cs) |
+ cgraph_set_call_stmt (cs, new_stmt); |
+ update_ssa (TODO_update_ssa); |
+ free_dominance_info (CDI_DOMINATORS); |
+} |
+ |
+/* 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 = VEC_length (ipa_parm_adjustment_t, adjustments); |
+ bool one = false; |
+ |
+ for (i = 0; i < len; i++) |
+ { |
+ struct ipa_parm_adjustment *adj; |
+ adj = VEC_index (ipa_parm_adjustment_t, 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 = VEC_length (ipa_parm_adjustment_t, outer); |
+ int inlen = VEC_length (ipa_parm_adjustment_t, inner); |
+ int removals = 0; |
+ ipa_parm_adjustment_vec adjustments, tmp; |
+ |
+ tmp = VEC_alloc (ipa_parm_adjustment_t, heap, inlen); |
+ for (i = 0; i < inlen; i++) |
+ { |
+ struct ipa_parm_adjustment *n; |
+ n = VEC_index (ipa_parm_adjustment_t, inner, i); |
+ |
+ if (n->remove_param) |
+ removals++; |
+ else |
+ VEC_quick_push (ipa_parm_adjustment_t, tmp, n); |
+ } |
+ |
+ adjustments = VEC_alloc (ipa_parm_adjustment_t, heap, outlen + removals); |
+ for (i = 0; i < outlen; i++) |
+ { |
+ struct ipa_parm_adjustment *r; |
+ struct ipa_parm_adjustment *out = VEC_index (ipa_parm_adjustment_t, |
+ outer, i); |
+ struct ipa_parm_adjustment *in = VEC_index (ipa_parm_adjustment_t, tmp, |
+ out->base_index); |
+ |
+ gcc_assert (!in->remove_param); |
+ if (out->remove_param) |
+ { |
+ if (!index_in_adjustments_multiple_times_p (in->base_index, tmp)) |
+ { |
+ r = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL); |
+ memset (r, 0, sizeof (*r)); |
+ r->remove_param = true; |
+ } |
+ continue; |
+ } |
+ |
+ r = VEC_quick_push (ipa_parm_adjustment_t, adjustments, NULL); |
+ memset (r, 0, sizeof (*r)); |
+ r->base_index = in->base_index; |
+ r->type = out->type; |
+ |
+ /* FIXME: Create nonlocal value too. */ |
+ |
+ if (in->copy_param && out->copy_param) |
+ r->copy_param = true; |
+ else if (in->copy_param) |
+ r->offset = out->offset; |
+ else if (out->copy_param) |
+ r->offset = in->offset; |
+ else |
+ r->offset = in->offset + out->offset; |
+ } |
+ |
+ for (i = 0; i < inlen; i++) |
+ { |
+ struct ipa_parm_adjustment *n = VEC_index (ipa_parm_adjustment_t, |
+ inner, i); |
+ |
+ if (n->remove_param) |
+ VEC_quick_push (ipa_parm_adjustment_t, adjustments, n); |
+ } |
+ |
+ VEC_free (ipa_parm_adjustment_t, heap, tmp); |
+ 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 = VEC_length (ipa_parm_adjustment_t, adjustments); |
+ bool first = true; |
+ VEC(tree, heap) *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 = VEC_index (ipa_parm_adjustment_t, adjustments, i); |
+ |
+ if (!first) |
+ fprintf (file, " "); |
+ else |
+ first = false; |
+ |
+ fprintf (file, "%i. base_index: %i - ", i, adj->base_index); |
+ print_generic_expr (file, VEC_index (tree, parms, adj->base_index), 0); |
+ if (adj->base) |
+ { |
+ fprintf (file, ", base: "); |
+ print_generic_expr (file, adj->base, 0); |
+ } |
+ if (adj->reduction) |
+ { |
+ fprintf (file, ", reduction: "); |
+ print_generic_expr (file, adj->reduction, 0); |
+ } |
+ if (adj->new_ssa_base) |
+ { |
+ fprintf (file, ", new_ssa_base: "); |
+ print_generic_expr (file, adj->new_ssa_base, 0); |
+ } |
+ |
+ if (adj->copy_param) |
+ fprintf (file, ", copy_param"); |
+ else if (adj->remove_param) |
+ 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"); |
+ } |
+ VEC_free (tree, heap, parms); |
+} |
+ |
+/* Stream out jump function JUMP_FUNC to OB. */ |
+ |
+static void |
+ipa_write_jump_function (struct output_block *ob, |
+ struct ipa_jump_func *jump_func) |
+{ |
+ lto_output_uleb128_stream (ob->main_stream, |
+ jump_func->type); |
+ |
+ switch (jump_func->type) |
+ { |
+ case IPA_JF_UNKNOWN: |
+ break; |
+ case IPA_JF_CONST: |
+ lto_output_tree (ob, jump_func->value.constant, true); |
+ break; |
+ case IPA_JF_PASS_THROUGH: |
+ lto_output_tree (ob, jump_func->value.pass_through.operand, true); |
+ lto_output_uleb128_stream (ob->main_stream, |
+ jump_func->value.pass_through.formal_id); |
+ lto_output_uleb128_stream (ob->main_stream, |
+ jump_func->value.pass_through.operation); |
+ break; |
+ case IPA_JF_ANCESTOR: |
+ lto_output_uleb128_stream (ob->main_stream, |
+ jump_func->value.ancestor.offset); |
+ lto_output_tree (ob, jump_func->value.ancestor.type, true); |
+ lto_output_uleb128_stream (ob->main_stream, |
+ jump_func->value.ancestor.formal_id); |
+ break; |
+ case IPA_JF_CONST_MEMBER_PTR: |
+ lto_output_tree (ob, jump_func->value.member_cst.pfn, true); |
+ lto_output_tree (ob, jump_func->value.member_cst.delta, false); |
+ break; |
+ } |
+} |
+ |
+/* Read in jump function JUMP_FUNC from IB. */ |
+ |
+static void |
+ipa_read_jump_function (struct lto_input_block *ib, |
+ struct ipa_jump_func *jump_func, |
+ struct data_in *data_in) |
+{ |
+ jump_func->type = (enum jump_func_type) lto_input_uleb128 (ib); |
+ |
+ switch (jump_func->type) |
+ { |
+ case IPA_JF_UNKNOWN: |
+ break; |
+ case IPA_JF_CONST: |
+ jump_func->value.constant = lto_input_tree (ib, data_in); |
+ break; |
+ case IPA_JF_PASS_THROUGH: |
+ jump_func->value.pass_through.operand = lto_input_tree (ib, data_in); |
+ jump_func->value.pass_through.formal_id = lto_input_uleb128 (ib); |
+ jump_func->value.pass_through.operation = (enum tree_code) lto_input_uleb128 (ib); |
+ break; |
+ case IPA_JF_ANCESTOR: |
+ jump_func->value.ancestor.offset = lto_input_uleb128 (ib); |
+ jump_func->value.ancestor.type = lto_input_tree (ib, data_in); |
+ jump_func->value.ancestor.formal_id = lto_input_uleb128 (ib); |
+ break; |
+ case IPA_JF_CONST_MEMBER_PTR: |
+ jump_func->value.member_cst.pfn = lto_input_tree (ib, data_in); |
+ jump_func->value.member_cst.delta = lto_input_tree (ib, data_in); |
+ break; |
+ } |
+} |
+ |
+/* Stream out a parameter call note. */ |
+ |
+static void |
+ipa_write_param_call_note (struct output_block *ob, |
+ struct ipa_param_call_note *note) |
+{ |
+ gcc_assert (!note->processed); |
+ lto_output_uleb128_stream (ob->main_stream, gimple_uid (note->stmt)); |
+ lto_output_sleb128_stream (ob->main_stream, note->formal_id); |
+ lto_output_sleb128_stream (ob->main_stream, note->count); |
+ lto_output_sleb128_stream (ob->main_stream, note->frequency); |
+ lto_output_sleb128_stream (ob->main_stream, note->loop_nest); |
+} |
+ |
+/* Read in a parameter call note. */ |
+ |
+static void |
+ipa_read_param_call_note (struct lto_input_block *ib, |
+ struct ipa_node_params *info) |
+ |
+{ |
+ struct ipa_param_call_note *note = XCNEW (struct ipa_param_call_note); |
+ |
+ note->lto_stmt_uid = (unsigned int) lto_input_uleb128 (ib); |
+ note->formal_id = (int) lto_input_sleb128 (ib); |
+ note->count = (gcov_type) lto_input_sleb128 (ib); |
+ note->frequency = (int) lto_input_sleb128 (ib); |
+ note->loop_nest = (int) lto_input_sleb128 (ib); |
+ |
+ note->next = info->param_calls; |
+ info->param_calls = note; |
+} |
+ |
+ |
+/* Stream out NODE info to OB. */ |
+ |
+static void |
+ipa_write_node_info (struct output_block *ob, struct cgraph_node *node) |
+{ |
+ int node_ref; |
+ lto_cgraph_encoder_t encoder; |
+ struct ipa_node_params *info = IPA_NODE_REF (node); |
+ int j; |
+ struct cgraph_edge *e; |
+ struct bitpack_d *bp; |
+ int note_count = 0; |
+ struct ipa_param_call_note *note; |
+ |
+ encoder = ob->decl_state->cgraph_node_encoder; |
+ node_ref = lto_cgraph_encoder_encode (encoder, node); |
+ lto_output_uleb128_stream (ob->main_stream, node_ref); |
+ |
+ bp = bitpack_create (); |
+ bp_pack_value (bp, info->called_with_var_arguments, 1); |
+ bp_pack_value (bp, info->uses_analysis_done, 1); |
+ gcc_assert (info->modification_analysis_done |
+ || ipa_get_param_count (info) == 0); |
+ gcc_assert (!info->node_enqueued); |
+ gcc_assert (!info->ipcp_orig_node); |
+ for (j = 0; j < ipa_get_param_count (info); j++) |
+ bp_pack_value (bp, info->params[j].modified, 1); |
+ lto_output_bitpack (ob->main_stream, bp); |
+ bitpack_delete (bp); |
+ for (e = node->callees; e; e = e->next_callee) |
+ { |
+ struct ipa_edge_args *args = IPA_EDGE_REF (e); |
+ |
+ lto_output_uleb128_stream (ob->main_stream, |
+ ipa_get_cs_argument_count (args)); |
+ for (j = 0; j < ipa_get_cs_argument_count (args); j++) |
+ ipa_write_jump_function (ob, ipa_get_ith_jump_func (args, j)); |
+ } |
+ |
+ for (note = info->param_calls; note; note = note->next) |
+ note_count++; |
+ lto_output_uleb128_stream (ob->main_stream, note_count); |
+ for (note = info->param_calls; note; note = note->next) |
+ ipa_write_param_call_note (ob, note); |
+} |
+ |
+/* Srtream in NODE info from IB. */ |
+ |
+static void |
+ipa_read_node_info (struct lto_input_block *ib, struct cgraph_node *node, |
+ struct data_in *data_in) |
+{ |
+ struct ipa_node_params *info = IPA_NODE_REF (node); |
+ int k; |
+ struct cgraph_edge *e; |
+ struct bitpack_d *bp; |
+ int i, note_count; |
+ |
+ ipa_initialize_node_params (node); |
+ |
+ bp = lto_input_bitpack (ib); |
+ info->called_with_var_arguments = bp_unpack_value (bp, 1); |
+ info->uses_analysis_done = bp_unpack_value (bp, 1); |
+ if (ipa_get_param_count (info) != 0) |
+ { |
+ info->modification_analysis_done = true; |
+ info->uses_analysis_done = true; |
+ } |
+ info->node_enqueued = false; |
+ for (k = 0; k < ipa_get_param_count (info); k++) |
+ info->params[k].modified = bp_unpack_value (bp, 1); |
+ bitpack_delete (bp); |
+ for (e = node->callees; e; e = e->next_callee) |
+ { |
+ struct ipa_edge_args *args = IPA_EDGE_REF (e); |
+ int count = lto_input_uleb128 (ib); |
+ |
+ ipa_set_cs_argument_count (args, count); |
+ if (!count) |
+ continue; |
+ |
+ args->jump_functions = GGC_CNEWVEC (struct ipa_jump_func, |
+ ipa_get_cs_argument_count (args)); |
+ for (k = 0; k < ipa_get_cs_argument_count (args); k++) |
+ ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), data_in); |
+ } |
+ |
+ note_count = lto_input_uleb128 (ib); |
+ for (i = 0; i < note_count; i++) |
+ ipa_read_param_call_note (ib, info); |
+} |
+ |
+/* Write jump functions for nodes in SET. */ |
+ |
+void |
+ipa_prop_write_jump_functions (cgraph_node_set set) |
+{ |
+ struct cgraph_node *node; |
+ struct output_block *ob = create_output_block (LTO_section_jump_functions); |
+ unsigned int count = 0; |
+ cgraph_node_set_iterator csi; |
+ |
+ ob->cgraph_node = NULL; |
+ |
+ for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) |
+ { |
+ node = csi_node (csi); |
+ if (node->analyzed && IPA_NODE_REF (node) != NULL) |
+ count++; |
+ } |
+ |
+ lto_output_uleb128_stream (ob->main_stream, count); |
+ |
+ /* Process all of the functions. */ |
+ for (csi = csi_start (set); !csi_end_p (csi); csi_next (&csi)) |
+ { |
+ node = csi_node (csi); |
+ if (node->analyzed && IPA_NODE_REF (node) != NULL) |
+ ipa_write_node_info (ob, node); |
+ } |
+ lto_output_1_stream (ob->main_stream, 0); |
+ produce_asm (ob, NULL); |
+ destroy_output_block (ob); |
+} |
+ |
+/* Read section in file FILE_DATA of length LEN with data DATA. */ |
+ |
+static void |
+ipa_prop_read_section (struct lto_file_decl_data *file_data, const char *data, |
+ size_t len) |
+{ |
+ const struct lto_function_header *header = |
+ (const struct lto_function_header *) data; |
+ const int32_t cfg_offset = sizeof (struct lto_function_header); |
+ const int32_t main_offset = cfg_offset + header->cfg_size; |
+ const int32_t string_offset = main_offset + header->main_size; |
+ struct data_in *data_in; |
+ struct lto_input_block ib_main; |
+ unsigned int i; |
+ unsigned int count; |
+ |
+ LTO_INIT_INPUT_BLOCK (ib_main, (const char *) data + main_offset, 0, |
+ header->main_size); |
+ |
+ data_in = |
+ lto_data_in_create (file_data, (const char *) data + string_offset, |
+ header->string_size, NULL); |
+ count = lto_input_uleb128 (&ib_main); |
+ |
+ for (i = 0; i < count; i++) |
+ { |
+ unsigned int index; |
+ struct cgraph_node *node; |
+ lto_cgraph_encoder_t encoder; |
+ |
+ index = lto_input_uleb128 (&ib_main); |
+ encoder = file_data->cgraph_node_encoder; |
+ node = lto_cgraph_encoder_deref (encoder, index); |
+ ipa_read_node_info (&ib_main, node, data_in); |
+ } |
+ lto_free_section_data (file_data, LTO_section_jump_functions, NULL, data, |
+ len); |
+ lto_data_in_delete (data_in); |
+} |
+ |
+/* Read ipcp jump functions. */ |
+ |
+void |
+ipa_prop_read_jump_functions (void) |
+{ |
+ struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data (); |
+ struct lto_file_decl_data *file_data; |
+ unsigned int j = 0; |
+ |
+ ipa_check_create_node_params (); |
+ ipa_check_create_edge_args (); |
+ ipa_register_cgraph_hooks (); |
+ |
+ while ((file_data = file_data_vec[j++])) |
+ { |
+ size_t len; |
+ const char *data = lto_get_section_data (file_data, LTO_section_jump_functions, NULL, &len); |
+ |
+ if (data) |
+ ipa_prop_read_section (file_data, data, len); |
+ } |
+} |
+ |
+/* After merging units, we can get mismatch in argument counts. |
+ Also decl merging might've rendered parameter lists obsolette. |
+ Also compute called_with_variable_arg info. */ |
+ |
+void |
+ipa_update_after_lto_read (void) |
+{ |
+ struct cgraph_node *node; |
+ struct cgraph_edge *cs; |
+ |
+ ipa_check_create_node_params (); |
+ ipa_check_create_edge_args (); |
+ |
+ for (node = cgraph_nodes; node; node = node->next) |
+ if (node->analyzed) |
+ ipa_initialize_node_params (node); |
+ |
+ for (node = cgraph_nodes; node; node = node->next) |
+ if (node->analyzed) |
+ for (cs = node->callees; cs; cs = cs->next_callee) |
+ { |
+ if (ipa_get_cs_argument_count (IPA_EDGE_REF (cs)) |
+ != ipa_get_param_count (IPA_NODE_REF (cs->callee))) |
+ ipa_set_called_with_variable_arg (IPA_NODE_REF (cs->callee)); |
+ } |
+} |
+ |
+/* Walk param call notes of NODE and set their call statements given the uid |
+ stored in each note and STMTS which is an array of statements indexed by the |
+ uid. */ |
+ |
+void |
+lto_ipa_fixup_call_notes (struct cgraph_node *node, gimple *stmts) |
+{ |
+ struct ipa_node_params *info; |
+ struct ipa_param_call_note *note; |
+ |
+ ipa_check_create_node_params (); |
+ info = IPA_NODE_REF (node); |
+ note = info->param_calls; |
+ /* If there are no notes or they have already been fixed up (the same fixup |
+ is called for both inlining and ipa-cp), there's nothing to do. */ |
+ if (!note || note->stmt) |
+ return; |
+ |
+ do |
+ { |
+ note->stmt = stmts[note->lto_stmt_uid]; |
+ note = note->next; |
+ } |
+ while (note); |
+} |