/* Return true if this callsite should be redirected to the original callee (instead of the cloned one). */ static bool ipcp_need_redirect_p (struct cgraph_edge *cs) { struct ipa_node_params *orig_callee_info; int i, count; struct cgraph_node *node = cs->callee, *orig; if (!n_cloning_candidates) return false; if ((orig = ipcp_get_orig_node (node)) != NULL) node = orig; if (ipcp_get_orig_node (cs->caller)) return false; orig_callee_info = IPA_NODE_REF (node); count = ipa_get_param_count (orig_callee_info); for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (orig_callee_info, i); struct ipa_jump_func *jump_func; jump_func = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), i); if ((ipcp_lat_is_const (lat) && jump_func->type != IPA_JF_CONST) || (!ipa_param_cannot_devirtualize_p (orig_callee_info, i) && !ipa_param_types_vec_empty (orig_callee_info, i) && jump_func->type != IPA_JF_CONST && jump_func->type != IPA_JF_KNOWN_TYPE)) return true; } return false; }
/* Initialize ipcp_lattices array. The lattices corresponding to supported types (integers, real types and Fortran constants defined as const_decls) are initialized to IPA_TOP, the rest of them to IPA_BOTTOM. */ static void ipcp_initialize_node_lattices (struct cgraph_node *node) { int i; struct ipa_node_params *info = IPA_NODE_REF (node); enum ipa_lattice_type type; if (ipa_is_called_with_var_arguments (info)) type = IPA_BOTTOM; else if (node->local.local) type = IPA_TOP; /* When cloning is allowed, we can assume that externally visible functions are not called. We will compensate this by cloning later. */ else if (ipcp_cloning_candidate_p (node)) type = IPA_TOP, n_cloning_candidates ++; else type = IPA_BOTTOM; for (i = 0; i < ipa_get_param_count (info) ; i++) { ipcp_get_lattice (info, i)->type = type; if (type == IPA_BOTTOM) ipa_set_param_cannot_devirtualize (info, i); } }
int ipa_get_param_decl_index (struct ipa_node_params *info, tree ptree) { int i, count; count = ipa_get_param_count (info); for (i = 0; i < count; i++) if (ipa_get_param (info, i) == ptree) return i; return -1; }
/* If NODE was cloned, how much would program grow? */ static long ipcp_estimate_growth (struct cgraph_node *node) { struct cgraph_edge *cs; int redirectable_node_callers = 0; int removable_args = 0; bool need_original = !cgraph_will_be_removed_from_program_if_no_direct_calls (node); struct ipa_node_params *info; int i, count; int growth; for (cs = node->callers; cs != NULL; cs = cs->next_caller) if (cs->caller == node || !ipcp_need_redirect_p (cs)) redirectable_node_callers++; else need_original = true; /* If we will be able to fully replace original node, we never increase program size. */ if (!need_original) return 0; info = IPA_NODE_REF (node); count = ipa_get_param_count (info); if (node->local.can_change_signature) for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); /* We can proactively remove obviously unused arguments. */ if (!ipa_is_param_used (info, i)) removable_args++; if (lat->type == IPA_CONST_VALUE) removable_args++; } /* We make just very simple estimate of savings for removal of operand from call site. Precise cost is difficult to get, as our size metric counts constants and moves as free. Generally we are looking for cases that small function is called very many times. */ growth = node->local.inline_summary.self_size - removable_args * redirectable_node_callers; if (growth < 0) return 0; return growth; }
/* Fix the callsites and the call graph after function cloning was done. */ static void ipcp_update_callgraph (void) { struct cgraph_node *node; for (node = cgraph_nodes; node; node = node->next) if (node->analyzed && ipcp_node_is_clone (node)) { bitmap args_to_skip = NULL; struct cgraph_node *orig_node = ipcp_get_orig_node (node); struct ipa_node_params *info = IPA_NODE_REF (orig_node); int i, count = ipa_get_param_count (info); struct cgraph_edge *cs, *next; if (node->local.can_change_signature) { args_to_skip = BITMAP_ALLOC (NULL); for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); /* We can proactively remove obviously unused arguments. */ if (!ipa_is_param_used (info, i)) { bitmap_set_bit (args_to_skip, i); continue; } if (lat->type == IPA_CONST_VALUE) bitmap_set_bit (args_to_skip, i); } } for (cs = node->callers; cs; cs = next) { next = cs->next_caller; if (!ipcp_node_is_clone (cs->caller) && ipcp_need_redirect_p (cs)) { if (dump_file) fprintf (dump_file, "Redirecting edge %s/%i -> %s/%i " "back to %s/%i.", cgraph_node_name (cs->caller), cs->caller->uid, cgraph_node_name (cs->callee), cs->callee->uid, cgraph_node_name (orig_node), orig_node->uid); cgraph_redirect_edge_callee (cs, orig_node); } } } }
/* Print all ipcp_lattices of all functions to F. */ static void ipcp_print_all_lattices (FILE * f) { struct cgraph_node *node; int i, count; fprintf (f, "\nLattice:\n"); for (node = cgraph_nodes; node; node = node->next) { struct ipa_node_params *info; if (!node->analyzed) continue; info = IPA_NODE_REF (node); fprintf (f, " Node: %s:\n", cgraph_node_name (node)); count = ipa_get_param_count (info); for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); fprintf (f, " param [%d]: ", i); if (lat->type == IPA_CONST_VALUE) { tree cst = lat->constant; fprintf (f, "type is CONST "); print_generic_expr (f, cst, 0); if (TREE_CODE (cst) == ADDR_EXPR && TREE_CODE (TREE_OPERAND (cst, 0)) == CONST_DECL) { fprintf (f, " -> "); print_generic_expr (f, DECL_INITIAL (TREE_OPERAND (cst, 0)), 0); } } else if (lat->type == IPA_TOP) fprintf (f, "type is TOP"); else fprintf (f, "type is BOTTOM"); if (ipa_param_cannot_devirtualize_p (info, i)) fprintf (f, " - cannot_devirtualize set\n"); else if (ipa_param_types_vec_empty (info, i)) fprintf (f, " - type list empty\n"); else fprintf (f, "\n"); } } }
/* Return true if there are some formal parameters whose value is IPA_TOP (in the whole compilation unit). Change their values to IPA_BOTTOM, since they most probably get their values from outside of this compilation unit. */ static bool ipcp_change_tops_to_bottom (void) { int i, count; struct cgraph_node *node; bool prop_again; prop_again = false; for (node = cgraph_nodes; node; node = node->next) { struct ipa_node_params *info = IPA_NODE_REF (node); count = ipa_get_param_count (info); for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); if (lat->type == IPA_TOP) { prop_again = true; if (dump_file) { fprintf (dump_file, "Forcing param "); print_generic_expr (dump_file, ipa_get_param (info, i), 0); fprintf (dump_file, " of node %s to bottom.\n", cgraph_node_name (node)); } lat->type = IPA_BOTTOM; } if (!ipa_param_cannot_devirtualize_p (info, i) && ipa_param_types_vec_empty (info, i)) { prop_again = true; ipa_set_param_cannot_devirtualize (info, i); if (dump_file) { fprintf (dump_file, "Marking param "); print_generic_expr (dump_file, ipa_get_param (info, i), 0); fprintf (dump_file, " of node %s as unusable for " "devirtualization.\n", cgraph_node_name (node)); } } } } return prop_again; }
/* Return number of live constant parameters. */ static int ipcp_const_param_count (struct cgraph_node *node) { int const_param = 0; struct ipa_node_params *info = IPA_NODE_REF (node); int count = ipa_get_param_count (info); int i; for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); if ((ipcp_lat_is_insertable (lat) /* Do not count obviously unused arguments. */ && ipa_is_param_used (info, i)) || (!ipa_param_cannot_devirtualize_p (info, i) && !ipa_param_types_vec_empty (info, i))) const_param++; } return const_param; }
/* Propagate the constant parameters found by ipcp_iterate_stage() to the function's code. */ static void ipcp_insert_stage (void) { struct cgraph_node *node, *node1 = NULL; int i; VEC (cgraph_edge_p, heap) * redirect_callers; VEC (ipa_replace_map_p,gc)* replace_trees; int node_callers, count; tree parm_tree; struct ipa_replace_map *replace_param; fibheap_t heap; long overall_size = 0, new_size = 0; long max_new_size; ipa_check_create_node_params (); ipa_check_create_edge_args (); if (dump_file) fprintf (dump_file, "\nIPA insert stage:\n\n"); dead_nodes = BITMAP_ALLOC (NULL); for (node = cgraph_nodes; node; node = node->next) if (node->analyzed) { if (node->count > max_count) max_count = node->count; overall_size += node->local.inline_summary.self_size; } max_new_size = overall_size; if (max_new_size < PARAM_VALUE (PARAM_LARGE_UNIT_INSNS)) max_new_size = PARAM_VALUE (PARAM_LARGE_UNIT_INSNS); max_new_size = max_new_size * PARAM_VALUE (PARAM_IPCP_UNIT_GROWTH) / 100 + 1; /* First collect all functions we proved to have constant arguments to heap. */ heap = fibheap_new (); for (node = cgraph_nodes; node; node = node->next) { struct ipa_node_params *info; /* Propagation of the constant is forbidden in certain conditions. */ if (!node->analyzed || !ipcp_node_modifiable_p (node)) continue; info = IPA_NODE_REF (node); if (ipa_is_called_with_var_arguments (info)) continue; if (ipcp_const_param_count (node)) node->aux = fibheap_insert (heap, ipcp_estimate_cloning_cost (node), node); } /* Now clone in priority order until code size growth limits are met or heap is emptied. */ while (!fibheap_empty (heap)) { struct ipa_node_params *info; int growth = 0; bitmap args_to_skip; struct cgraph_edge *cs; node = (struct cgraph_node *)fibheap_extract_min (heap); node->aux = NULL; if (dump_file) fprintf (dump_file, "considering function %s\n", cgraph_node_name (node)); growth = ipcp_estimate_growth (node); if (new_size + growth > max_new_size) break; if (growth && optimize_function_for_size_p (DECL_STRUCT_FUNCTION (node->decl))) { if (dump_file) fprintf (dump_file, "Not versioning, cold code would grow"); continue; } info = IPA_NODE_REF (node); count = ipa_get_param_count (info); replace_trees = VEC_alloc (ipa_replace_map_p, gc, 1); if (node->local.can_change_signature) args_to_skip = BITMAP_GGC_ALLOC (); else args_to_skip = NULL; for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); parm_tree = ipa_get_param (info, i); /* We can proactively remove obviously unused arguments. */ if (!ipa_is_param_used (info, i)) { if (args_to_skip) bitmap_set_bit (args_to_skip, i); continue; } if (lat->type == IPA_CONST_VALUE) { replace_param = ipcp_create_replace_map (parm_tree, lat); if (replace_param == NULL) break; VEC_safe_push (ipa_replace_map_p, gc, replace_trees, replace_param); if (args_to_skip) bitmap_set_bit (args_to_skip, i); } } if (i < count) { if (dump_file) fprintf (dump_file, "Not versioning, some parameters couldn't be replaced"); continue; } new_size += growth; /* Look if original function becomes dead after cloning. */ for (cs = node->callers; cs != NULL; cs = cs->next_caller) if (cs->caller == node || ipcp_need_redirect_p (cs)) break; if (!cs && cgraph_will_be_removed_from_program_if_no_direct_calls (node)) bitmap_set_bit (dead_nodes, node->uid); /* Compute how many callers node has. */ node_callers = 0; for (cs = node->callers; cs != NULL; cs = cs->next_caller) node_callers++; redirect_callers = VEC_alloc (cgraph_edge_p, heap, node_callers); for (cs = node->callers; cs != NULL; cs = cs->next_caller) if (!cs->indirect_inlining_edge) VEC_quick_push (cgraph_edge_p, redirect_callers, cs); /* Redirecting all the callers of the node to the new versioned node. */ node1 = cgraph_create_virtual_clone (node, redirect_callers, replace_trees, args_to_skip, "constprop"); args_to_skip = NULL; VEC_free (cgraph_edge_p, heap, redirect_callers); replace_trees = NULL; if (node1 == NULL) continue; ipcp_process_devirtualization_opportunities (node1); if (dump_file) fprintf (dump_file, "versioned function %s with growth %i, overall %i\n", cgraph_node_name (node), (int)growth, (int)new_size); ipcp_init_cloned_node (node, node1); info = IPA_NODE_REF (node); for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); if (lat->type == IPA_CONST_VALUE) ipcp_discover_new_direct_edges (node1, i, lat->constant); } if (dump_file) dump_function_to_file (node1->decl, dump_file, dump_flags); for (cs = node->callees; cs; cs = cs->next_callee) if (cs->callee->aux) { fibheap_delete_node (heap, (fibnode_t) cs->callee->aux); cs->callee->aux = fibheap_insert (heap, ipcp_estimate_cloning_cost (cs->callee), cs->callee); } } while (!fibheap_empty (heap)) { if (dump_file) fprintf (dump_file, "skipping function %s\n", cgraph_node_name (node)); node = (struct cgraph_node *) fibheap_extract_min (heap); node->aux = NULL; } fibheap_delete (heap); BITMAP_FREE (dead_nodes); ipcp_update_callgraph (); ipcp_update_profiling (); }