Mercurial > hg > CbC > CbC_gcc
diff gcc/ipa-prop.c @ 55:77e2b8dfacca gcc-4.4.5
update it from 4.4.3 to 4.5.0
author | ryoma <e075725@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 12 Feb 2010 23:39:51 +0900 |
parents | a06113de4d67 |
children | b7f97abdc517 |
line wrap: on
line diff
--- a/gcc/ipa-prop.c Sun Feb 07 18:28:00 2010 +0900 +++ b/gcc/ipa-prop.c Fri Feb 12 23:39:51 2010 +0900 @@ -1,5 +1,5 @@ /* Interprocedural analyses. - Copyright (C) 2005, 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 2005, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GCC. @@ -33,11 +33,12 @@ #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 +46,24 @@ 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 +76,33 @@ 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 +143,20 @@ } } +/* 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 +164,9 @@ 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 +188,30 @@ } } -/* 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. */ - -static void -ipa_check_stmt_modifications (struct ipa_node_params *info, gimple stmt) -{ - int j; - int index; - tree lhs; - - switch (gimple_code (stmt)) - { - case GIMPLE_ASSIGN: - lhs = gimple_assign_lhs (stmt); +/* 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. */ - 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; +static bool +visit_store_addr_for_mod_analysis (gimple stmt ATTRIBUTE_UNUSED, + tree op, void *data) +{ + struct ipa_node_params *info = (struct ipa_node_params *) data; - 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; + if (TREE_CODE (op) == PARM_DECL) + { + int index = ipa_get_param_decl_index (info, op); + gcc_assert (index >= 0); + info->params[index].modified = true; + } - default: - break; - } + 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 +220,17 @@ 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 +249,7 @@ 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 +281,16 @@ 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 +298,25 @@ 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 +335,73 @@ } } +/* 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 +422,24 @@ 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 (SSA_NAME_IS_DEFAULT_DEF (arg)) + { + int index = ipa_get_param_decl_index (info, SSA_NAME_VAR (arg)); - if (index >= 0) - { - functions[num].type = IPA_PASS_THROUGH; - functions[num].value.formal_id = index; + 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 +505,9 @@ 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 +527,29 @@ 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 +576,7 @@ 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 +589,7 @@ 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 +607,7 @@ if (!delta && fld == delta_field) { + rhs = get_ssa_def_if_simple_copy (rhs); if (TREE_CODE (rhs) == INTEGER_CST) { delta = rhs; @@ -545,7 +641,7 @@ { 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 +662,8 @@ 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 +681,28 @@ 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 +712,15 @@ 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. */ @@ -652,8 +751,10 @@ 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 +857,15 @@ 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 +898,7 @@ 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 +909,8 @@ 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 +918,10 @@ 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 +974,10 @@ /* 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 +992,26 @@ { struct ipa_jump_func *src, *dst = ipa_get_ith_jump_func (args, i); - if (dst->type != IPA_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)) + if (dst->type == IPA_JF_ANCESTOR) { - dst->type = IPA_BOTTOM; + dst->type = IPA_JF_UNKNOWN; continue; } - src = ipa_get_ith_jump_func (top, dst->value.formal_id); + if (dst->type != IPA_JF_PASS_THROUGH) + continue; + + /* 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_JF_UNKNOWN; + continue; + } + + src = ipa_get_ith_jump_func (top, dst->value.pass_through.formal_id); *dst = *src; } } @@ -910,7 +1026,7 @@ 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 +1062,25 @@ 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 +1102,20 @@ 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 +1157,10 @@ 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 +1177,7 @@ 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 +1195,7 @@ 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 +1266,22 @@ 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 +1303,8 @@ 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,7 +1420,9 @@ 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)) @@ -1296,3 +1443,803 @@ 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; + } + + /* 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); + gcc_assert (info->modification_analysis_done + || ipa_get_param_count (info) == 0); + gcc_assert (info->uses_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); + bp_pack_value (bp, info->params[j].called, 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); + 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); + info->params[k].called = 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) + continue; + ipa_initialize_node_params (node); + 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); +}