From c11ab4e01d54310e48c2f45123aa08db2d9eb145 Mon Sep 17 00:00:00 2001 From: amylaar Date: Mon, 16 Nov 2009 15:03:27 +0000 Subject: [PATCH] Merge in changes from: http://gcc-ici.svn.sourceforge.net/svnroot/gcc-ici/branches/gcc-4.4.0-ici-2.0-adapt This is mostly a textual merge, and still won't compile. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/ici-20091108-branch@154203 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/Makefile.in | 9 + gcc/cgraph.h | 4 + gcc/cgraphunit.c | 375 +++++- gcc/common.opt | 8 + gcc/events.c | 49 +- gcc/feature-internal.h | 6 +- gcc/feature-list.c | 38 +- gcc/feature.h | 6 +- gcc/function-instrumentation.c | 259 ++++ gcc/gcc-plugin.h | 10 +- gcc/gcc.c | 72 +- gcc/generic-cloning.c | 637 +++++++++ gcc/graphite.c | 3 + gcc/highlev-plugin-internal.h | 39 +- gcc/highlev-plugin.h | 45 +- gcc/ic-interface.h | 2 + gcc/ipa-inline.c | 19 +- gcc/loop-init.c | 19 + gcc/loop-unroll.c | 88 +- gcc/pass-manager.c | 117 +- gcc/pass-manager.h | 14 +- gcc/passes.c | 50 +- gcc/plugin-loader.c | 2 - gcc/plugin.c | 33 +- gcc/toplev.c | 16 +- gcc/tree-inline.c | 2854 +++++++++++++++++++++++++++++++++++----- gcc/tree-inline.h | 2 + gcc/tree-nrv.c | 1 - gcc/tree-optimize.c | 24 +- gcc/tree-pass.h | 8 + 30 files changed, 4346 insertions(+), 463 deletions(-) create mode 100644 gcc/function-instrumentation.c create mode 100644 gcc/generic-cloning.c diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 6cfc1a2bb73..e439270d4c1 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1181,6 +1181,8 @@ OBJS-common = \ expr.o \ feature.o \ feature-list.o \ + generic-cloning.o \ + function-instrumentation.o \ final.o \ fixed-value.o \ fold-const.o \ @@ -2727,6 +2729,13 @@ feature-list.o : feature-list.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \ $(TREE_H) $(INPUT_H) $(BASIC_BLOCK_H) $(TREE_INLINE_H) $(TREE_FLOW_H) \ tree-pass.h version.h opts.h params.h function.h feature-internal.h +generic-cloning.o : generic-cloning.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ + tree-pass.h $(FLAGS_H) $(CGRAPH_H) $(TREE_H) $(TARGET_H) ipa-prop.h opts.h + +function-instrumentation.o : function-instrumentation.c $(CONFIG_H) \ + $(SYSTEM_H) coretypes.h tree-pass.h \ + $(FLAGS_H) $(CGRAPH_H) $(TREE_H) $(TARGET_H) ipa-prop.h opts.h + plugin-loader.o : plugin-loader.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(HASHTAB_H) $(GCC_PLUGIN_H) \ $(TOPLEV_H) $(GCC_H) options.h highlev-plugin-internal.h feature-internal.h diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 016ce9de14c..fb8456668ed 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -453,6 +453,10 @@ void verify_cgraph_node (struct cgraph_node *); void cgraph_build_static_cdtor (char which, tree body, int priority); void cgraph_reset_static_var_maps (void); void init_cgraph (void); +struct cgraph_node *generic_cloning_cgraph_function_versioning (struct cgraph_node *, + VEC(cgraph_edge_p,heap)*, + varray_type, + bitmap); struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, VEC(cgraph_edge_p,heap)*, VEC(ipa_replace_map_p,gc)*, diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 7105e59c5ec..8c2d5e01892 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -136,6 +136,10 @@ along with GCC; see the file COPYING3. If not see #include "output.h" #include "coverage.h" +#include "opts.h" +#include "highlev-plugin-internal.h" + + static void cgraph_expand_all_functions (void); static void cgraph_mark_functions_to_output (void); static void cgraph_expand_function (struct cgraph_node *); @@ -1350,41 +1354,215 @@ cgraph_preserve_function_body_p (tree decl) return false; } + +/* Load function specific optimizations with parameters. */ + +/* separate string into arguments. */ +static void +ici_separate_arguments (const char *string, unsigned int *argc, char ***argv) +{ + const char *p; + char c; + char **args; + unsigned int len; + int i; + + /* Count number of args */ + p = string; + c = *p; + *argc = 1; + while (c) + { + if ((c == ' ' || c == '\t') && len) + { + len = 0; + ++*argc; + } + else + len = 1; + c = *++p; + } + if (len) + ++*argc; + + args = (char **) xmalloc (sizeof(char *) * (*argc)); + *argv = args; + args[0] = (char*)xmalloc(sizeof(char)); /* argv[0] unavailable */ + args[0][0]='\0'; + i = 1; + p = string; + c = *p; + len = 0; + while (c) + { + if (c == ' ' || c == '\t') + { + if (len) + { + *(args + i) = (char *) xmalloc (sizeof(char) * (len + 1)); + strncpy (*(args + i), (p - len), len); + args[i][len] = '\0'; + ++i; + len = 0; + } + } + else + ++len; + c = *++p; + } + if (len) + { + *(args + i) = (char *) xmalloc (sizeof(char) * (len + 1)); + strncpy (*(args + i), (p - len), len); + args[i][len] = '\0'; + } +} + +/* free arguments string. */ +static void +ici_free_arguments (unsigned int argc, char **argv) +{ + unsigned int i; + for (i = 0; i < argc; ++i) + { + free (argv[i]); + } + free (argv); + +} + + +/* load function specific option strings and save to function structures. */ +static void +ici_load_function_specific_optimizations (void) +{ + static char *ici_function_spec_string; + struct cgraph_node *node; + struct function *old_cfun = cfun; /* Backup cfun. */ + for (node = cgraph_nodes; node; node = node->next) + { + struct function *fun; + tree fun_decl; + + fun = DECL_STRUCT_FUNCTION (node->decl); + if (!fun) + continue; + set_cfun (fun); + + ici_function_spec_string = NULL; + register_event_parameter ("function_spec_string", + (void *) &ici_function_spec_string, EP_VOID); + call_plugin_event ("function_spec_loader"); + unregister_event_parameter ("function_spec_string"); + if (!ici_function_spec_string) + continue; + + fun_decl = fun->decl; + if (TREE_CODE (fun_decl) == FUNCTION_DECL) + { + unsigned int argc; + char **argv; + struct cl_optimization old_global_opts; + tree old_function_opts; + int saved_flag_strict_aliasing; + int saved_flag_pcc_struct_return, + saved_flag_omit_frame_pointer, + saved_flag_asynchronous_unwind_tables; + + /* Save old global and function-specific optimizations. */ + cl_optimization_save (&old_global_opts); + + /* Store function-specific optimizations to global. */ + old_function_opts + = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fun_decl); + if (old_function_opts) + cl_optimization_restore (TREE_OPTIMIZATION (old_function_opts)); + + /* Parse options string. */ + ici_separate_arguments (ici_function_spec_string, &argc, &argv); + + /* Save flags which should not be changed. */ + + /* Change global optimizations with loaded + function specific string */ + saved_flag_strict_aliasing = flag_strict_aliasing; + saved_flag_omit_frame_pointer = flag_omit_frame_pointer; + saved_flag_pcc_struct_return = flag_pcc_struct_return; + saved_flag_asynchronous_unwind_tables = flag_asynchronous_unwind_tables; + decode_options (argc, (const char **) argv); + + flag_strict_aliasing = saved_flag_strict_aliasing; + flag_omit_frame_pointer = saved_flag_omit_frame_pointer; + flag_asynchronous_unwind_tables = saved_flag_asynchronous_unwind_tables; + flag_pcc_struct_return = saved_flag_pcc_struct_return; + + ici_free_arguments (argc, argv); + argv = NULL; + + /* Restore saved flags. */ + + /* Store global optimizations to function. */ + DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fun_decl) + = build_optimization_node (); + + /* Restore old global optmizations. */ + cl_optimization_restore (&old_global_opts); + } + + free (ici_function_spec_string); + } + /* Restore cfun */ + set_cfun (old_cfun); +} + + static void ipa_passes (void) { + static int ici_ipa_passes_substitute_status; + set_cfun (NULL); current_function_decl = NULL; gimple_register_cfg_hooks (); bitmap_obstack_initialize (NULL); - if (!in_lto_p) - execute_ipa_pass_list (all_small_ipa_passes); + invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_START, NULL); + if ((invoke_plugin_va_callbacks + (PLUGIN_ALL_IPA_PASSES_EXECUTION, + "substitute_status", EP_SILENT, &ici_ipa_passes_substitute_status) + != PLUGEVT_SUCCESS) + || ici_ipa_passes_substitute_status == 0) + { + if (!in_lto_p) + execute_ipa_pass_list (all_small_ipa_passes); - /* If pass_all_early_optimizations was not scheduled, the state of - the cgraph will not be properly updated. Update it now. */ - if (cgraph_state < CGRAPH_STATE_IPA_SSA) - cgraph_state = CGRAPH_STATE_IPA_SSA; + /* If pass_all_early_optimizations was not scheduled, the state of + the cgraph will not be properly updated. Update it now. */ + if (cgraph_state < CGRAPH_STATE_IPA_SSA) + cgraph_state = CGRAPH_STATE_IPA_SSA; - if (!in_lto_p) - { - /* Generate coverage variables and constructors. */ - coverage_finish (); + if (!in_lto_p) + { + /* Generate coverage variables and constructors. */ + coverage_finish (); - /* Process new functions added. */ - set_cfun (NULL); - current_function_decl = NULL; - cgraph_process_new_functions (); + /* Process new functions added. */ + set_cfun (NULL); + current_function_decl = NULL; + cgraph_process_new_functions (); - execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_regular_ipa_passes); - } - execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_lto_gen_passes); + execute_ipa_summary_passes + ((struct ipa_opt_pass_d *) all_regular_ipa_passes); + } + execute_ipa_summary_passes ((struct ipa_opt_pass_d *) all_lto_gen_passes); - if (!in_lto_p) - ipa_write_summaries (); + if (!in_lto_p) + ipa_write_summaries (); - if (!flag_ltrans) - execute_ipa_pass_list (all_regular_ipa_passes); + if (!flag_ltrans) + execute_ipa_pass_list (all_regular_ipa_passes); + } + invoke_plugin_callbacks (PLUGIN_ALL_IPA_PASSES_END, NULL); bitmap_obstack_release (NULL); } @@ -1416,6 +1594,9 @@ cgraph_optimize (void) fprintf (stderr, "Performing interprocedural optimizations\n"); cgraph_state = CGRAPH_STATE_IPA; + /* ICI: load and set function specific optimization with plugin. */ + ici_load_function_specific_optimizations (); + /* Don't run the IPA passes if there was any error or sorry messages. */ if (errorcount == 0 && sorrycount == 0) ipa_passes (); @@ -1652,6 +1833,106 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version, return new_version; } + +static struct cgraph_node * +generic_cloning_cgraph_copy_node_for_versioning (struct cgraph_node *old_version, + tree new_decl, + VEC(cgraph_edge_p,heap) *redirect_callers) + { + struct cgraph_node *new_version; + struct cgraph_edge *e, *new_e; + struct cgraph_edge *next_callee; + unsigned i; + + gcc_assert (old_version); + + new_version = cgraph_node (new_decl); + + gcc_assert(true == old_version->analyzed); + + new_version->analyzed = true; + new_version->local = old_version->local; + new_version->global = old_version->global; + new_version->rtl = new_version->rtl; + + /* probably wrong*/ + new_version->rtl = old_version->rtl; + + gcc_assert(true == old_version->reachable); + + new_version->reachable = true; + new_version->count = old_version->count; + + /*XXX: do not know what to do with the following pointers */ + new_version->origin = old_version->origin; + gcc_assert(NULL == old_version->nested); + gcc_assert(NULL == old_version->next_nested); + /*gcc_assert(NULL == old_version->next_needed);*/ + gcc_assert(NULL == old_version->next_clone); + gcc_assert(NULL == old_version->prev_clone); + /* gcc_assert(NULL == old_version->master_clone);*/ + /* gcc_assert(NULL == old_version->call_site_has);*/ + /* gcc_assert(NULL == old_version->aux);*/ + gcc_assert(old_version->master_clone == old_version); + new_version->master_clone = new_version; + new_version->count = old_version->count; + /* filed: uid, order should be different */ + /* try to set the same pid */ + /* new_version->pid = old_version->pid;*/ + new_version->needed = old_version->needed; + new_version->abstract_and_needed = old_version->abstract_and_needed; + new_version->reachable = old_version->reachable; + new_version->lowered = old_version->lowered; + new_version->analyzed = old_version->analyzed; + new_version->output = old_version->output; + new_version->alias = old_version->alias; + gcc_assert(NULL_TREE== old_version->inline_decl); + + + /* Clone the old node callees. Recursive calls are + also cloned. */ + for (e = old_version->callees;e; e=e->next_callee) + { + /*new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->frequency, + e->loop_nest, true);*/ + new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->frequency, + e->loop_nest, false); + new_e->count = e->count; + } + /* Fix recursive calls. + If OLD_VERSION has a recursive call after the + previous edge cloning, the new version will have an edge + pointing to the old version, which is wrong; + Redirect it to point to the new version. */ + + /* Do not fix recursive calls, recursive call all go through the + original function */ + /* + for (e = new_version->callees ; e; e = next_callee) + { + next_callee = e->next_callee; + if (e->callee == old_version) + cgraph_redirect_edge_callee (e, new_version); + + if (!next_callee) + break; + } + */ + + /* do not redirect callers*/ + gcc_assert(NULL == redirect_callers); + for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++) + { + /* Redirect calls to the old version node to point to its new + version. */ + cgraph_redirect_edge_callee (e, new_version); + } + + return new_version; + } + + + /* Perform function versioning. Function versioning includes copying of the tree and a callgraph update (creating a new cgraph node and updating @@ -1668,6 +1949,58 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version, */ struct cgraph_node * +generic_cloning_cgraph_function_versioning (struct cgraph_node *old_version_node, + VEC(cgraph_edge_p,heap) *redirect_callers, + varray_type tree_map, + bitmap args_to_skip) +{ + tree old_decl = old_version_node->decl; + struct cgraph_node *new_version_node = NULL; + tree new_decl; + + + /* Make a new FUNCTION_DECL tree node for the + new version. */ + if (!args_to_skip) + new_decl = copy_node (old_decl); + else + new_decl = build_function_decl_skip_args (old_decl, args_to_skip); + + /* Create the new version's call-graph node. + and update the edges of the new node. */ + new_version_node = + generic_cloning_cgraph_copy_node_for_versioning (old_version_node, new_decl, + redirect_callers); + + /* Copy the OLD_VERSION_NODE function tree to the new version. */ + generic_cloning_tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip); + + /* Update the new version's properties. + Make The new version visible only within this translation unit. Make sure + that is not weak also. + ??? We cannot use COMDAT linkage because there is no + ABI support for this. */ +/* + DECL_EXTERNAL (new_version_node->decl) = 0; + DECL_ONE_ONLY (new_version_node->decl) = 0; + TREE_PUBLIC (new_version_node->decl) = 0; + DECL_COMDAT (new_version_node->decl) = 0; + DECL_WEAK (new_version_node->decl) = 0; + DECL_VIRTUAL_P (new_version_node->decl) = 0; + new_version_node->local.externally_visible = 0; + new_version_node->local.local = 1; + new_version_node->lowered = true; +*/ + /* Update the call_expr on the edges to call the new version node. */ +/* + update_call_expr (new_version_node); + + cgraph_call_function_insertion_hooks (new_version_node); +*/ + return new_version_node; +} + +struct cgraph_node * cgraph_function_versioning (struct cgraph_node *old_version_node, VEC(cgraph_edge_p,heap) *redirect_callers, VEC (ipa_replace_map_p,gc)* tree_map, diff --git a/gcc/common.opt b/gcc/common.opt index d2277759bac..8ccbec721a3 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -607,6 +607,14 @@ fici Common Report Var(flag_ici) Enables Interactive Compilation Interface +fapi-clone-functions +Common Report Var(flag_api_clone_functions) +Enable API function cloning + +fapi-instrument-functions +Common Report Var(flag_api_instrument_functions) +Enable API function/code instrumentation + ; Nonzero means ignore `#ident' directives. 0 means handle them. ; Generate position-independent code for executables if possible ; On SVR4 targets, it also controls whether or not to emit a diff --git a/gcc/events.c b/gcc/events.c index 600bdfd71fc..0ea1ffe2a54 100644 --- a/gcc/events.c +++ b/gcc/events.c @@ -32,16 +32,19 @@ along with GCC; see the file COPYING3. If not see #include "highlev-plugin-internal.h" /* Event structure. */ -struct hl_plugin_event { +struct hl_plugin_event +{ int event; /* Number of the event */ event_callback_t run; /* Callback function */ }; /* Parameter structure. */ -struct event_parameter { +struct event_parameter +{ const char *name; /* Name for the parameter */ void *value; /* Pointer to data */ + event_parameter_type type; /* Type enumeration of value */ }; @@ -119,7 +122,20 @@ register_plugin_event (const char *event_name, event_callback_t func) ev->event = get_named_event_id (event_name, INSERT); ev->run = func; - register_callback ("unknown", ev->event, hash_param_callback, ev); + register_callback ("ICI", ev->event, hash_param_callback, ev); +} + + +/* Unregister an event. */ +void +unregister_plugin_event (const char *event_name) +{ + int event = get_named_event_id (event_name, NO_INSERT); + + if (event < 0) + return; + + unregister_callback ("ICI", event); } @@ -147,13 +163,15 @@ list_plugin_events (void) * Insert parameter into hash table. Allocates hash table * if needed. */ void -register_event_parameter (const char *name, void* value) +register_event_parameter (const char *name, void* value, + event_parameter_type type) { void **slot; struct event_parameter *param = XCNEW (struct event_parameter); param->name = name; param->value = value; + param->type = type; if (param->name == NULL || !strcmp ("", param->name)) internal_error ("Parameter cannot be registered with NULL name string\n"); @@ -187,7 +205,7 @@ unregister_event_parameter (const char *name) /* Get parameter pointer to data. - * Used by plugin to get pointer to data of the parameter . */ + Used by plugin to get pointer to data of the parameter . */ void * get_event_parameter (const char *name) { @@ -208,6 +226,27 @@ get_event_parameter (const char *name) return NULL; } +/* Get parameter type. + Used by plugin to get type of data pointer of the parameter . */ +event_parameter_type +get_event_parameter_type (const char *name) +{ + struct event_parameter tmp_param, *param; + tmp_param.name = name; + + if (parameters_hash == NULL) + internal_error ("Parameter hash not initialized.\n"); + + if (parameters_hash != NULL) + { + param = (struct event_parameter *) + htab_find (parameters_hash, &tmp_param); + + if (param != NULL && param->value != NULL) + return param->type; + } + return EP_VOID; +} /* Get list of names of all registered ICI parameters. * Traverse the hash table collecting the names of entries. diff --git a/gcc/feature-internal.h b/gcc/feature-internal.h index b2886c5ddd2..094c01fde85 100644 --- a/gcc/feature-internal.h +++ b/gcc/feature-internal.h @@ -55,11 +55,11 @@ extern void *set_subfeature (const char *feature_name, /* Extract the size of features and feature sets */ extern int get_feature_size (const char *feature_name); -extern int get_num_features (int type); -extern int get_num_subfeatures (const char *feat_name, int type); +extern int get_num_features (int type); /* not implemented */ +extern int get_num_subfeatures (const char *feat_name, int type); /* not implemented */ /* List of all known features, or all known elements of a feature set */ extern const char **list_features (void); -extern const char **list_subfeatures (const char *feat_name, int type); +extern const char **list_subfeatures (const char *feat_name, int type); /* not implemented */ #endif /* FEATURE_INTERNAL_H */ diff --git a/gcc/feature-list.c b/gcc/feature-list.c index 5e72d2f60ff..38479279451 100644 --- a/gcc/feature-list.c +++ b/gcc/feature-list.c @@ -53,6 +53,7 @@ extern int passes_by_id_size; current function is defined, and return NULL otherwise. */ static const void *function_name (void) { + /* DEBUG */ if (!cfun) printf("DEBUG: function_name: cfun is empty\n"); if (cfun) return (const void *) current_function_name(); else @@ -446,6 +447,22 @@ get_cfun_def_filename (void) return (const void *) LOCATION_FILE (cfun->function_start_locus); } +static const void * +is_cfun_artificial (void) +{ + if (!cfun) + return NULL; + + return (const void *) ((long int) DECL_ARTIFICIAL (cfun->decl)); +} + +static const void * +get_main_input_filename (void) +{ + /* GSoC: this is the path to main input file. */ + return (const void *) main_input_filename; +} + const struct feature feature_function_start = { "function_start_line", /* name */ NULL, /* data */ @@ -455,6 +472,24 @@ const struct feature feature_function_start = { NULL /* no set subfeature callback */ }; +const struct feature feature_function_is_artificial = { + "function_is_artificial", /* name */ + NULL, /* data */ + 0, /* no data */ + &is_cfun_artificial, /* callback */ + NULL, /* no get subfeature callback */ + NULL /* no set subfeature callback */ +}; + +const struct feature feature_main_input_filename = { + "main_input_filename", /* name */ + NULL, /* data */ + 0, /* no data */ + &get_main_input_filename, /* callback */ + NULL, /* no get subfeature callback */ + NULL /* no set subfeature callback */ +}; + const struct feature feature_function_end = { "function_end_line", /* name */ NULL, /* data */ @@ -491,7 +526,6 @@ const struct feature feature_function_decl_file = { NULL /* no set subfeature callback */ }; - /* FICI0: initialize all known features */ void init_features (void) { @@ -511,5 +545,7 @@ void init_features (void) register_feature (&feature_function_file); register_feature (&feature_function_decl_line); register_feature (&feature_function_decl_file); + register_feature (&feature_function_is_artificial); + register_feature (&feature_main_input_filename); } diff --git a/gcc/feature.h b/gcc/feature.h index d6b7282d630..5ba347b4a22 100644 --- a/gcc/feature.h +++ b/gcc/feature.h @@ -34,11 +34,11 @@ extern void *set_subfeature (const char *feature_name, /* Extract the size of features and feature sets */ extern int get_feature_size (const char *feature_name); -extern int get_num_features (int type); -extern int get_num_subfeatures (const char *feat_name, int type); +extern int get_num_features (int type); /* not implemented */ +extern int get_num_subfeatures (const char *feat_name, int type); /* not implemented */ /* List of all known features, or all known elements of a feature set */ extern const char **list_features (void); -extern const char **list_subfeatures (const char *feat_name, int type); +extern const char **list_subfeatures (const char *feat_name, int type); /* not implemented */ #endif /* FEATURE_H */ diff --git a/gcc/function-instrumentation.c b/gcc/function-instrumentation.c new file mode 100644 index 00000000000..0dab235c2b8 --- /dev/null +++ b/gcc/function-instrumentation.c @@ -0,0 +1,259 @@ +/* Function Instrumentation. + Copyright (C) 2009 by Cupertino Miranda, Shuangde Huang, Liang Peng + INRIA Futurs, France; ICT, China; + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "tm_p.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "output.h" +#include "diagnostic.h" +#include "tree-flow.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "timevar.h" +#include "cfgloop.h" +#include "flags.h" +#include "tree-inline.h" +#include "cgraph.h" +#include "ipa-prop.h" +#include "opts.h" +#include "tree-iterator.h" +#include "gimple.h" +#include "toplev.h" +#include "highlev-plugin-internal.h" + +/* Info for function instrumentation. */ +instrinfo idi; + +static bool +is_it_instrumentable (struct cgraph_node *cg_func) +{ + if (cgraph_function_body_availability (cg_func) == AVAIL_NOT_AVAILABLE) + return false; + else + return true; +} + +static void +freeinstrinfo (instrinfo idi) +{ + /* free instrumentation info. */ + int i; + for(i=0; idecl; + tree ftype = build_function_type (void_type_node, void_list_node); + tree func_decl = build_fn_decl (funname, ftype); + tree call_expr = build_function_call_expr (func_decl, NULL_TREE); + location = DECL_SOURCE_LOCATION(decl); + DECL_SOURCE_LOCATION(func_decl) = location; + + /*convert the call_expr (tree) to gimple_seq(gp_seq)*/ + gimplify_stmt(&call_expr, &gp_seq); + + func = DECL_STRUCT_FUNCTION (decl); + bb = ENTRY_BLOCK_PTR_FOR_FUNCTION (func)->next_bb; + gsi = gsi_start_bb(bb); + if(cloned == 1) + gsi_insert_seq_after (&gsi, gp_seq, GSI_NEW_STMT); + else + gsi_insert_seq_before (&gsi, gp_seq, GSI_NEW_STMT); + stmt = gsi_stmt (gsi); + gimple_set_location (stmt, location); + + cgraph_create_edge (cg_func, cgraph_node (func_decl), + stmt, + bb->count, CGRAPH_FREQ_MAX, bb->loop_depth); + +} + +static void +add_timer_end (struct cgraph_node *cg_func, char *funname) +{ + edge e; + edge_iterator ei; + gimple_stmt_iterator gsi; + struct function *func = NULL; + gimple stmt = NULL; + gimple_seq gp_seq = NULL; + tree decl, ftype, func_decl, call_expr; + location_t location; + + decl = cg_func->decl; + ftype = build_function_type (void_type_node, void_list_node); + func_decl = build_fn_decl (funname, ftype); + + func = DECL_STRUCT_FUNCTION (decl); + location = func->function_end_locus; + DECL_SOURCE_LOCATION(func_decl) = location; + + FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FUNCTION (func)->preds) + { + gsi = gsi_last_bb (e->src); + + call_expr = build_function_call_expr (func_decl, NULL_TREE); + gimplify_stmt(&call_expr, &gp_seq); + + if (gimple_code(gsi_stmt (gsi)) == GIMPLE_RETURN) + gsi_insert_seq_before (&gsi, gp_seq, GSI_NEW_STMT); + else + gsi_insert_seq_after (&gsi, gp_seq, GSI_NEW_STMT); + + stmt = gsi_stmt (gsi); + gimple_set_location (stmt, location); + gp_seq = NULL; + + cgraph_create_edge (cg_func, cgraph_node (func_decl), + stmt, + e->src->count, CGRAPH_FREQ_MAX, + e->src->loop_depth); + } +} + +static unsigned int +exec_instrument_functions (void) +{ + struct cgraph_node *node; + struct function *saved_cfun, *func; + int nid = -1; + char *beginname = NULL, *endname = NULL; + saved_cfun = cfun; + idi.numofinstrfun = 0; + + register_event_parameter("instr_info", &idi, EP_VOID); + call_plugin_event("load_instr_config"); + unregister_event_parameter("instr_info"); + + if (idi.numofinstrfun == 0) + { + fprintf (stderr, "No infomation for instrumentation pass\n"); + return 0; + } + + /* Perform instrument to functions. */ + for (node = cgraph_nodes; node; node = node->next) + { + const char *node_name = NULL; + const char *file_name = NULL; + func = DECL_STRUCT_FUNCTION (node->decl); + set_cfun (func); + node_name = IDENTIFIER_POINTER (DECL_NAME (node->decl)); + file_name = expand_location(DECL_SOURCE_LOCATION (node->decl)).file; + + if (is_in_instrument_list (node_name, file_name, &nid) + && is_it_instrumentable(node)) + { + int flag_cloned = 0; + if (*(idi.cloned + nid) == '1') + flag_cloned = 1; + if (getenv ("ICI_VERBOSE") != NULL) + printf("Now instrument function: %s \n", node_name); + beginname = (char *) alloca (strlen (node_name) + + strlen (*(idi.timer1 + nid))); + if ((*(idi.timer1 + nid))[0] == '_') + sprintf (beginname, "%s%s", node_name, *(idi.timer1 + nid)); + else + sprintf (beginname, "%s", *(idi.timer1 + nid)); + + add_timer_begin (node, beginname, flag_cloned); + + endname = (char *) alloca (strlen (node_name) + + strlen (*(idi.timer2 + nid))); + if ((*(idi.timer2 + nid))[0] == '_') + sprintf (endname, "%s%s", node_name, *(idi.timer2 + nid)); + else + sprintf (endname, "%s", *(idi.timer2 + nid)); + + add_timer_end (node, endname); + } + } + + set_cfun(saved_cfun); + freeinstrinfo (idi); + return 0; +} + +static bool +gate_instrument_functions(void) +{ + return flag_api_instrument_functions; +} + +struct gimple_opt_pass pass_instrument_functions = { + { + SIMPLE_IPA_PASS , + "instrumentation", /* name */ + gate_instrument_functions, /* gate */ + exec_instrument_functions, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_cgraph | TODO_verify_flow, /* todo_flags_finish */ + } +}; diff --git a/gcc/gcc-plugin.h b/gcc/gcc-plugin.h index a4ba6833a44..d3578afb4d7 100644 --- a/gcc/gcc-plugin.h +++ b/gcc/gcc-plugin.h @@ -48,10 +48,14 @@ enum plugin_event /* The following events might be subject to change or deletion without prior notice. Plugins should only use them by name. */ PLUGIN_FIRST_EXPERIMENTAL, - PLUGIN_UNROLL_FEATURE_CHANGE = PLUGIN_FIRST_EXPERIMENTAL, - PLUGIN_ALL_PASSES_MANAGER, + PLUGIN_UNROLL_PARAMETER_HANDLER = PLUGIN_FIRST_EXPERIMENTAL, + PLUGIN_ALL_PASSES_START, + PLUGIN_ALL_PASSES_EXECUTION, + PLUGIN_ALL_PASSES_END, PLUGIN_AVOID_GATE, PLUGIN_PASS_EXECUTION, + PLUGIN_EARLY_GIMPLE_PASSES_START, + PLUGIN_EARLY_GIMPLE_PASSES_END, PLUGIN_EVENT_LAST /* Dummy event used for indexing callback array. */ @@ -158,4 +162,6 @@ extern void register_callback (const char *plugin_name, plugin_callback_func callback, void *user_data); +extern void unregister_callback (const char *plugin_name, int event); + #endif /* GCC_PLUGIN_H */ diff --git a/gcc/gcc.c b/gcc/gcc.c index 6bc8e150a67..717f127e164 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -3479,7 +3479,70 @@ add_linker_option (const char *option, int len) linker_options [n_linker_options - 1] = save_string (option, len); } - + +static void +add_plugin_libs(int *argcp, const char *const **argvp) +{ + int argc = *argcp; + const char *const *argv = *argvp; + int spaces = 0, i = 0, nonspace_occurred = 0, arglen = 0; + int new_size = argc; + const char **new_argv; + const char *plugin_libs; + const char *sp, *sp_begin, *sp_end; + int length = 0; + GET_ENVIRONMENT(plugin_libs, "ICI_LIBS"); + if (plugin_libs != NULL) + { + length = strlen(plugin_libs); + /* Trim ici_libs string */ + for (sp = plugin_libs; *sp == ' '; sp++); + sp_begin = sp; + for (sp = plugin_libs + length - 1; (*sp == ' ') && (sp > sp_begin); sp--); + sp_end = sp + 1; + length = sp_end - sp_begin; + /* split ici_libs and save args in argvp*/ + if (length > 0) + { + for (sp = sp_begin; sp < sp_end; sp++) + { + if ((*sp == ' ') && nonspace_occurred) + { + spaces ++; + nonspace_occurred = 0; + } + if (*sp != ' ') + nonspace_occurred = 1; + } + new_size = new_size + spaces + 1; + /* Additional trailing slot is for NULL argument. */ + new_argv = XNEWVEC (const char *, new_size + 1); + i = 0; + do + { + new_argv[i] = argv[i]; + i++; + } + while (i < argc); + sp = sp_begin; + while (sp < sp_end) + { + arglen = 0; + for (; (*sp == ' ') && (sp < sp_end); sp++); + for(; (*(sp + arglen) != ' ') && ((sp + arglen) < sp_end); arglen++); + new_argv[i] = save_string(sp, arglen); + sp += arglen; + i++; + } + + /* The last one should be the NULL */ + new_argv[i] = NULL; + *argcp = new_size; + *argvp = new_argv; + } + } +} + /* Create the vector `switches' and its contents. Store its length in `n_switches'. */ @@ -3506,6 +3569,9 @@ process_command (int argc, const char **argv) n_infiles = 0; added_libraries = 0; + /* Add ICI_LIBS options */ + // add_plugin_libs(&argc, (const char *const **) &argv); + /* Figure compiler version from version string. */ compiler_version = temp1 = xstrdup (version_string); @@ -6783,6 +6849,10 @@ main (int argc, char **argv) struct user_specs *uptr; char **old_argv = argv; + /* Add ICI_LIBS options */ + add_plugin_libs(&argc, (const char *const **) &argv); + old_argv = argv; + /* Initialize here, not in definition. The IRIX 6 O32 cc sometimes chokes on ?: in file-scope variable initializations. */ asm_debug = ASM_DEBUG_SPEC; diff --git a/gcc/generic-cloning.c b/gcc/generic-cloning.c new file mode 100644 index 00000000000..fd3e4c6dca6 --- /dev/null +++ b/gcc/generic-cloning.c @@ -0,0 +1,637 @@ +/* Generic function cloning. + Copyright (C) 2009 by Cupertino Miranda, Shuangde Huang, Liang Peng + INRIA Futurs, France; ICT, China; + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +GCC is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING. If not, write to the Free +Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301, USA. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "rtl.h" +#include "tm_p.h" +#include "hard-reg-set.h" +#include "basic-block.h" +#include "output.h" +#include "diagnostic.h" +#include "tree-flow.h" +#include "tree-dump.h" +#include "tree-pass.h" +#include "timevar.h" +#include "cfgloop.h" +#include "flags.h" +#include "tree-inline.h" +#include "cgraph.h" +#include "ipa-prop.h" +#include "opts.h" +#include "tree-iterator.h" +#include "gimple.h" +#include "toplev.h" +#include "highlev-plugin-internal.h" + +/* Info for generic cloning. */ +cloneinfo cdi; + +static inline bool +is_it_main (struct cgraph_node *cg_func) +{ + if (!strcmp (IDENTIFIER_POINTER (DECL_NAME (cg_func->decl)), "MAIN__")) + return true; + if (!strcmp (IDENTIFIER_POINTER (DECL_NAME (cg_func->decl)), "main")) + return true; + + return false; +} + +static bool +has_variable_arguments(tree decl) +{ + return DECL_STRUCT_FUNCTION(decl)->stdarg == 1; +} + +static bool +is_it_clonable (struct cgraph_node *cg_func) +{ + tree decl = cg_func->decl; + + /* TODO Are this enough */ + if (cg_func->global.inlined_to) + return false; + + if (is_it_main (cg_func)) + return false; + + if (cgraph_function_body_availability (cg_func) == AVAIL_NOT_AVAILABLE) + return false; + + if (flags_from_decl_or_type (decl) & ECF_NORETURN) + return false; + + /* ??? Mismatch between the returned type and the result declaration. */ + if (TREE_TYPE (TREE_TYPE (decl)) != TREE_TYPE (DECL_RESULT (decl))) + return false; + + /* it has variable number of arguments */ + if (has_variable_arguments(decl)) + return false; + + return true; +} + +static void +freecloneinfo (cloneinfo cdi) +{ + /* free clone info. */ + int i,j; + int num = 3; + free (cdi.external_libraries); + for(i = 0, num = 0; idecl; + location = DECL_SOURCE_LOCATION(decl); + + /* Get arguments of original function. */ + func = DECL_STRUCT_FUNCTION (decl); + args = get_arguments (DECL_ARGUMENTS (decl)); + + first_bb = ENTRY_BLOCK_PTR_FOR_FUNCTION (func)->next_bb; + gsi = gsi_start_bb (first_bb); + entry = first_bb; + + /* build call to adaptation_function. */ + ftype = build_function_type (unsigned_type_node, void_list_node); + select_name = (char *) alloca + (strlen((IDENTIFIER_POINTER (DECL_NAME (decl)))) + + 64); + if ((*(cdi.adaptation_function + nid))[0] == '_') + sprintf (select_name, "%s%s",IDENTIFIER_POINTER (DECL_NAME (decl)), + *(cdi.adaptation_function + nid)); + else + sprintf (select_name, "%s", *(cdi.adaptation_function + nid)); + + select_func = build_fn_decl (select_name, ftype); + DECL_SOURCE_LOCATION(select_func) = location; + select_call = build_function_call_expr (select_func, NULL_TREE); + gp_select_expr = gimple_build_call_from_tree (select_call); + gimple_set_location (gp_select_expr, location); + + /* switch index. */ + index = create_tmp_var (unsigned_type_node, "index"); + gimple_call_set_lhs (gp_select_expr, index); + + DECL_EXTERNAL (select_func) = 1; + TREE_PUBLIC (select_func) = 1; + select_node = cgraph_node (select_func); + cgraph_mark_needed_node (select_node); + select_node->local.externally_visible = 1; + select_node->local.local = 0; + cgraph_create_edge (orig, select_node, gp_select_expr, + first_bb->count, + CGRAPH_FREQ_MAX, first_bb->loop_depth); + cgraph_mark_needed_node (select_node); + + /* default label of switch. */ + body_label = build3 (CASE_LABEL_EXPR, void_type_node, NULL_TREE, + NULL_TREE, create_artificial_label ()); + gp_body_label_expr = gimple_build_label(CASE_LABEL(body_label)); + gimple_set_location (gp_body_label_expr, location); + + /* switch label vector : labels. */ + labels = VEC_alloc (tree, heap, cdi.clones[nid]); + + gsi_insert_before (&gsi, gp_select_expr, GSI_SAME_STMT); + body_edge = split_block (entry, gp_select_expr); + body_block = body_edge->dest; + first_bb = body_edge->src; + entry->flags |= BB_REACHABLE; + body_block->flags |= BB_REACHABLE; + body_edge->flags |= EDGE_EXECUTABLE; + body_edge->flags &= ~EDGE_FALLTHRU; + + /* For each clone. */ + for(i = 0; i < cdi.clones[nid]; i++) + { + clone = NULL; + /*get the clone node from function name. */ + clone_fname = (char *) alloca + (strlen (IDENTIFIER_POINTER (DECL_NAME (decl))) + + strlen (*(cdi.clone_extension + nid)) + + 64); + sprintf (clone_fname, "%s%s_%d", + IDENTIFIER_POINTER (DECL_NAME (decl)), + *(cdi.clone_extension + nid), i); + + for(curr_node = cgraph_nodes; curr_node; + curr_node = curr_node->next) + { + if (strcmp (IDENTIFIER_POINTER (DECL_NAME (curr_node->decl)), + clone_fname) == 0) + { + clone = curr_node; + break; + } + } + + if (clone == NULL) + break; + + call_block = create_basic_block (NULL, NULL, first_bb); + + /* Set gsi to current block. */ + gsi = gsi_start_bb (call_block); + + /* build clone call expr, and return expr. */ + clone_call = build_function_call_expr (clone->decl, args); + gp_clone_expr = gimple_build_call_from_tree (clone_call); + gimple_set_location (gp_clone_expr, location); + + if(TREE_TYPE (TREE_TYPE (decl)) != void_type_node) + { + if (aggregate_value_p (DECL_RESULT(decl), decl) + && !is_gimple_reg_type (TREE_TYPE (DECL_RESULT (decl))) + && !DECL_NAME (DECL_RESULT(decl))) + return_var = DECL_RESULT (decl); + else + return_var = + create_tmp_var (TREE_TYPE (DECL_RESULT (decl)), "ret"); + DECL_CONTEXT (return_var) = decl; + gimple_call_set_lhs (gp_clone_expr, return_var); + gp_return_expr = gimple_build_return(return_var); + } + else + { + gp_return_expr = gimple_build_return(NULL); + } + + gimple_set_location (gp_return_expr, location); + + /* add switch label, + clone expr and return expr to current block. */ + call_label = build3 (CASE_LABEL_EXPR, void_type_node, + build_int_cst (integer_type_node, i+1), + NULL_TREE, create_artificial_label ()); + gp_call_label_expr = gimple_build_label(CASE_LABEL(call_label)); + gimple_set_location (gp_call_label_expr, location); + + gsi_insert_before (&gsi, gp_call_label_expr, GSI_SAME_STMT); + gsi_insert_before (&gsi, gp_clone_expr, GSI_SAME_STMT); + /* + SET_EXPR_LOCUS (clone_call, gimple_location_ptr (first_stmt (first_bb))); + TREE_BLOCK (clone_call) = TREE_BLOCK (select_call); */ + + gsi_insert_before (&gsi, gp_return_expr, GSI_SAME_STMT); + + VEC_safe_insert (tree, heap, labels, i, call_label); + + /* CGRAPH corrections. */ + + cge = cgraph_create_edge (orig, cgraph_node (clone->decl), + gp_clone_expr, + first_bb->count, CGRAPH_FREQ_MAX, + first_bb->loop_depth); + cge->inline_failed = "Clonned function"; + cgraph_mark_needed_node (cgraph_node (clone->decl)); + + /* CFG corrections. */ + call_block->flags |= BB_REACHABLE; + call_block->flags |= BB_DISABLE_SCHEDULE; + make_edge (entry, call_block, EDGE_EXECUTABLE); + make_edge (call_block, EXIT_BLOCK_PTR, 0); + update_stmt (gp_call_label_expr); + update_stmt (gp_clone_expr); + update_stmt (gp_return_expr); + } + + entry->flags |= BB_DISABLE_SCHEDULE; + body_block->flags |= BB_DISABLE_SCHEDULE; + + /* insert default label. */ + gsi = gsi_start_bb (body_block); + gsi_insert_before (&gsi, gp_body_label_expr, GSI_SAME_STMT); + + CASE_LOW (body_label) = NULL; + if (!VEC_empty (tree, labels)) + sort_case_labels (labels); + + gp_switch_expr = gimple_build_switch_vec (index, body_label, labels); + gimple_set_location (gp_switch_expr, location); + gsi = gsi_start_bb (first_bb); + gsi_insert_after (&gsi, gp_switch_expr, GSI_SAME_STMT); + + /* redirect incomming edges from entry to body_block. */ + removed = true; + while (removed) + { + removed = false; + FOR_EACH_EDGE (e, ei, entry->preds) + { + if (e->src != ENTRY_BLOCK_PTR_FOR_FUNCTION (func)) + { + make_edge (e->src, body_block, e->flags); + remove_edge (e); + removed = true; + break; + } + } + } + update_stmt (gp_select_expr); + update_stmt (gp_body_label_expr); + update_stmt (gp_switch_expr); +} + +static unsigned int +exec_clone_functions (void) +{ + struct cgraph_node *node; + struct function *saved_cfun, *func; + tree fundecl; + unsigned int argc; + char **argv; + int nid = -1, optid = -1; + int i; + int saved_flag_strict_aliasing, saved_flag_pcc_struct_return, + saved_flag_omit_frame_pointer, saved_flag_asynchronous_unwind_tables; + char *output_name = NULL; + + saved_cfun = cfun; + cdi.numofclonefun = 0; + + register_event_parameter("clone_info", &cdi, EP_VOID); + call_plugin_event("load_clone_config"); + unregister_event_parameter("clone_info"); + + if (cdi.numofclonefun == 0) + { + fprintf (stderr, "No infomation for function cloning pass\n"); + return 0; + } + + /* Perform cloning to functions */ + for (node = cgraph_nodes; node; node = node->next) + { + const char *node_name = NULL; + const char *file_name = NULL; + func = DECL_STRUCT_FUNCTION (node->decl); + set_cfun(func); + node_name = IDENTIFIER_POINTER (DECL_NAME (node->decl)); + file_name = expand_location(DECL_SOURCE_LOCATION (node->decl)).file; + /*is cfun in the clone_list*/ + if(is_in_clone_list(node_name, file_name, &nid)) + { + if (is_it_clonable (node)) + { + struct cgraph_node *clone_node = NULL; + if (getenv ("ICI_VERBOSE") != NULL) + printf("Now clone function: %s \n", node_name); + /* Do not clone virtual clone function. */ + for(i = 0; i < cdi.numofclonefun; i++) + { + if (!strcmp (node_name + (strlen (node_name) - + strlen (*(cdi.clone_extension + i))), + *(cdi.clone_extension + i))) + { + printf ("%s is a virtual clone function\n", + node_name); + break; + } + } + if( i < cdi.numofclonefun) + continue; + + DECL_UNINLINABLE(node->decl) = 1; + for(i = 0; i < cdi.clones[nid]; i++) + { + set_cfun(DECL_STRUCT_FUNCTION (node->decl)); + + clone_node = generic_cloning_cgraph_function_versioning (node, + NULL, + NULL, 0); + + DECL_POSSIBLY_INLINED(clone_node->decl) = 0; + + /* The cloned function should not be inlined. */ + clone_node->local.inlinable = false; + DECL_UNINLINABLE(clone_node->decl) = 1; + + /* Generate a new name for the new version. */ + output_name = + (char *) alloca (strlen + (IDENTIFIER_POINTER + (DECL_NAME (node->decl))) + + 64); + sprintf (output_name, "%s%s_%d", + IDENTIFIER_POINTER + (DECL_NAME (node->decl)), + *(cdi.clone_extension + nid), i); + + DECL_NAME (clone_node->decl) = + get_identifier (output_name); + + /* Create a new SYMBOL_REF rtx for the new name. */ + + if (DECL_RTL (node->decl) != NULL) + { + SET_DECL_RTL (clone_node->decl, + copy_rtx (DECL_RTL + (node->decl))); + XEXP (DECL_RTL (clone_node->decl), 0) = + gen_rtx_SYMBOL_REF( GET_MODE + (XEXP (DECL_RTL + (node->decl), 0)), + IDENTIFIER_POINTER + (DECL_NAME + (clone_node->decl))); + } + + DECL_STRUCT_FUNCTION (clone_node->decl) + ->function_start_locus = DECL_STRUCT_FUNCTION + (node->decl)->function_start_locus; + + fundecl = clone_node->decl; + if (TREE_CODE (fundecl) == FUNCTION_DECL + && find_clone_options (output_name, &optid)) + { + struct cl_optimization cur_opts; + tree old_opts = + DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fundecl); + + /* Save current options. */ + cl_optimization_save (&cur_opts); + + /* If we previously had some optimization options, + use them as the default. */ + if (old_opts) + cl_optimization_restore (TREE_OPTIMIZATION (old_opts)); + + /* Parse options, and update the vector. */ + argv = + parse_clone_option (*(cdi.clone_option + optid), &argc); + + /* may need to preserve more in the future, + reference OPTIMIZATION_OPTIONS and OVERRIDE_OPTIONS */ + saved_flag_strict_aliasing = flag_strict_aliasing; + saved_flag_omit_frame_pointer = flag_omit_frame_pointer; + saved_flag_pcc_struct_return = flag_pcc_struct_return; + saved_flag_asynchronous_unwind_tables = + flag_asynchronous_unwind_tables; + + decode_options (argc, (const char **) argv); + + /* Don't allow changing following flags. */ + flag_omit_frame_pointer = saved_flag_omit_frame_pointer; + flag_asynchronous_unwind_tables = + saved_flag_asynchronous_unwind_tables; + flag_pcc_struct_return = saved_flag_pcc_struct_return; + flag_strict_aliasing = saved_flag_strict_aliasing; + + DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fundecl) + = build_optimization_node (); + + /* Restore current options. */ + cl_optimization_restore (&cur_opts); + } + + } + add_call_to_clones (node, nid); + } + else + { + printf("%s (in the clone list) can not be cloned!\n", + node_name); + set_function_invalid(node_name); + } + } + } + + set_cfun(saved_cfun); + freecloneinfo (cdi); + return 0; +} + +static bool +gate_clone_functions(void) +{ + return flag_api_clone_functions; +} + +struct gimple_opt_pass pass_clone_functions = { + { + SIMPLE_IPA_PASS , + "generic_cloning", /* name */ + gate_clone_functions, /* gate */ + exec_clone_functions, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + 0, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_dump_cgraph | TODO_verify_flow, /* todo_flags_finish */ + } +}; + diff --git a/gcc/graphite.c b/gcc/graphite.c index 5e892635268..dbad693b3dc 100644 --- a/gcc/graphite.c +++ b/gcc/graphite.c @@ -56,6 +56,9 @@ along with GCC; see the file COPYING3. If not see #include "sese.h" #include "predict.h" +/* ICI internal interface. */ +#include "highlev-plugin-internal.h" + #ifdef HAVE_cloog #include "cloog/cloog.h" diff --git a/gcc/highlev-plugin-internal.h b/gcc/highlev-plugin-internal.h index 04337b462ba..c1ed13696e5 100644 --- a/gcc/highlev-plugin-internal.h +++ b/gcc/highlev-plugin-internal.h @@ -40,8 +40,24 @@ extern int call_plugin_event (const char *event_name); #define PLUGEVT_NO_SUCH_EVENT 2 #define PLUGEVT_NO_CALLBACK 3 +/* Datatype of event parameter structure. */ +typedef enum +{ + EP_SILENT, /* Used to pass infomation between ICI and plugin, + parameter of this type will not be recorded. + The data type is int. */ + EP_VOID, + EP_CHAR, + EP_UNSIGNED_CHAR, + EP_INT, + EP_UNSIGNED, + EP_LONG, + EP_UNSIGNED_LONG +} event_parameter_type; + /* Manipulation of event parameters (callback arguments) */ -extern void register_event_parameter (const char *name, void *param); +extern void register_event_parameter (const char *name, void *param, + event_parameter_type type); extern void unregister_event_parameter (const char *name); extern const char **list_event_parameters (void); extern void *get_event_parameter (const char *name); @@ -49,4 +65,25 @@ extern void *get_event_parameter (const char *name); /* ICI-specific, environment-based plugin loading */ extern void load_ici_plugin (void); +/* Info needed by adaptation. */ +typedef struct { + int numofclonefun; + char **clone_function_list; + char **function_filename_list; + int *clones; + char **clone_extension; + char **adaptation_function; + char **clone_option; + char *external_libraries; +} cloneinfo ; + +typedef struct { + int numofinstrfun; + char **instrument_function_list; + char **function_filename_list; + char **timer1; + char **timer2; + char *cloned; +} instrinfo ; + #endif /* HIGHLEV_PLUGIN_INTERNAL_H*/ diff --git a/gcc/highlev-plugin.h b/gcc/highlev-plugin.h index 4392d03c075..c3224604bed 100644 --- a/gcc/highlev-plugin.h +++ b/gcc/highlev-plugin.h @@ -28,6 +28,21 @@ along with GCC; see the file COPYING3. If not see /* Callback type for high-level argument-less event callbacks */ typedef void (*event_callback_t) (void); +/* Datatype of event parameter structure. */ +typedef enum +{ + EP_SILENT, /* Used to pass infomation between ICI and plugin, + parameter of this type will not be recorded. + The data type is int. */ + EP_VOID, + EP_CHAR, + EP_UNSIGNED_CHAR, + EP_INT, + EP_UNSIGNED, + EP_LONG, + EP_UNSIGNED_LONG +} event_parameter_type; + /* manipulation of event tables and callback lists */ extern void register_plugin_event (const char *name, event_callback_t func); extern void unregister_plugin_event (const char *name); @@ -39,7 +54,35 @@ extern const char **list_event_parameters (void); extern void *get_event_parameter (const char *name); /* pass management */ -extern void run_pass (char *pass_name); extern const char **list_passes (void); +extern void run_pass (const char *pass_name); +extern void run_ipa_pass (const char *pass_name); +extern void *initialize_ici_pass_list (int); +extern void insert_ici_pass_list (void *, int, const char *); +extern void run_ici_pass_list (void *); +extern void run_ici_pass_list_ipa_summary (void *); +extern void run_ici_pass_list_per_function (void *); +extern void delete_ici_pass_list (void *); + +/* Info needed by adaptation. */ +typedef struct { + int numofclonefun; + char **clone_function_list; + char **function_filename_list; + int *clones; + char **clone_extension; + char **adaptation_function; + char **clone_option; + char *external_libraries; +} cloneinfo ; + +typedef struct { + int numofinstrfun; + char **instrument_function_list; + char **function_filename_list; + char **timer1; + char **timer2; + char *cloned; +} instrinfo ; #endif /* HIGHLEV_PLUGIN_H */ diff --git a/gcc/ic-interface.h b/gcc/ic-interface.h index 4750645371f..6332b5a116e 100644 --- a/gcc/ic-interface.h +++ b/gcc/ic-interface.h @@ -30,4 +30,6 @@ along with GCC; see the file COPYING3. If not see #include "feature.h" #include "pass-manager.h" +/* XXX: Yuanjie: moved cloneinfo and instrinfo to highlev-plugin.h */ + #endif /* IC_INTERFACE_H */ diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c index bc7048f51c6..57d3791a833 100644 --- a/gcc/ipa-inline.c +++ b/gcc/ipa-inline.c @@ -1906,8 +1906,21 @@ analyze_function (struct cgraph_node *node) current_function_decl = node->decl; compute_inline_parameters (node); - if (flag_indirect_inlining) - inline_indirect_intraprocedural_analysis (node); +/* if (flag_indirect_inlining) + inline_indirect_intraprocedural_analysis (node); */ +/* we shoule not execute inline_indirect_intraprocedural_analysis() + if current_function_decl has function_specific_optimization, and + optimize is 0 or 1. Right? */ + if(DECL_FUNCTION_SPECIFIC_OPTIMIZATION(current_function_decl) + != NULL_TREE) + { + if(DECL_FUNCTION_SPECIFIC_OPTIMIZATION(current_function_decl) + ->optimization.opts.optimize > 1) + inline_indirect_intraprocedural_analysis (node); + } + else + if(flag_indirect_inlining) + inline_indirect_intraprocedural_analysis (node); current_function_decl = NULL; pop_cfun (); @@ -2003,7 +2016,7 @@ struct ipa_opt_pass_d pass_ipa_inline = { IPA_PASS, "inline", /* name */ - NULL, /* gate */ + NULL, /* gate */ cgraph_decide_inlining, /* execute */ NULL, /* sub */ NULL, /* next */ diff --git a/gcc/loop-init.c b/gcc/loop-init.c index a1a91639dc4..a0d4dd48fa9 100644 --- a/gcc/loop-init.c +++ b/gcc/loop-init.c @@ -33,6 +33,8 @@ along with GCC; see the file COPYING3. If not see #include "flags.h" #include "df.h" #include "ggc.h" +/* ICI internal interface */ +#include "highlev-plugin-internal.h" /* Initialize loop structures. This is used by the tree and RTL loop @@ -322,12 +324,29 @@ gate_rtl_unroll_and_peel_loops (void) static unsigned int rtl_unroll_and_peel_loops (void) { + static int ici_flag_peel_loops; + static int ici_flag_unroll_loops; + static int ici_flag_unroll_all_loops; + if (number_of_loops () > 1) { int flags = 0; if (dump_file) df_dump (dump_file); + register_event_parameter ("loop.flag_peel_loops", + &ici_flag_peel_loops, EP_INT); + register_event_parameter ("loop.flag_unroll_loops", + &ici_flag_unroll_loops, EP_INT); + register_event_parameter ("loop.flag_unroll_all_loops", + &ici_flag_unroll_all_loops, EP_INT); + + call_plugin_event ("unroll_parameter_handler"); + + unregister_event_parameter ("loop.flag_peel_loops"); + unregister_event_parameter ("loop.flag_unroll_loops"); + unregister_event_parameter ("loop.flag_unroll_all_loops"); + if (flag_peel_loops) flags |= UAP_PEEL; if (flag_unroll_loops) diff --git a/gcc/loop-unroll.c b/gcc/loop-unroll.c index 2b571ecf01a..b20ebda1f7a 100644 --- a/gcc/loop-unroll.c +++ b/gcc/loop-unroll.c @@ -171,23 +171,6 @@ unroll_and_peel_loops (int flags) FOR_EACH_LOOP (li, loop, LI_FROM_INNERMOST) { check = true; - { - /* Code for loop-unrolling ICI decision enabling. */ - invoke_plugin_va_callbacks - (PLUGIN_UNROLL_FEATURE_CHANGE, - "loop->num", &(loop->num), - "loop->ninsns", &(loop->ninsns), - "loop->av_ninsns", &(loop->av_ninsns), - "loop->lpt_decision.times", &(loop->lpt_decision.times), - "loop->lpt_decision.decision", &(loop->lpt_decision.decision), - "loop->lpt_decision.unroll_runtime", - (loop->lpt_decision.decision == LPT_UNROLL_RUNTIME - ? (void *) 1 : (void *) 0), - "loop->lpt_decision.unroll_constant", - (loop->lpt_decision.decision == LPT_UNROLL_CONSTANT - ? (void *) 1 : (void *) 0), - NULL); - } /* And perform the appropriate transformations. */ switch (loop->lpt_decision.decision) @@ -287,6 +270,8 @@ decide_unrolling_and_peeling (int flags) struct loop *loop; loop_iterator li; + /* ICI: parameter holders */ + /* Scan the loops, inner ones first. */ FOR_EACH_LOOP (li, loop, LI_FROM_INNERMOST) { @@ -295,44 +280,87 @@ decide_unrolling_and_peeling (int flags) if (dump_file) fprintf (dump_file, "\n;; *** Considering loop %d ***\n", loop->num); + /* Do not peel cold areas. */ if (optimize_loop_for_size_p (loop)) - { - if (dump_file) - fprintf (dump_file, ";; Not considering loop, cold area\n"); - continue; - } + { + static bool ici_optimize_loop_for_size_p; + ici_optimize_loop_for_size_p = 1; + invoke_plugin_va_callbacks + (PLUGIN_UNROLL_PARAMETER_HANDLER + "_loop.id", EP_INT, &(loop->num), + "loop.optimize_loop_for_size_p", EP_UNSIGNED_CHAR, + &ici_optimize_loop_for_size_p, NULL); + + if (ici_optimize_loop_for_size_p) + { + if (dump_file) + fprintf (dump_file, ";; Not considering loop, cold area\n"); + continue; + } + } /* Can the loop be manipulated? */ if (!can_duplicate_loop_p (loop)) { + static bool ici_can_duplicate_loop_p; + ici_can_duplicate_loop_p = 0; + invoke_plugin_va_callbacks + (PLUGIN_UNROLL_PARAMETER_HANDLER + "_loop.id", EP_INT, &(loop->num), + "_loop.can_duplicate_loop_p", EP_UNSIGNED_CHAR, + &ici_can_duplicate_loop_p, NULL); + if (dump_file) fprintf (dump_file, ";; Not considering loop, cannot duplicate\n"); - continue; + continue; } /* Skip non-innermost loops. */ if (loop->inner) { - if (dump_file) - fprintf (dump_file, ";; Not considering loop, is not innermost\n"); - continue; + static bool ici_is_inner_most; + ici_is_inner_most = 0; + invoke_plugin_va_callbacks + (PLUGIN_UNROLL_PARAMETER_HANDLER + "_loop.id", EP_INT, &(loop->num), + "_loop.is_inner_most", EP_UNSIGNED_CHAR, &ici_is_inner_most, + NULL); + + if (dump_file) + fprintf (dump_file, + ";; Not considering loop, is not innermost\n"); + continue; } loop->ninsns = num_loop_insns (loop); loop->av_ninsns = average_num_loop_insns (loop); /* Try transformations one by one in decreasing order of - priority. */ + priority. */ decide_unroll_constant_iterations (loop, flags); if (loop->lpt_decision.decision == LPT_NONE) - decide_unroll_runtime_iterations (loop, flags); + decide_unroll_runtime_iterations (loop, flags); if (loop->lpt_decision.decision == LPT_NONE) - decide_unroll_stupid (loop, flags); + decide_unroll_stupid (loop, flags); if (loop->lpt_decision.decision == LPT_NONE) - decide_peel_simple (loop, flags); + decide_peel_simple (loop, flags); + + invoke_plugin_va_callbacks + (PLUGIN_UNROLL_PARAMETER_HANDLER + "_loop.id", EP_INT, &(loop->num), + /* ICI: Number of loop insns. */ + "_loop.ninsns", EP_UNSIGNED, &(loop->ninsns), + /* ICI: Average number of executed insns per iteration. */ + "_loop.av_ninsns", EP_UNSIGNED, &(loop->av_ninsns), + /* ICI: Number of blocks contained within the loop. */ + "_loop.num_nodes", EP_UNSIGNED, &(loop->num_nodes), + /* ICI: Loop unrolling/peeling decision. */ + "loop.lpt_decision.times", EP_UNSIGNED, &(loop->lpt_decision.times), + "loop.lpt_decision.decision", EP_INT, /* Enum treated as int. */ + &(loop->lpt_decision.decision), NULL); } } diff --git a/gcc/pass-manager.c b/gcc/pass-manager.c index 4643ca9286b..03a47d73134 100644 --- a/gcc/pass-manager.c +++ b/gcc/pass-manager.c @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "highlev-plugin-internal.h" #include "feature-internal.h" #include "pass-manager.h" +#include "tree.h" static htab_t passes_hash = NULL; @@ -93,7 +94,13 @@ const char **list_passes (void) /* Insert a pass mapped by is pass_name in hash table. * Allocate hash table when used for the first time. */ -void register_pass_name (struct opt_pass *pass) + +/* XXX: Passes registered in this way always pointes to the + first insitance of the pass, with static_pass_number equals -1 + and (pass->todo_flags_start & TODO_mark_first_instance) = true + if passes_htab_hash generate hash with pass->name . */ + +void register_pass_by_name (struct opt_pass *pass) { void **slot; @@ -115,9 +122,9 @@ void register_pass_name (struct opt_pass *pass) *slot = pass; } - -/* Executes a pass, from any plugin, by its pass_name. */ -void run_pass (char *pass_name) +/* Executes a pass, from any plugin, by its pass_name. + IPA pass execute in this way does not generate summary. */ +void run_pass (const char *pass_name) { struct opt_pass tmp_pass; struct opt_pass *pass; @@ -128,3 +135,105 @@ void run_pass (char *pass_name) execute_one_pass (pass); } + +/* Executes an IPA pass with its summary generated before execution. + Take care while run IPA passes this way because GCC IPA summaries are + supposed to be generated for a whole list of IPA passes at one time. */ +void run_ipa_pass (const char *pass_name) +{ + struct opt_pass tmp_pass; + struct opt_pass *pass; + + tmp_pass.name = pass_name; + pass = (struct opt_pass *) htab_find (passes_hash, &tmp_pass); + + execute_one_ipa_summary_pass (pass); + execute_one_pass (pass); +} + +/* Initialize a list of pointers to pass with fixed size. */ +void * +initialize_ici_pass_list (int num) +{ + int i; + struct opt_pass **list; + list = (struct opt_pass **) xmalloc (sizeof (struct opt_pass *) * (num + 1)); + for (i = 0; i <= num; ++i) + *(list + i) = NULL; + return ((void *) list); +} + +/* Add pass by name to a list of pointers to pass at position indexed + as cursor. */ +void +insert_ici_pass_list (void *list, int cursor, const char *pass_name) +{ + struct opt_pass tmp_pass; + struct opt_pass *pass; + + tmp_pass.name = pass_name; + + pass = (struct opt_pass *) htab_find (passes_hash, &tmp_pass); + + if (*(((struct opt_pass **)list) + cursor) != NULL) + error ("Could not add pass to ICI pass list."); + + *(((struct opt_pass **)list) + cursor) = pass; +} + +/* Execute IPA summary passes. */ +void +run_ici_pass_list_ipa_summary (void *list) +{ + struct opt_pass **pass; + + if (list == NULL) + return; + + pass = (struct opt_pass **) list; + do + { + execute_one_ipa_summary_pass (*pass); + ++pass; + } + while (*pass); +} + +/* Execute passes in the list one by one on current function. */ +void +run_ici_pass_list (void *list) +{ + struct opt_pass **pass; + + if (list == NULL) + return; + + pass = (struct opt_pass **) list; + do + { + execute_one_pass (*pass); + ++pass; + } + while (*pass); +} + +/* Executes passes in the list on cgraph functions, one function a time + in top order. */ +void +run_ici_pass_list_per_function (void *list) +{ + do_per_function_toporder ((void (*)(void *)) run_ici_pass_list, + list); + } + +/* Delete the the list of pointers to pass. */ +void +delete_ici_pass_list (void *list) +{ + if (list == NULL) + return; + + free ((struct opt_pass **) list); + list = NULL; +} + diff --git a/gcc/pass-manager.h b/gcc/pass-manager.h index 1240ba51c0d..64c9072f3bf 100644 --- a/gcc/pass-manager.h +++ b/gcc/pass-manager.h @@ -31,8 +31,16 @@ along with GCC; see the file COPYING3. If not see typedef struct opt_pass opt_pass; extern const char **list_passes (void); -extern void register_pass_name (struct opt_pass *pass); -extern int unregister_pass_name (char *pass_name); -extern void run_pass (char *pass_name); +extern void register_pass_by_name (struct opt_pass *pass); +extern int unregister_pass_by_name (const char *pass_name); /* not implemented */ +extern void run_pass (const char *pass_name); +extern void run_ipa_pass (const char *pass_name); + +extern void *initialize_ici_pass_list (int); +extern void insert_ici_pass_list (void *, int, const char *); +extern void run_ici_pass_list (void *); +extern void run_ici_pass_list_ipa_summary (void *); +extern void run_ici_pass_list_per_function (void *); +extern void delete_ici_pass_list (void *); #endif /* PASS_MANAGER_H */ diff --git a/gcc/passes.c b/gcc/passes.c index 083b4e083b6..2b29bfa8691 100644 --- a/gcc/passes.c +++ b/gcc/passes.c @@ -496,7 +496,7 @@ make_pass_instance (struct opt_pass *pass, bool track_duplicates) pass->static_pass_number = -1; /* Inserts pass in ICI pass list. */ - register_pass_name (pass); + register_pass_by_name (pass); } return pass; } @@ -734,6 +734,7 @@ init_optimization_passes (void) /* Interprocedural optimization passes. */ p = &all_small_ipa_passes; NEXT_PASS (pass_ipa_function_and_variable_visibility); + NEXT_PASS (pass_clone_functions); NEXT_PASS (pass_ipa_early_inline); { struct opt_pass **p = &pass_ipa_early_inline.pass.sub; @@ -742,6 +743,7 @@ init_optimization_passes (void) NEXT_PASS (pass_rebuild_cgraph_edges); } NEXT_PASS (pass_ipa_free_lang_data); + NEXT_PASS (pass_instrument_functions); NEXT_PASS (pass_early_local_passes); { struct opt_pass **p = &pass_early_local_passes.pass.sub; @@ -1110,7 +1112,10 @@ static GTY ((length ("nnodes"))) struct cgraph_node **order; function CALLBACK for every function in the call graph. Otherwise, call CALLBACK on the current function. */ -static void +/* ICI: ipa pass manager needs to walt through cgraph with this function. + static void */ + +void do_per_function_toporder (void (*callback) (void *data), void *data) { int i; @@ -1133,9 +1138,14 @@ do_per_function_toporder (void (*callback) (void *data), void *data) node->process = 0; if (node->analyzed) { + int *bypass + = (int *) get_event_parameter ("bypass_gimple_in_ipa"); push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function_decl = node->decl; callback (data); + /* Reset bypass_gimple_in_ipa to stop recording GIMPLE passes*/ + if (bypass) + *bypass = 1; free_dominance_info (CDI_DOMINATORS); free_dominance_info (CDI_POST_DOMINATORS); current_function_decl = NULL; @@ -1522,7 +1532,9 @@ execute_one_pass (struct opt_pass *pass) User controls the value of the gate through the parameter "gate_status". */ gate_status = (pass->gate == NULL) ? true : pass->gate(); - invoke_plugin_va_callbacks (PLUGIN_AVOID_GATE, "gate_status", &gate_status); + /* Override gate with plugin. */ + invoke_plugin_va_callbacks (PLUGIN_AVOID_GATE, + "gate_status", EP_INT, &gate_status); if (!gate_status) { current_pass = NULL; @@ -1532,7 +1544,8 @@ execute_one_pass (struct opt_pass *pass) /* Pass execution event trigger: useful to identify passes being executed. Pass name is accessible as a feature (it is a constant object in GCC.) */ - invoke_plugin_va_callbacks (PLUGIN_PASS_EXECUTION); + invoke_plugin_va_callbacks (PLUGIN_PASS_EXECUTION, + "_pass_type", EP_INT, &(pass->type)); if (!quiet_flag && !cfun) fprintf (stderr, " <%s>", pass->name ? pass->name : ""); @@ -1773,8 +1786,12 @@ execute_ipa_pass_list (struct opt_pass *pass) if (execute_one_pass (pass) && pass->sub) { if (pass->sub->type == GIMPLE_PASS) - do_per_function_toporder ((void (*)(void *))execute_pass_list, - pass->sub); + { + invoke_plugin_callbacks (PLUGIN_EARLY_GIMPLE_PASSES_START, NULL); + do_per_function_toporder ((void (*)(void *))execute_pass_list, + pass->sub); + invoke_plugin_callbacks (PLUGIN_EARLY_GIMPLE_PASSES_END, NULL); + } else if (pass->sub->type == SIMPLE_IPA_PASS || pass->sub->type == IPA_PASS) execute_ipa_pass_list (pass->sub); @@ -1788,6 +1805,27 @@ execute_ipa_pass_list (struct opt_pass *pass) while (pass); } +/* ICI: Execute one IPA summary pass */ +bool +execute_one_ipa_summary_pass (struct opt_pass *pass) +{ + gcc_assert (!current_function_decl); + gcc_assert (!cfun); + gcc_assert (pass->type == SIMPLE_IPA_PASS || pass->type == IPA_PASS); + if (!quiet_flag && !cfun) + fprintf (stderr, " "); + + /* Generate summary as execute_ipa_summary_passes, + but only for current pass. */ + if (pass->type == IPA_PASS + && (!pass->gate || pass->gate ())) + { + pass_init_dump_file (pass); + ((struct ipa_opt_pass *)pass)->generate_summary (); + pass_fini_dump_file (pass); + } +} + extern void debug_properties (unsigned int); extern void dump_properties (FILE *, unsigned int); diff --git a/gcc/plugin-loader.c b/gcc/plugin-loader.c index c15f8e0b490..a698755c9e5 100644 --- a/gcc/plugin-loader.c +++ b/gcc/plugin-loader.c @@ -47,11 +47,9 @@ typedef union { plugin_callback_func callback_ptr; } dl_symbol_t ; - /* pointers to initialization/cleanup functions */ start_stop_func ici_plugin_start = NULL; - /* report any errors encountered during a dl* operation */ static inline bool check_for_dlerror (void) { diff --git a/gcc/plugin.c b/gcc/plugin.c index 18d27d5e81f..4472e05f893 100644 --- a/gcc/plugin.c +++ b/gcc/plugin.c @@ -63,11 +63,15 @@ static const char *plugin_event_name_init[] = "PLUGIN_ATTRIBUTES", "PLUGIN_START_UNIT", "PLUGIN_PRAGMAS", - "PLUGIN_EVENT_LAST", - "unroll_feature_change", - "all_passes_manager", + "unroll_parameter_handler", + "all_passes_start", + "all_passes_execution", + "all_passes_end", "avoid_gate", "pass_execution", + "early_gimple_passes_start", + "early_gimple_passes_end", + "PLUGIN_EVENT_LAST", }; const char **plugin_event_name = plugin_event_name_init; @@ -404,8 +408,11 @@ register_callback (const char *plugin_name, case PLUGIN_EVENT_LAST: default: if (event < PLUGIN_FIRST_EXPERIMENTAL || event >= event_last) - error ("Unknown callback event registered by plugin %s", - plugin_name); + { + error ("Unknown callback event registered by plugin %s", + plugin_name); + return; + } /* Fall through. */ case PLUGIN_FINISH_TYPE: case PLUGIN_START_UNIT: @@ -436,6 +443,22 @@ register_callback (const char *plugin_name, } } +int +unregister_callback (const char *plugin_name, int event) +{ + struct callback_info *callback, **cbp; + + if (event >= event_last) + return PLUGEVT_NO_SUCH_EVENT; + + for (cbp = &plugin_callbacks[event]; callback = *cbp; cbp = &callback->next) + if (strcmp (callback->plugin_name, plugin_name) == 0) + { + *cbp = callback->next; + return PLUGEVT_SUCCESS; + } + return PLUGEVT_NO_CALLBACK; +} /* Called from inside GCC. Invoke all plug-in callbacks registered with the specified event. diff --git a/gcc/toplev.c b/gcc/toplev.c index 8686e4dd947..6d07f034704 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1771,6 +1771,8 @@ init_alignments (void) static void process_options (void) { + /* ??? will this function be called more than once */ + static bool first_time_p = true; /* Just in case lang_hooks.post_options ends up calling a debug_hook. This can happen with incorrect pre-processed input. */ debug_hooks = &do_nothing_debug_hooks; @@ -2137,6 +2139,18 @@ process_options (void) "for correctness"); flag_omit_frame_pointer = 0; } + + /* ??? some flags may have changed, so save the optimization_default_node again. + We may need to remove the corresponding code in decode_options() */ + if (first_time_p) + { + + optimization_default_node = build_optimization_node (); + optimization_current_node = optimization_default_node; + + first_time_p = false; + } + } /* This function can be called multiple times to reinitialize the compiler @@ -2176,7 +2190,7 @@ backend_init_target (void) /* We may need to recompute regno_save_code[] and regno_restore_code[] after a mode change as well. */ - if (flag_caller_saves) + /*if (flag_caller_saves)*/ init_caller_save (); expand_dummy_function_end (); } diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index f0ed4ba73a7..4d854fdd1b6 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -132,6 +132,12 @@ static tree copy_result_decl_to_var (tree, copy_body_data *); static tree copy_decl_maybe_to_var (tree, copy_body_data *); static gimple remap_gimple_stmt (gimple, copy_body_data *); static bool delete_unreachable_blocks_update_callgraph (copy_body_data *id); +tree generic_cloning_remap_decl (tree decl, copy_body_data *id); +tree generic_cloning_copy_tree_body_r (tree *tp, int *walk_subtrees, + void *data); +tree generic_cloning_remap_type (tree type, copy_body_data *id); +tree generic_cloning_copy_tree_r (tree *tp, int *walk_subtrees, + void *data ATTRIBUTE_UNUSED); /* Insert a tree->tree mapping for ID. Despite the name suggests that the trees should be variables, it is used for more than that. */ @@ -329,6 +335,82 @@ remap_decl (tree decl, copy_body_data *id) return unshare_expr (*n); } + + +/* Remap DECL during the copying of the BLOCK tree for the function. */ + +tree +generic_cloning_remap_decl (tree decl, copy_body_data *id) +{ + tree *n; + tree fn; + + /* We only remap local variables in the current function. */ + fn = id->src_fn; + + /* See if we have remapped this declaration. */ + + n = (tree *) pointer_map_contains (id->decl_map, decl); + + /* If we didn't already have an equivalent for this declaration, + create one now. */ + if (!n) + { + /* Make a copy of the variable or label. */ + tree t = id->copy_decl (decl, id); + + /* Remember it, so that if we encounter this local entity again + we can reuse this copy. Do this early because remap_type may + need this decl for TYPE_STUB_DECL. */ + insert_decl_map (id, decl, t); + + if (!DECL_P (t)) + return t; + + /* Remap types, if necessary. */ + TREE_TYPE (t) = generic_cloning_remap_type (TREE_TYPE (t), id); + if (TREE_CODE (t) == TYPE_DECL) + DECL_ORIGINAL_TYPE (t) = generic_cloning_remap_type (DECL_ORIGINAL_TYPE (t), id); + + /* Remap sizes as necessary. */ + walk_tree (&DECL_SIZE (t), generic_cloning_copy_tree_body_r, id, NULL); + walk_tree (&DECL_SIZE_UNIT (t), generic_cloning_copy_tree_body_r, id, NULL); + + /* If fields, do likewise for offset and qualifier. */ + if (TREE_CODE (t) == FIELD_DECL) + { + walk_tree (&DECL_FIELD_OFFSET (t), generic_cloning_copy_tree_body_r, id, NULL); + if (TREE_CODE (DECL_CONTEXT (t)) == QUAL_UNION_TYPE) + walk_tree (&DECL_QUALIFIER (t), generic_cloning_copy_tree_body_r, id, NULL); + } + + /* we do cloning before ssa */ + gcc_assert(!(cfun && gimple_in_ssa_p(cfun))); + + if (cfun && gimple_in_ssa_p (cfun) + && (TREE_CODE (t) == VAR_DECL + || TREE_CODE (t) == RESULT_DECL || TREE_CODE (t) == PARM_DECL)) + { + tree def = gimple_default_def (id->src_cfun, decl); + get_var_ann (t); + if (TREE_CODE (decl) != PARM_DECL && def) + { + tree map = remap_ssa_name (def, id); + /* Watch out RESULT_DECLs whose SSA names map directly + to them. */ + if (TREE_CODE (map) == SSA_NAME + && gimple_nop_p (SSA_NAME_DEF_STMT (map))) + set_default_def (t, map); + } + add_referenced_var (t); + } + return t; + } + + return unshare_expr (*n); +} + + static tree remap_type_1 (tree type, copy_body_data *id) { @@ -444,6 +526,120 @@ remap_type_1 (tree type, copy_body_data *id) return new_tree; } + + + +static tree +generic_cloning_remap_type_1 (tree type, copy_body_data *id) +{ + tree new_tree, t; + + /* We do need a copy. build and register it now. If this is a pointer or + reference type, remap the designated type and make a new pointer or + reference type. */ + if (TREE_CODE (type) == POINTER_TYPE) + { + new_tree = build_pointer_type_for_mode (generic_cloning_remap_type (TREE_TYPE (type), id), + TYPE_MODE (type), + TYPE_REF_CAN_ALIAS_ALL (type)); + insert_decl_map (id, type, new_tree); + return new_tree; + } + else if (TREE_CODE (type) == REFERENCE_TYPE) + { + new_tree = build_reference_type_for_mode (generic_cloning_remap_type (TREE_TYPE (type), id), + TYPE_MODE (type), + TYPE_REF_CAN_ALIAS_ALL (type)); + insert_decl_map (id, type, new_tree); + return new_tree; + } + else + new_tree = copy_node (type); + + + insert_decl_map (id, type, new_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 (type); + if (type != t) + { + t = generic_cloning_remap_type (t, id); + TYPE_MAIN_VARIANT (new_tree) = t; + TYPE_NEXT_VARIANT (new_tree) = TYPE_NEXT_VARIANT (t); + TYPE_NEXT_VARIANT (t) = new_tree; + } + else + { + TYPE_MAIN_VARIANT (new_tree) = new_tree; + TYPE_NEXT_VARIANT (new_tree) = NULL; + } + + if (TYPE_STUB_DECL (type)) + TYPE_STUB_DECL (new_tree) = generic_cloning_remap_decl (TYPE_STUB_DECL (type), id); + + /* Lazily create pointer and reference types. */ + TYPE_POINTER_TO (new_tree) = NULL; + TYPE_REFERENCE_TO (new_tree) = NULL; + + switch (TREE_CODE (new_tree)) + { + case INTEGER_TYPE: + case REAL_TYPE: + case FIXED_POINT_TYPE: + case ENUMERAL_TYPE: + case BOOLEAN_TYPE: + + t = TYPE_MIN_VALUE (new_tree); + if (t && TREE_CODE (t) != INTEGER_CST) + walk_tree (&TYPE_MIN_VALUE (new_tree), generic_cloning_copy_tree_body_r, id, NULL); + + t = TYPE_MAX_VALUE (new_tree); + if (t && TREE_CODE (t) != INTEGER_CST) + walk_tree (&TYPE_MAX_VALUE (new_tree), generic_cloning_copy_tree_body_r, id, NULL); + return new_tree; + + case FUNCTION_TYPE: + TREE_TYPE (new_tree) = generic_cloning_remap_type (TREE_TYPE (new_tree), id); + walk_tree (&TYPE_ARG_TYPES (new_tree), generic_cloning_copy_tree_body_r, id, NULL); + return new_tree; + + case ARRAY_TYPE: + TREE_TYPE (new_tree) = generic_cloning_remap_type (TREE_TYPE (new_tree), id); + TYPE_DOMAIN (new_tree) = generic_cloning_remap_type (TYPE_DOMAIN (new_tree), id); + break; + + case RECORD_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + { + tree f, nf = NULL; + + for (f = TYPE_FIELDS (new_tree); f ; f = TREE_CHAIN (f)) + { + t = generic_cloning_remap_decl (f, id); + DECL_CONTEXT (t) = new_tree; + TREE_CHAIN (t) = nf; + nf = t; + } + TYPE_FIELDS (new_tree) = nreverse (nf); + } + break; + + case OFFSET_TYPE: + default: + /* Shouldn't have been thought variable sized. */ + gcc_unreachable (); + } + + walk_tree (&TYPE_SIZE (new_tree), generic_cloning_copy_tree_body_r, id, NULL); + walk_tree (&TYPE_SIZE_UNIT (new_tree), generic_cloning_copy_tree_body_r, id, NULL); + + return new_tree; +} + + + tree remap_type (tree type, copy_body_data *id) { @@ -472,6 +668,49 @@ remap_type (tree type, copy_body_data *id) return tmp; } +tree +generic_cloning_remap_type (tree type, copy_body_data *id) +{ + tree *node; + tree tmp; + + + + if (type == NULL) + return type; + + + if (TREE_CODE(type)==TYPE_DECL && type->decl_with_rtl.rtl != NULL_RTX) + printf("generic_cloning_remap_type: %s: type rtl not null!\n", IDENTIFIER_POINTER(DECL_NAME(current_function_decl))); + + /* See if we have remapped this type. */ + node = (tree *) pointer_map_contains (id->decl_map, type); + if (node) + return *node; + + /* The type only needs remapping if it's variably modified. */ + + if (! variably_modified_type_p (type, id->src_fn)) + { + insert_decl_map (id, type, type); + return type; + } +/* + if (TREE_CODE(type) == VOID_TYPE) + { + insert_decl_map (id, type, type); + return type; + } +*/ + id->remapping_type_depth++; + tmp = generic_cloning_remap_type_1 (type, id); + id->remapping_type_depth--; + + return tmp; +} + + + /* Return previously remapped type of TYPE in ID. Return NULL if TYPE is NULL or TYPE has not been remapped before. */ @@ -529,6 +768,43 @@ can_be_nonlocal (tree decl, copy_body_data *id) return true; } + +static bool +generic_cloning_can_be_nonlocal (tree decl, copy_body_data *id) +{ + /* We can not duplicate function decls. */ + if (TREE_CODE (decl) == FUNCTION_DECL) + return true; + + /* Local static vars must be non-local or we get multiple declaration + problems. */ + if (TREE_CODE (decl) == VAR_DECL + && !auto_var_in_fn_p (decl, id->src_fn)) + return true; + + /* At the moment dwarf2out can handle only these types of nodes. We + can support more later. */ + if (TREE_CODE (decl) != VAR_DECL && TREE_CODE (decl) != PARM_DECL) + return false; + + /* We must use global type. We call remapped_type instead of + remap_type since we don't want to remap this type here if it + hasn't been remapped before. */ + if (TREE_TYPE (decl) != remapped_type (TREE_TYPE (decl), id)) + return false; + + /* Wihtout SSA we can't tell if variable is used. */ + if (!gimple_in_ssa_p (cfun)) + return false; + + /* Live variables must be copied so we can attach DECL_RTL. */ + if (var_ann (decl)) + return false; + + return true; +} + + static tree remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id) { @@ -582,25 +858,82 @@ remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id) return nreverse (new_decls); } -/* Copy the BLOCK to contain remapped versions of the variables - therein. And hook the new block into the block-tree. */ -static void -remap_block (tree *block, copy_body_data *id) -{ - tree old_block; - tree new_block; - tree fn; - /* Make the new block. */ - old_block = *block; - new_block = make_node (BLOCK); - TREE_USED (new_block) = TREE_USED (old_block); - BLOCK_ABSTRACT_ORIGIN (new_block) = old_block; - BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block); - BLOCK_NONLOCALIZED_VARS (new_block) - = VEC_copy (tree, gc, BLOCK_NONLOCALIZED_VARS (old_block)); - *block = new_block; +static tree +generic_cloning_remap_decls (tree decls, VEC(tree,gc) **nonlocalized_list, copy_body_data *id) +{ + tree old_var; + tree new_decls = NULL_TREE; + + /* Remap its variables. */ + for (old_var = decls; old_var; old_var = TREE_CHAIN (old_var)) + { + tree new_var; + tree origin_var = DECL_ORIGIN (old_var); + + gcc_assert(origin_var == old_var); + + if (generic_cloning_can_be_nonlocal (old_var, id)) + { + if (TREE_CODE (old_var) == VAR_DECL + && (var_ann (old_var) || !gimple_in_ssa_p (cfun))) + cfun->local_decls = tree_cons (NULL_TREE, old_var, + cfun->local_decls); + if (debug_info_level > DINFO_LEVEL_TERSE + && !DECL_IGNORED_P (old_var) + && nonlocalized_list) + VEC_safe_push (tree, gc, *nonlocalized_list, origin_var); + continue; + } + + /* Remap the variable. */ + new_var = generic_cloning_remap_decl (old_var, id); + + /* If we didn't remap this variable, we can't mess with its + TREE_CHAIN. If we remapped this variable to the return slot, it's + already declared somewhere else, so don't declare it here. */ + + if (new_var == id->retvar) + ; + else if (!new_var) + { + if (debug_info_level > DINFO_LEVEL_TERSE + && !DECL_IGNORED_P (old_var) + && nonlocalized_list) + VEC_safe_push (tree, gc, *nonlocalized_list, origin_var); + } + else + { + gcc_assert (DECL_P (new_var)); + TREE_CHAIN (new_var) = new_decls; + new_decls = new_var; + } + } + + return nreverse (new_decls); +} + + +/* Copy the BLOCK to contain remapped versions of the variables + therein. And hook the new block into the block-tree. */ + +static void +remap_block (tree *block, copy_body_data *id) +{ + tree old_block; + tree new_block; + tree fn; + + /* Make the new block. */ + old_block = *block; + new_block = make_node (BLOCK); + TREE_USED (new_block) = TREE_USED (old_block); + BLOCK_ABSTRACT_ORIGIN (new_block) = old_block; + BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block); + BLOCK_NONLOCALIZED_VARS (new_block) + = VEC_copy (tree, gc, BLOCK_NONLOCALIZED_VARS (old_block)); + *block = new_block; /* Remap its variables. */ BLOCK_VARS (new_block) = remap_decls (BLOCK_VARS (old_block), @@ -616,6 +949,45 @@ remap_block (tree *block, copy_body_data *id) insert_decl_map (id, old_block, new_block); } + + +static void +generic_cloning_remap_block (tree *block, copy_body_data *id) +{ + tree old_block; + tree new_block; + tree fn; + + /* Make the new block. */ + old_block = *block; + new_block = make_node (BLOCK); + TREE_USED (new_block) = TREE_USED (old_block); + + gcc_assert(NULL_TREE == BLOCK_ABSTRACT_ORIGIN(old_block)); + + /*BLOCK_ABSTRACT_ORIGIN (new_block) = old_block;*/ + BLOCK_ABSTRACT_ORIGIN (new_block) = NULL_TREE; + + BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block); + BLOCK_NONLOCALIZED_VARS (new_block) + = VEC_copy (tree, gc, BLOCK_NONLOCALIZED_VARS (old_block)); + *block = new_block; + + /* Remap its variables. */ + BLOCK_VARS (new_block) = generic_cloning_remap_decls (BLOCK_VARS (old_block), + &BLOCK_NONLOCALIZED_VARS (new_block), + id); + + fn = id->dst_fn; + + if (id->transform_lang_insert_block) + id->transform_lang_insert_block (new_block); + + /* Remember the remapped block. */ + insert_decl_map (id, old_block, new_block); +} + + /* Copy the whole block tree and root it in id->block. */ static tree remap_blocks (tree block, copy_body_data *id) @@ -636,6 +1008,25 @@ remap_blocks (tree block, copy_body_data *id) return new_tree; } +static tree +generic_cloning_remap_blocks (tree block, copy_body_data *id) +{ + tree t; + tree new_tree = block; + + if (!block) + return NULL; + + generic_cloning_remap_block (&new_tree, id); + gcc_assert (new_tree != block); + for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t)) + prepend_lexical_block (new_tree, generic_cloning_remap_blocks (t, id)); + /* Blocks are in arbitrary order, but make things slightly prettier and do + not swap order when producing a copy. */ + BLOCK_SUBBLOCKS (new_tree) = blocks_nreverse (BLOCK_SUBBLOCKS (new_tree)); + return new_tree; +} + static void copy_statement_list (tree *tp) { @@ -676,6 +1067,25 @@ copy_bind_expr (tree *tp, int *walk_subtrees, copy_body_data *id) } +static void +generic_cloning_copy_bind_expr (tree *tp, int *walk_subtrees, copy_body_data *id) +{ + tree block = BIND_EXPR_BLOCK (*tp); + /* Copy (and replace) the statement. */ + generic_cloning_copy_tree_r (tp, walk_subtrees, NULL); + if (block) + { + generic_cloning_remap_block (&block, id); + BIND_EXPR_BLOCK (*tp) = block; + } + + if (BIND_EXPR_VARS (*tp)) + /* This will remap a lot of the same decls again, but this should be + harmless. */ + BIND_EXPR_VARS (*tp) = generic_cloning_remap_decls (BIND_EXPR_VARS (*tp), NULL, id); +} + + /* Create a new gimple_seq by remapping all the statements in BODY using the inlining information in ID. */ @@ -908,63 +1318,31 @@ remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data) } -/* Called from copy_body_id via walk_tree. DATA is really a - `copy_body_data *'. */ - -tree -copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) +static tree +generic_cloning_remap_gimple_op_r (tree *tp, int *walk_subtrees, void *data) { - copy_body_data *id = (copy_body_data *) data; + struct walk_stmt_info *wi_p = (struct walk_stmt_info *) data; + copy_body_data *id = (copy_body_data *) wi_p->info; tree fn = id->src_fn; - tree new_block; - - /* Begin by recognizing trees that we'll completely rewrite for the - inlining context. Our output for these trees is completely - different from out input (e.g. RETURN_EXPR is deleted, and morphs - into an edge). Further down, we'll handle trees that get - duplicated and/or tweaked. */ - - /* When requested, RETURN_EXPRs should be transformed to just the - contained MODIFY_EXPR. The branch semantics of the return will - be handled elsewhere by manipulating the CFG rather than a statement. */ - if (TREE_CODE (*tp) == RETURN_EXPR && id->transform_return_to_modify) - { - tree assignment = TREE_OPERAND (*tp, 0); - /* If we're returning something, just turn that into an - assignment into the equivalent of the original RESULT_DECL. - If the "assignment" is just the result decl, the result - decl has already been set (e.g. a recent "foo (&result_decl, - ...)"); just toss the entire RETURN_EXPR. */ - if (assignment && TREE_CODE (assignment) == MODIFY_EXPR) - { - /* Replace the RETURN_EXPR with (a copy of) the - MODIFY_EXPR hanging underneath. */ - *tp = copy_node (assignment); - } - else /* Else the RETURN_EXPR returns no value. */ - { - *tp = NULL; - return (tree) (void *)1; - } - } - else if (TREE_CODE (*tp) == SSA_NAME) + gcc_assert(TREE_CODE(*tp) != SSA_NAME); + if (TREE_CODE (*tp) == SSA_NAME) { *tp = remap_ssa_name (*tp, id); *walk_subtrees = 0; return NULL; } - - /* Local variables and labels need to be replaced by equivalent - variables. We don't want to copy static variables; there's only - one of those, no matter how many times we inline the containing - function. Similarly for globals from an outer function. */ else if (auto_var_in_fn_p (*tp, fn)) { + /* Local variables and labels need to be replaced by equivalent + variables. We don't want to copy static variables; there's + only one of those, no matter how many times we inline the + containing function. Similarly for globals from an outer + function. */ tree new_decl; /* Remap the declaration. */ - new_decl = remap_decl (*tp, id); + new_decl = generic_cloning_remap_decl (*tp, id); gcc_assert (new_decl); /* Replace this variable with the copy. */ STRIP_TYPE_NOPS (new_decl); @@ -972,26 +1350,22 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) *walk_subtrees = 0; } else if (TREE_CODE (*tp) == STATEMENT_LIST) - copy_statement_list (tp); - else if (TREE_CODE (*tp) == SAVE_EXPR - || TREE_CODE (*tp) == TARGET_EXPR) - remap_save_expr (tp, id->decl_map, walk_subtrees); + gcc_unreachable (); + else if (TREE_CODE (*tp) == SAVE_EXPR) + gcc_unreachable (); else if (TREE_CODE (*tp) == LABEL_DECL - && (! DECL_CONTEXT (*tp) + && (!DECL_CONTEXT (*tp) || decl_function_context (*tp) == id->src_fn)) /* These may need to be remapped for EH handling. */ - *tp = remap_decl (*tp, id); - else if (TREE_CODE (*tp) == BIND_EXPR) - copy_bind_expr (tp, walk_subtrees, id); - /* Types may need remapping as well. */ + *tp = generic_cloning_remap_decl (*tp, id); else if (TYPE_P (*tp)) - *tp = remap_type (*tp, id); - - /* If this is a constant, we have to copy the node iff the type will be - remapped. copy_tree_r will not copy a constant. */ + /* Types may need remapping as well. */ + *tp = generic_cloning_remap_type (*tp, id); else if (CONSTANT_CLASS_P (*tp)) { - tree new_type = remap_type (TREE_TYPE (*tp), id); + /* If this is a constant, we have to copy the node iff the type + will be remapped. copy_tree_r will not copy a constant. */ + tree new_type = generic_cloning_remap_type (TREE_TYPE (*tp), id); if (new_type == TREE_TYPE (*tp)) *walk_subtrees = 0; @@ -1005,37 +1379,11 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) TREE_TYPE (*tp) = new_type; } } - - /* Otherwise, just copy the node. Note that copy_tree_r already - knows not to copy VAR_DECLs, etc., so this is safe. */ else { - /* Here we handle trees that are not completely rewritten. - First we detect some inlining-induced bogosities for - discarding. */ - if (TREE_CODE (*tp) == MODIFY_EXPR - && TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1) - && (auto_var_in_fn_p (TREE_OPERAND (*tp, 0), fn))) - { - /* Some assignments VAR = VAR; don't generate any rtl code - and thus don't count as variable modification. Avoid - keeping bogosities like 0 = 0. */ - tree decl = TREE_OPERAND (*tp, 0), value; - tree *n; - - n = (tree *) pointer_map_contains (id->decl_map, decl); - if (n) - { - value = *n; - STRIP_TYPE_NOPS (value); - if (TREE_CONSTANT (value) || TREE_READONLY (value)) - { - *tp = build_empty_stmt (EXPR_LOCATION (*tp)); - return copy_tree_body_r (tp, walk_subtrees, data); - } - } - } - else if (TREE_CODE (*tp) == INDIRECT_REF) + /* Otherwise, just copy the node. Note that copy_tree_r already + knows not to copy VAR_DECLs, etc., so this is safe. */ + if (TREE_CODE (*tp) == INDIRECT_REF) { /* Get rid of *& from inline substitutions that can happen when a pointer argument is an ADDR_EXPR. */ @@ -1045,22 +1393,255 @@ copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) n = (tree *) pointer_map_contains (id->decl_map, decl); if (n) { - tree new_tree; - tree old; + tree type, new_tree, old; + /* If we happen to get an ADDR_EXPR in n->value, strip - it manually here as we'll eventually get ADDR_EXPRs + it manually here as we'll eventually get ADDR_EXPRs which lie about their types pointed to. In this case - build_fold_indirect_ref wouldn't strip the INDIRECT_REF, - but we absolutely rely on that. As fold_indirect_ref - does other useful transformations, try that first, though. */ - tree type = TREE_TYPE (TREE_TYPE (*n)); - if (id->do_not_unshare) - new_tree = *n; - else - new_tree = unshare_expr (*n); + build_fold_indirect_ref wouldn't strip the + INDIRECT_REF, but we absolutely rely on that. As + fold_indirect_ref does other useful transformations, + try that first, though. */ + type = TREE_TYPE (TREE_TYPE (*n)); + new_tree = unshare_expr (*n); old = *tp; *tp = gimple_fold_indirect_ref (new_tree); - if (! *tp) + if (!*tp) + { + if (TREE_CODE (new_tree) == ADDR_EXPR) + { + *tp = fold_indirect_ref_1 (type, new_tree); + /* ??? We should either assert here or build + a VIEW_CONVERT_EXPR instead of blindly leaking + incompatible types to our IL. */ + if (! *tp) + *tp = TREE_OPERAND (new_tree, 0); + } + else + { + *tp = build1 (INDIRECT_REF, type, new_tree); + TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old); + TREE_NO_WARNING (*tp) = TREE_NO_WARNING (old); + } + } + *walk_subtrees = 0; + return NULL; + } + } + + /* Here is the "usual case". Copy this tree node, and then + tweak some special cases. */ + generic_cloning_copy_tree_r (tp, walk_subtrees, NULL); + + /* Global variables we haven't seen yet need to go into referenced + vars. If not referenced from types only. */ + gcc_assert(!gimple_in_ssa_p(cfun)); + if (gimple_in_ssa_p (cfun) + && TREE_CODE (*tp) == VAR_DECL + && id->remapping_type_depth == 0) + add_referenced_var (*tp); + + /* We should never have TREE_BLOCK set on non-statements. */ + if (EXPR_P (*tp)) + gcc_assert (!TREE_BLOCK (*tp)); + + if (TREE_CODE (*tp) != OMP_CLAUSE) + TREE_TYPE (*tp) = generic_cloning_remap_type (TREE_TYPE (*tp), id); + + if (TREE_CODE (*tp) == TARGET_EXPR && TREE_OPERAND (*tp, 3)) + { + /* The copied TARGET_EXPR has never been expanded, even if the + original node was expanded already. */ + TREE_OPERAND (*tp, 1) = TREE_OPERAND (*tp, 3); + TREE_OPERAND (*tp, 3) = NULL_TREE; + } + else if (TREE_CODE (*tp) == ADDR_EXPR) + { + /* Variable substitution need not be simple. In particular, + the INDIRECT_REF substitution above. Make sure that + TREE_CONSTANT and friends are up-to-date. But make sure + to not improperly set TREE_BLOCK on some sub-expressions. */ + int invariant = is_gimple_min_invariant (*tp); + tree block = id->block; + id->block = NULL_TREE; + walk_tree (&TREE_OPERAND (*tp, 0), generic_cloning_copy_tree_body_r, id, NULL); + id->block = block; + + /* Handle the case where we substituted an INDIRECT_REF + into the operand of the ADDR_EXPR. */ + if (TREE_CODE (TREE_OPERAND (*tp, 0)) == INDIRECT_REF) + *tp = TREE_OPERAND (TREE_OPERAND (*tp, 0), 0); + else + recompute_tree_invariant_for_addr_expr (*tp); + + /* If this used to be invariant, but is not any longer, + then regimplification is probably needed. */ + if (invariant && !is_gimple_min_invariant (*tp)) + id->regimplify = true; + + *walk_subtrees = 0; + } + } + + /* Keep iterating. */ + return NULL_TREE; +} + + + +/* Called from copy_body_id via walk_tree. DATA is really a + `copy_body_data *'. */ + +tree +copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) +{ + copy_body_data *id = (copy_body_data *) data; + tree fn = id->src_fn; + tree new_block; + + /* Begin by recognizing trees that we'll completely rewrite for the + inlining context. Our output for these trees is completely + different from out input (e.g. RETURN_EXPR is deleted, and morphs + into an edge). Further down, we'll handle trees that get + duplicated and/or tweaked. */ + + /* When requested, RETURN_EXPRs should be transformed to just the + contained MODIFY_EXPR. The branch semantics of the return will + be handled elsewhere by manipulating the CFG rather than a statement. */ + if (TREE_CODE (*tp) == RETURN_EXPR && id->transform_return_to_modify) + { + tree assignment = TREE_OPERAND (*tp, 0); + + /* If we're returning something, just turn that into an + assignment into the equivalent of the original RESULT_DECL. + If the "assignment" is just the result decl, the result + decl has already been set (e.g. a recent "foo (&result_decl, + ...)"); just toss the entire RETURN_EXPR. */ + if (assignment && TREE_CODE (assignment) == MODIFY_EXPR) + { + /* Replace the RETURN_EXPR with (a copy of) the + MODIFY_EXPR hanging underneath. */ + *tp = copy_node (assignment); + } + else /* Else the RETURN_EXPR returns no value. */ + { + *tp = NULL; + return (tree) (void *)1; + } + } + else if (TREE_CODE (*tp) == SSA_NAME) + { + *tp = remap_ssa_name (*tp, id); + *walk_subtrees = 0; + return NULL; + } + + /* Local variables and labels need to be replaced by equivalent + variables. We don't want to copy static variables; there's only + one of those, no matter how many times we inline the containing + function. Similarly for globals from an outer function. */ + else if (auto_var_in_fn_p (*tp, fn)) + { + tree new_decl; + + /* Remap the declaration. */ + new_decl = remap_decl (*tp, id); + gcc_assert (new_decl); + /* Replace this variable with the copy. */ + STRIP_TYPE_NOPS (new_decl); + *tp = new_decl; + *walk_subtrees = 0; + } + else if (TREE_CODE (*tp) == STATEMENT_LIST) + copy_statement_list (tp); + else if (TREE_CODE (*tp) == SAVE_EXPR + || TREE_CODE (*tp) == TARGET_EXPR) + remap_save_expr (tp, id->decl_map, walk_subtrees); + else if (TREE_CODE (*tp) == LABEL_DECL + && (! DECL_CONTEXT (*tp) + || decl_function_context (*tp) == id->src_fn)) + /* These may need to be remapped for EH handling. */ + *tp = remap_decl (*tp, id); + else if (TREE_CODE (*tp) == BIND_EXPR) + copy_bind_expr (tp, walk_subtrees, id); + /* Types may need remapping as well. */ + else if (TYPE_P (*tp)) + *tp = remap_type (*tp, id); + + /* If this is a constant, we have to copy the node iff the type will be + remapped. copy_tree_r will not copy a constant. */ + else if (CONSTANT_CLASS_P (*tp)) + { + tree new_type = remap_type (TREE_TYPE (*tp), id); + + if (new_type == TREE_TYPE (*tp)) + *walk_subtrees = 0; + + else if (TREE_CODE (*tp) == INTEGER_CST) + *tp = build_int_cst_wide (new_type, TREE_INT_CST_LOW (*tp), + TREE_INT_CST_HIGH (*tp)); + else + { + *tp = copy_node (*tp); + TREE_TYPE (*tp) = new_type; + } + } + + /* Otherwise, just copy the node. Note that copy_tree_r already + knows not to copy VAR_DECLs, etc., so this is safe. */ + else + { + /* Here we handle trees that are not completely rewritten. + First we detect some inlining-induced bogosities for + discarding. */ + if (TREE_CODE (*tp) == MODIFY_EXPR + && TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1) + && (auto_var_in_fn_p (TREE_OPERAND (*tp, 0), fn))) + { + /* Some assignments VAR = VAR; don't generate any rtl code + and thus don't count as variable modification. Avoid + keeping bogosities like 0 = 0. */ + tree decl = TREE_OPERAND (*tp, 0), value; + tree *n; + + n = (tree *) pointer_map_contains (id->decl_map, decl); + if (n) + { + value = *n; + STRIP_TYPE_NOPS (value); + if (TREE_CONSTANT (value) || TREE_READONLY (value)) + { + *tp = build_empty_stmt (EXPR_LOCATION (*tp)); + return copy_tree_body_r (tp, walk_subtrees, data); + } + } + } + else if (TREE_CODE (*tp) == INDIRECT_REF) + { + /* Get rid of *& from inline substitutions that can happen when a + pointer argument is an ADDR_EXPR. */ + tree decl = TREE_OPERAND (*tp, 0); + tree *n; + + n = (tree *) pointer_map_contains (id->decl_map, decl); + if (n) + { + tree new_tree; + tree old; + /* If we happen to get an ADDR_EXPR in n->value, strip + it manually here as we'll eventually get ADDR_EXPRs + which lie about their types pointed to. In this case + build_fold_indirect_ref wouldn't strip the INDIRECT_REF, + but we absolutely rely on that. As fold_indirect_ref + does other useful transformations, try that first, though. */ + tree type = TREE_TYPE (TREE_TYPE (*n)); + if (id->do_not_unshare) + new_tree = *n; + else + new_tree = unshare_expr (*n); + old = *tp; + *tp = gimple_fold_indirect_ref (new_tree); + if (! *tp) { if (TREE_CODE (new_tree) == ADDR_EXPR) { @@ -1169,24 +1750,554 @@ remap_eh_region_nr (int old_nr, copy_body_data *id) return new_r->index; } -/* Similar, but operate on INTEGER_CSTs. */ +/* Similar, but operate on INTEGER_CSTs. */ + +static tree +remap_eh_region_tree_nr (tree old_t_nr, copy_body_data *id) +{ + int old_nr, new_nr; + + old_nr = tree_low_cst (old_t_nr, 0); + new_nr = remap_eh_region_nr (old_nr, id); + + return build_int_cst (NULL, new_nr); +} + + + +tree +generic_cloning_copy_tree_body_r (tree *tp, int *walk_subtrees, void *data) +{ + copy_body_data *id = (copy_body_data *) data; + tree fn = id->src_fn; + tree new_block; + + /* Begin by recognizing trees that we'll completely rewrite for the + inlining context. Our output for these trees is completely + different from out input (e.g. RETURN_EXPR is deleted, and morphs + into an edge). Further down, we'll handle trees that get + duplicated and/or tweaked. */ + + /* When requested, RETURN_EXPRs should be transformed to just the + contained MODIFY_EXPR. The branch semantics of the return will + be handled elsewhere by manipulating the CFG rather than a statement. */ + + gcc_assert(id->transform_return_to_modify == false); + gcc_assert(TREE_CODE(*tp) != SSA_NAME); + + if (TREE_CODE (*tp) == RETURN_EXPR && id->transform_return_to_modify) + { + tree assignment = TREE_OPERAND (*tp, 0); + + /* If we're returning something, just turn that into an + assignment into the equivalent of the original RESULT_DECL. + If the "assignment" is just the result decl, the result + decl has already been set (e.g. a recent "foo (&result_decl, + ...)"); just toss the entire RETURN_EXPR. */ + if (assignment && TREE_CODE (assignment) == MODIFY_EXPR) + { + /* Replace the RETURN_EXPR with (a copy of) the + MODIFY_EXPR hanging underneath. */ + *tp = copy_node (assignment); + } + else /* Else the RETURN_EXPR returns no value. */ + { + *tp = NULL; + return (tree) (void *)1; + } + } + else if (TREE_CODE (*tp) == SSA_NAME) + { + *tp = remap_ssa_name (*tp, id); + *walk_subtrees = 0; + return NULL; + } + + /* Local variables and labels need to be replaced by equivalent + variables. We don't want to copy static variables; there's only + one of those, no matter how many times we inline the containing + function. Similarly for globals from an outer function. */ + else if (auto_var_in_fn_p (*tp, fn)) + { + tree new_decl; + + /* Remap the declaration. */ + new_decl = generic_cloning_remap_decl (*tp, id); + gcc_assert (new_decl); + /* Replace this variable with the copy. */ + STRIP_TYPE_NOPS (new_decl); + *tp = new_decl; + *walk_subtrees = 0; + } + else if (TREE_CODE (*tp) == STATEMENT_LIST) + copy_statement_list (tp); + else if (TREE_CODE (*tp) == SAVE_EXPR) + remap_save_expr (tp, id->decl_map, walk_subtrees); + else if (TREE_CODE (*tp) == LABEL_DECL + && (! DECL_CONTEXT (*tp) + || decl_function_context (*tp) == id->src_fn)) + /* These may need to be remapped for EH handling. */ + *tp = generic_cloning_remap_decl (*tp, id); + else if (TREE_CODE (*tp) == BIND_EXPR) + generic_cloning_copy_bind_expr (tp, walk_subtrees, id); + /* Types may need remapping as well. */ + else if (TYPE_P (*tp)) + *tp = generic_cloning_remap_type (*tp, id); + + /* If this is a constant, we have to copy the node iff the type will be + remapped. copy_tree_r will not copy a constant. */ + else if (CONSTANT_CLASS_P (*tp)) + { + tree new_type = generic_cloning_remap_type (TREE_TYPE (*tp), id); + + if (new_type == TREE_TYPE (*tp)) + *walk_subtrees = 0; + + else if (TREE_CODE (*tp) == INTEGER_CST) + *tp = build_int_cst_wide (new_type, TREE_INT_CST_LOW (*tp), + TREE_INT_CST_HIGH (*tp)); + else + { + *tp = copy_node (*tp); + TREE_TYPE (*tp) = new_type; + } + } + + /* Otherwise, just copy the node. Note that copy_tree_r already + knows not to copy VAR_DECLs, etc., so this is safe. */ + else + { + /* Here we handle trees that are not completely rewritten. + First we detect some inlining-induced bogosities for + discarding. */ + if (TREE_CODE (*tp) == MODIFY_EXPR + && TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1) + && (auto_var_in_fn_p (TREE_OPERAND (*tp, 0), fn))) + { + /* Some assignments VAR = VAR; don't generate any rtl code + and thus don't count as variable modification. Avoid + keeping bogosities like 0 = 0. */ + tree decl = TREE_OPERAND (*tp, 0), value; + tree *n; + + n = (tree *) pointer_map_contains (id->decl_map, decl); + if (n) + { + value = *n; + STRIP_TYPE_NOPS (value); + if (TREE_CONSTANT (value) || TREE_READONLY (value)) + { + *tp = build_empty_stmt (); + return generic_cloning_copy_tree_body_r (tp, walk_subtrees, data); + } + } + } + else if (TREE_CODE (*tp) == INDIRECT_REF) + { + /* Get rid of *& from inline substitutions that can happen when a + pointer argument is an ADDR_EXPR. */ + tree decl = TREE_OPERAND (*tp, 0); + tree *n; + + n = (tree *) pointer_map_contains (id->decl_map, decl); + if (n) + { + tree new_tree; + tree old; + /* If we happen to get an ADDR_EXPR in n->value, strip + it manually here as we'll eventually get ADDR_EXPRs + which lie about their types pointed to. In this case + build_fold_indirect_ref wouldn't strip the INDIRECT_REF, + but we absolutely rely on that. As fold_indirect_ref + does other useful transformations, try that first, though. */ + tree type = TREE_TYPE (TREE_TYPE (*n)); + new_tree = unshare_expr (*n); + old = *tp; + *tp = gimple_fold_indirect_ref (new_tree); + if (! *tp) + { + if (TREE_CODE (new_tree) == ADDR_EXPR) + { + *tp = fold_indirect_ref_1 (type, new_tree); + /* ??? We should either assert here or build + a VIEW_CONVERT_EXPR instead of blindly leaking + incompatible types to our IL. */ + if (! *tp) + *tp = TREE_OPERAND (new_tree, 0); + } + else + { + *tp = build1 (INDIRECT_REF, type, new_tree); + TREE_THIS_VOLATILE (*tp) = TREE_THIS_VOLATILE (old); + TREE_SIDE_EFFECTS (*tp) = TREE_SIDE_EFFECTS (old); + } + } + *walk_subtrees = 0; + return NULL; + } + } + + /* Here is the "usual case". Copy this tree node, and then + tweak some special cases. */ + generic_cloning_copy_tree_r (tp, walk_subtrees, NULL); + + /* Global variables we haven't seen yet needs to go into referenced + vars. If not referenced from types only. */ + if (gimple_in_ssa_p (cfun) + && TREE_CODE (*tp) == VAR_DECL + && id->remapping_type_depth == 0) + add_referenced_var (*tp); + + /* If EXPR has block defined, map it to newly constructed block. + When inlining we want EXPRs without block appear in the block + of function call. */ + if (EXPR_P (*tp)) + { + new_block = id->block; + if (TREE_BLOCK (*tp)) + { + tree *n; + n = (tree *) pointer_map_contains (id->decl_map, + TREE_BLOCK (*tp)); + gcc_assert (n); + new_block = *n; + } + /*chenyang: if an expr has no block, we leave it like that*/ + /*TREE_BLOCK (*tp) = new_block;*/ + } + + if (TREE_CODE (*tp) == RESX_EXPR && id->eh_region_offset) + TREE_OPERAND (*tp, 0) = + build_int_cst (NULL_TREE, + id->eh_region_offset + + TREE_INT_CST_LOW (TREE_OPERAND (*tp, 0))); + + if (TREE_CODE (*tp) != OMP_CLAUSE) + TREE_TYPE (*tp) = generic_cloning_remap_type (TREE_TYPE (*tp), id); + + /* The copied TARGET_EXPR has never been expanded, even if the + original node was expanded already. */ + if (TREE_CODE (*tp) == TARGET_EXPR && TREE_OPERAND (*tp, 3)) + { + TREE_OPERAND (*tp, 1) = TREE_OPERAND (*tp, 3); + TREE_OPERAND (*tp, 3) = NULL_TREE; + } + + /* Variable substitution need not be simple. In particular, the + INDIRECT_REF substitution above. Make sure that TREE_CONSTANT + and friends are up-to-date. */ + else if (TREE_CODE (*tp) == ADDR_EXPR) + { + int invariant = is_gimple_min_invariant (*tp); + walk_tree (&TREE_OPERAND (*tp, 0), generic_cloning_copy_tree_body_r, id, NULL); + + /* Handle the case where we substituted an INDIRECT_REF + into the operand of the ADDR_EXPR. */ + if (TREE_CODE (TREE_OPERAND (*tp, 0)) == INDIRECT_REF) + *tp = TREE_OPERAND (TREE_OPERAND (*tp, 0), 0); + else + recompute_tree_invariant_for_addr_expr (*tp); + + /* If this used to be invariant, but is not any longer, + then regimplification is probably needed. */ + if (invariant && !is_gimple_min_invariant (*tp)) + id->regimplify = true; + + *walk_subtrees = 0; + } + } + + /* Keep iterating. */ + return NULL_TREE; +} + + + +/* Helper for copy_bb. Remap statement STMT using the inlining + information in ID. Return the new statement copy. */ + +static gimple +remap_gimple_stmt (gimple stmt, copy_body_data *id) +{ + gimple copy = NULL; + struct walk_stmt_info wi; + tree new_block; + bool skip_first = false; + + /* Begin by recognizing trees that we'll completely rewrite for the + inlining context. Our output for these trees is completely + different from out input (e.g. RETURN_EXPR is deleted, and morphs + into an edge). Further down, we'll handle trees that get + duplicated and/or tweaked. */ + + /* When requested, GIMPLE_RETURNs should be transformed to just the + contained GIMPLE_ASSIGN. The branch semantics of the return will + be handled elsewhere by manipulating the CFG rather than the + statement. */ + if (gimple_code (stmt) == GIMPLE_RETURN && id->transform_return_to_modify) + { + tree retval = gimple_return_retval (stmt); + + /* If we're returning something, just turn that into an + assignment into the equivalent of the original RESULT_DECL. + If RETVAL is just the result decl, the result decl has + already been set (e.g. a recent "foo (&result_decl, ...)"); + just toss the entire GIMPLE_RETURN. */ + if (retval && TREE_CODE (retval) != RESULT_DECL) + { + copy = gimple_build_assign (id->retvar, retval); + /* id->retvar is already substituted. Skip it on later remapping. */ + skip_first = true; + } + else + return gimple_build_nop (); + } + else if (gimple_has_substatements (stmt)) + { + gimple_seq s1, s2; + + /* When cloning bodies from the C++ front end, we will be handed bodies + in High GIMPLE form. Handle here all the High GIMPLE statements that + have embedded statements. */ + switch (gimple_code (stmt)) + { + case GIMPLE_BIND: + copy = copy_gimple_bind (stmt, id); + break; + + case GIMPLE_CATCH: + s1 = remap_gimple_seq (gimple_catch_handler (stmt), id); + copy = gimple_build_catch (gimple_catch_types (stmt), s1); + break; + + case GIMPLE_EH_FILTER: + s1 = remap_gimple_seq (gimple_eh_filter_failure (stmt), id); + copy = gimple_build_eh_filter (gimple_eh_filter_types (stmt), s1); + break; + + case GIMPLE_TRY: + s1 = remap_gimple_seq (gimple_try_eval (stmt), id); + s2 = remap_gimple_seq (gimple_try_cleanup (stmt), id); + copy = gimple_build_try (s1, s2, gimple_try_kind (stmt)); + break; + + case GIMPLE_WITH_CLEANUP_EXPR: + s1 = remap_gimple_seq (gimple_wce_cleanup (stmt), id); + copy = gimple_build_wce (s1); + break; + + case GIMPLE_OMP_PARALLEL: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy = gimple_build_omp_parallel + (s1, + gimple_omp_parallel_clauses (stmt), + gimple_omp_parallel_child_fn (stmt), + gimple_omp_parallel_data_arg (stmt)); + break; + + case GIMPLE_OMP_TASK: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy = gimple_build_omp_task + (s1, + gimple_omp_task_clauses (stmt), + gimple_omp_task_child_fn (stmt), + gimple_omp_task_data_arg (stmt), + gimple_omp_task_copy_fn (stmt), + gimple_omp_task_arg_size (stmt), + gimple_omp_task_arg_align (stmt)); + break; + + case GIMPLE_OMP_FOR: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + s2 = remap_gimple_seq (gimple_omp_for_pre_body (stmt), id); + copy = gimple_build_omp_for (s1, gimple_omp_for_clauses (stmt), + gimple_omp_for_collapse (stmt), s2); + { + size_t i; + for (i = 0; i < gimple_omp_for_collapse (stmt); i++) + { + gimple_omp_for_set_index (copy, i, + gimple_omp_for_index (stmt, i)); + gimple_omp_for_set_initial (copy, i, + gimple_omp_for_initial (stmt, i)); + gimple_omp_for_set_final (copy, i, + gimple_omp_for_final (stmt, i)); + gimple_omp_for_set_incr (copy, i, + gimple_omp_for_incr (stmt, i)); + gimple_omp_for_set_cond (copy, i, + gimple_omp_for_cond (stmt, i)); + } + } + break; + + case GIMPLE_OMP_MASTER: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy = gimple_build_omp_master (s1); + break; + + case GIMPLE_OMP_ORDERED: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy = gimple_build_omp_ordered (s1); + break; + + case GIMPLE_OMP_SECTION: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy = gimple_build_omp_section (s1); + break; + + case GIMPLE_OMP_SECTIONS: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy = gimple_build_omp_sections + (s1, gimple_omp_sections_clauses (stmt)); + break; + + case GIMPLE_OMP_SINGLE: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy = gimple_build_omp_single + (s1, gimple_omp_single_clauses (stmt)); + break; + + case GIMPLE_OMP_CRITICAL: + s1 = remap_gimple_seq (gimple_omp_body (stmt), id); + copy + = gimple_build_omp_critical (s1, gimple_omp_critical_name (stmt)); + break; + + default: + gcc_unreachable (); + } + } + else + { + if (gimple_assign_copy_p (stmt) + && gimple_assign_lhs (stmt) == gimple_assign_rhs1 (stmt) + && auto_var_in_fn_p (gimple_assign_lhs (stmt), id->src_fn)) + { + /* Here we handle statements that are not completely rewritten. + First we detect some inlining-induced bogosities for + discarding. */ + + /* Some assignments VAR = VAR; don't generate any rtl code + and thus don't count as variable modification. Avoid + keeping bogosities like 0 = 0. */ + tree decl = gimple_assign_lhs (stmt), value; + tree *n; + + n = (tree *) pointer_map_contains (id->decl_map, decl); + if (n) + { + value = *n; + STRIP_TYPE_NOPS (value); + if (TREE_CONSTANT (value) || TREE_READONLY (value)) + return gimple_build_nop (); + } + } + + if (gimple_debug_bind_p (stmt)) + { + copy = gimple_build_debug_bind (gimple_debug_bind_get_var (stmt), + gimple_debug_bind_get_value (stmt), + stmt); + VEC_safe_push (gimple, heap, id->debug_stmts, copy); + return copy; + } + + /* Create a new deep copy of the statement. */ + copy = gimple_copy (stmt); + + /* Remap the region numbers for __builtin_eh_{pointer,filter}, + RESX and EH_DISPATCH. */ + if (id->eh_map) + switch (gimple_code (copy)) + { + case GIMPLE_CALL: + { + tree r, fndecl = gimple_call_fndecl (copy); + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_EH_COPY_VALUES: + r = gimple_call_arg (copy, 1); + r = remap_eh_region_tree_nr (r, id); + gimple_call_set_arg (copy, 1, r); + /* FALLTHRU */ + + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_FILTER: + r = gimple_call_arg (copy, 0); + r = remap_eh_region_tree_nr (r, id); + gimple_call_set_arg (copy, 0, r); + break; + + default: + break; + } + } + break; + + case GIMPLE_RESX: + { + int r = gimple_resx_region (copy); + r = remap_eh_region_nr (r, id); + gimple_resx_set_region (copy, r); + } + break; + + case GIMPLE_EH_DISPATCH: + { + int r = gimple_eh_dispatch_region (copy); + r = remap_eh_region_nr (r, id); + gimple_eh_dispatch_set_region (copy, r); + } + break; + + default: + break; + } + } + + /* If STMT has a block defined, map it to the newly constructed + block. When inlining we want statements without a block to + appear in the block of the function call. */ + new_block = id->block; + if (gimple_block (copy)) + { + tree *n; + n = (tree *) pointer_map_contains (id->decl_map, gimple_block (copy)); + gcc_assert (n); + new_block = *n; + } + + gimple_set_block (copy, new_block); + + if (gimple_debug_bind_p (copy)) + return copy; + + /* Remap all the operands in COPY. */ + memset (&wi, 0, sizeof (wi)); + wi.info = id; + if (skip_first) + walk_tree (gimple_op_ptr (copy, 1), remap_gimple_op_r, &wi, NULL); + else + walk_gimple_op (copy, remap_gimple_op_r, &wi); + + /* Clear the copied virtual operands. We are not remapping them here + but are going to recreate them from scratch. */ + if (gimple_has_mem_ops (copy)) + { + gimple_set_vdef (copy, NULL_TREE); + gimple_set_vuse (copy, NULL_TREE); + } + + return copy; +} -static tree -remap_eh_region_tree_nr (tree old_t_nr, copy_body_data *id) -{ - int old_nr, new_nr; - old_nr = tree_low_cst (old_t_nr, 0); - new_nr = remap_eh_region_nr (old_nr, id); - return build_int_cst (NULL, new_nr); -} -/* Helper for copy_bb. Remap statement STMT using the inlining - information in ID. Return the new statement copy. */ static gimple -remap_gimple_stmt (gimple stmt, copy_body_data *id) +generic_cloning_remap_gimple_stmt (gimple stmt, copy_body_data *id) { gimple copy = NULL; struct walk_stmt_info wi; @@ -1203,8 +2314,12 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) contained GIMPLE_ASSIGN. The branch semantics of the return will be handled elsewhere by manipulating the CFG rather than the statement. */ + + gcc_assert(!id->transform_return_to_modify); + if (gimple_code (stmt) == GIMPLE_RETURN && id->transform_return_to_modify) { + tree retval = gimple_return_retval (stmt); /* If we're returning something, just turn that into an @@ -1225,6 +2340,9 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) { gimple_seq s1, s2; + /* XXX: deal with c++ */ + gcc_unreachable(); + /* When cloning bodies from the C++ front end, we will be handed bodies in High GIMPLE form. Handle here all the High GIMPLE statements that have embedded statements. */ @@ -1362,111 +2480,303 @@ remap_gimple_stmt (gimple stmt, copy_body_data *id) } } - if (gimple_debug_bind_p (stmt)) + /* Create a new deep copy of the statement. */ + copy = gimple_copy (stmt); + } + + /* If STMT has a block defined, map it to the newly constructed + block. When inlining we want statements without a block to + appear in the block of the function call. */ + /*new_block = id->block;*/ + if (gimple_block (copy)) + { + tree *n; + n = (tree *) pointer_map_contains (id->decl_map, gimple_block (copy)); + gcc_assert (n); + new_block = *n; + + gimple_set_block (copy, new_block); + } + + /* + gimple_set_block (copy, new_block); + */ + + /* Remap all the operands in COPY. */ + memset (&wi, 0, sizeof (wi)); + wi.info = id; + if (skip_first) + walk_tree (gimple_op_ptr (copy, 1), generic_cloning_remap_gimple_op_r, &wi, NULL); + else + walk_gimple_op (copy, generic_cloning_remap_gimple_op_r, &wi); + + /* We have to handle EH region remapping of GIMPLE_RESX specially because + the region number is not an operand. */ + if (gimple_code (stmt) == GIMPLE_RESX && id->eh_region_offset) + { + gimple_resx_set_region (copy, gimple_resx_region (stmt) + id->eh_region_offset); + } + return copy; +} + + +/* Copy basic block, scale profile accordingly. Edges will be taken care of + later */ + +static basic_block +copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, + gcov_type count_scale) +{ + gimple_stmt_iterator gsi, copy_gsi, seq_gsi; + basic_block copy_basic_block; + tree decl; + + /* create_basic_block() will append every new block to + basic_block_info automatically. */ + copy_basic_block = create_basic_block (NULL, (void *) 0, + (basic_block) bb->prev_bb->aux); + copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE; + + /* We are going to rebuild frequencies from scratch. These values + have just small importance to drive canonicalize_loop_headers. */ + copy_basic_block->frequency = ((gcov_type)bb->frequency + * frequency_scale / REG_BR_PROB_BASE); + + if (copy_basic_block->frequency > BB_FREQ_MAX) + copy_basic_block->frequency = BB_FREQ_MAX; + + copy_gsi = gsi_start_bb (copy_basic_block); + + for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + gimple orig_stmt = stmt; + + id->regimplify = false; + stmt = remap_gimple_stmt (stmt, id); + if (gimple_nop_p (stmt)) + continue; + + gimple_duplicate_stmt_histograms (cfun, stmt, id->src_cfun, orig_stmt); + seq_gsi = copy_gsi; + + /* With return slot optimization we can end up with + non-gimple (foo *)&this->m, fix that here. */ + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == NOP_EXPR + && !is_gimple_val (gimple_assign_rhs1 (stmt))) { - copy = gimple_build_debug_bind (gimple_debug_bind_get_var (stmt), - gimple_debug_bind_get_value (stmt), - stmt); - VEC_safe_push (gimple, heap, id->debug_stmts, copy); - return copy; + tree new_rhs; + new_rhs = force_gimple_operand_gsi (&seq_gsi, + gimple_assign_rhs1 (stmt), + true, NULL, false, GSI_NEW_STMT); + gimple_assign_set_rhs1 (stmt, new_rhs); + id->regimplify = false; } - /* Create a new deep copy of the statement. */ - copy = gimple_copy (stmt); + gsi_insert_after (&seq_gsi, stmt, GSI_NEW_STMT); - /* Remap the region numbers for __builtin_eh_{pointer,filter}, - RESX and EH_DISPATCH. */ - if (id->eh_map) - switch (gimple_code (copy)) - { - case GIMPLE_CALL: + if (id->regimplify) + gimple_regimplify_operands (stmt, &seq_gsi); + + /* If copy_basic_block has been empty at the start of this iteration, + call gsi_start_bb again to get at the newly added statements. */ + if (gsi_end_p (copy_gsi)) + copy_gsi = gsi_start_bb (copy_basic_block); + else + gsi_next (©_gsi); + + /* Process the new statement. The call to gimple_regimplify_operands + possibly turned the statement into multiple statements, we + need to process all of them. */ + do + { + tree fn; + + stmt = gsi_stmt (copy_gsi); + if (is_gimple_call (stmt) + && gimple_call_va_arg_pack_p (stmt) + && id->gimple_call) { - tree r, fndecl = gimple_call_fndecl (copy); - if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) - switch (DECL_FUNCTION_CODE (fndecl)) - { - case BUILT_IN_EH_COPY_VALUES: - r = gimple_call_arg (copy, 1); - r = remap_eh_region_tree_nr (r, id); - gimple_call_set_arg (copy, 1, r); - /* FALLTHRU */ + /* __builtin_va_arg_pack () should be replaced by + all arguments corresponding to ... in the caller. */ + tree p; + gimple new_call; + VEC(tree, heap) *argarray; + size_t nargs = gimple_call_num_args (id->gimple_call); + size_t n; - case BUILT_IN_EH_POINTER: - case BUILT_IN_EH_FILTER: - r = gimple_call_arg (copy, 0); - r = remap_eh_region_tree_nr (r, id); - gimple_call_set_arg (copy, 0, r); - break; + for (p = DECL_ARGUMENTS (id->src_fn); p; p = TREE_CHAIN (p)) + nargs--; + + /* Create the new array of arguments. */ + n = nargs + gimple_call_num_args (stmt); + argarray = VEC_alloc (tree, heap, n); + VEC_safe_grow (tree, heap, argarray, n); + + /* Copy all the arguments before '...' */ + memcpy (VEC_address (tree, argarray), + gimple_call_arg_ptr (stmt, 0), + gimple_call_num_args (stmt) * sizeof (tree)); + + /* Append the arguments passed in '...' */ + memcpy (VEC_address(tree, argarray) + gimple_call_num_args (stmt), + gimple_call_arg_ptr (id->gimple_call, 0) + + (gimple_call_num_args (id->gimple_call) - nargs), + nargs * sizeof (tree)); + + new_call = gimple_build_call_vec (gimple_call_fn (stmt), + argarray); + + VEC_free (tree, heap, argarray); + + /* Copy all GIMPLE_CALL flags, location and block, except + GF_CALL_VA_ARG_PACK. */ + gimple_call_copy_flags (new_call, stmt); + gimple_call_set_va_arg_pack (new_call, false); + gimple_set_location (new_call, gimple_location (stmt)); + gimple_set_block (new_call, gimple_block (stmt)); + gimple_call_set_lhs (new_call, gimple_call_lhs (stmt)); + + gsi_replace (©_gsi, new_call, false); + gimple_set_bb (stmt, NULL); + stmt = new_call; + } + else if (is_gimple_call (stmt) + && id->gimple_call + && (decl = gimple_call_fndecl (stmt)) + && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (decl) == BUILT_IN_VA_ARG_PACK_LEN) + { + /* __builtin_va_arg_pack_len () should be replaced by + the number of anonymous arguments. */ + size_t nargs = gimple_call_num_args (id->gimple_call); + tree count, p; + gimple new_stmt; + + for (p = DECL_ARGUMENTS (id->src_fn); p; p = TREE_CHAIN (p)) + nargs--; + + count = build_int_cst (integer_type_node, nargs); + new_stmt = gimple_build_assign (gimple_call_lhs (stmt), count); + gsi_replace (©_gsi, new_stmt, false); + stmt = new_stmt; + } + + /* Statements produced by inlining can be unfolded, especially + when we constant propagated some operands. We can't fold + them right now for two reasons: + 1) folding require SSA_NAME_DEF_STMTs to be correct + 2) we can't change function calls to builtins. + So we just mark statement for later folding. We mark + all new statements, instead just statements that has changed + by some nontrivial substitution so even statements made + foldable indirectly are updated. If this turns out to be + expensive, copy_body can be told to watch for nontrivial + changes. */ + if (id->statements_to_fold) + pointer_set_insert (id->statements_to_fold, stmt); + + /* We're duplicating a CALL_EXPR. Find any corresponding + callgraph edges and update or duplicate them. */ + if (is_gimple_call (stmt)) + { + struct cgraph_edge *edge; + int flags; + + switch (id->transform_call_graph_edges) + { + case CB_CGE_DUPLICATE: + edge = cgraph_edge (id->src_node, orig_stmt); + if (edge) + edge = cgraph_clone_edge (edge, id->dst_node, stmt, + gimple_uid (stmt), + REG_BR_PROB_BASE, 1, + edge->frequency, true); + break; + + case CB_CGE_MOVE_CLONES: + cgraph_set_call_stmt_including_clones (id->dst_node, + orig_stmt, stmt); + edge = cgraph_edge (id->dst_node, stmt); + break; + + case CB_CGE_MOVE: + edge = cgraph_edge (id->dst_node, orig_stmt); + if (edge) + cgraph_set_call_stmt (edge, stmt); + break; + + default: + gcc_unreachable (); + } - default: - break; - } - } - break; + /* Constant propagation on argument done during inlining + may create new direct call. Produce an edge for it. */ + if ((!edge + || (edge->indirect_call + && id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)) + && is_gimple_call (stmt) + && (fn = gimple_call_fndecl (stmt)) != NULL) + { + struct cgraph_node *dest = cgraph_node (fn); - case GIMPLE_RESX: - { - int r = gimple_resx_region (copy); - r = remap_eh_region_nr (r, id); - gimple_resx_set_region (copy, r); - } - break; + /* We have missing edge in the callgraph. This can happen + when previous inlining turned an indirect call into a + direct call by constant propagating arguments. In all + other cases we hit a bug (incorrect node sharing is the + most common reason for missing edges). */ + gcc_assert (dest->needed || !dest->analyzed); + if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES) + cgraph_create_edge_including_clones + (id->dst_node, dest, stmt, bb->count, + compute_call_stmt_bb_frequency (id->dst_node->decl, bb), + bb->loop_depth, CIF_ORIGINALLY_INDIRECT_CALL); + else + cgraph_create_edge (id->dst_node, dest, stmt, + bb->count, CGRAPH_FREQ_BASE, + bb->loop_depth)->inline_failed + = CIF_ORIGINALLY_INDIRECT_CALL; + if (dump_file) + { + fprintf (dump_file, "Created new direct edge to %s", + cgraph_node_name (dest)); + } + } - case GIMPLE_EH_DISPATCH: - { - int r = gimple_eh_dispatch_region (copy); - r = remap_eh_region_nr (r, id); - gimple_eh_dispatch_set_region (copy, r); + flags = gimple_call_flags (stmt); + if (flags & ECF_MAY_BE_ALLOCA) + cfun->calls_alloca = true; + if (flags & ECF_RETURNS_TWICE) + cfun->calls_setjmp = true; } - break; - default: - break; - } - } - - /* If STMT has a block defined, map it to the newly constructed - block. When inlining we want statements without a block to - appear in the block of the function call. */ - new_block = id->block; - if (gimple_block (copy)) - { - tree *n; - n = (tree *) pointer_map_contains (id->decl_map, gimple_block (copy)); - gcc_assert (n); - new_block = *n; - } + maybe_duplicate_eh_stmt_fn (cfun, stmt, id->src_cfun, orig_stmt, + id->eh_map, id->eh_lp_nr); - gimple_set_block (copy, new_block); + if (gimple_in_ssa_p (cfun) && !is_gimple_debug (stmt)) + { + ssa_op_iter i; + tree def; - if (gimple_debug_bind_p (copy)) - return copy; + find_new_referenced_vars (gsi_stmt (copy_gsi)); + FOR_EACH_SSA_TREE_OPERAND (def, stmt, i, SSA_OP_DEF) + if (TREE_CODE (def) == SSA_NAME) + SSA_NAME_DEF_STMT (def) = stmt; + } - /* Remap all the operands in COPY. */ - memset (&wi, 0, sizeof (wi)); - wi.info = id; - if (skip_first) - walk_tree (gimple_op_ptr (copy, 1), remap_gimple_op_r, &wi, NULL); - else - walk_gimple_op (copy, remap_gimple_op_r, &wi); + gsi_next (©_gsi); + } + while (!gsi_end_p (copy_gsi)); - /* Clear the copied virtual operands. We are not remapping them here - but are going to recreate them from scratch. */ - if (gimple_has_mem_ops (copy)) - { - gimple_set_vdef (copy, NULL_TREE); - gimple_set_vuse (copy, NULL_TREE); + copy_gsi = gsi_last_bb (copy_basic_block); } - return copy; + return copy_basic_block; } - -/* Copy basic block, scale profile accordingly. Edges will be taken care of - later */ - static basic_block -copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, +generic_cloning_copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, gcov_type count_scale) { gimple_stmt_iterator gsi, copy_gsi, seq_gsi; @@ -1477,13 +2787,15 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, basic_block_info automatically. */ copy_basic_block = create_basic_block (NULL, (void *) 0, (basic_block) bb->prev_bb->aux); - copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE; - +/* copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE; +*/ + copy_basic_block->count = bb->count; /* We are going to rebuild frequencies from scratch. These values have just small importance to drive canonicalize_loop_headers. */ - copy_basic_block->frequency = ((gcov_type)bb->frequency +/* copy_basic_block->frequency = ((gcov_type)bb->frequency * frequency_scale / REG_BR_PROB_BASE); - +*/ + copy_basic_block->frequency = (gcov_type)bb->frequency; if (copy_basic_block->frequency > BB_FREQ_MAX) copy_basic_block->frequency = BB_FREQ_MAX; @@ -1495,7 +2807,7 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, gimple orig_stmt = stmt; id->regimplify = false; - stmt = remap_gimple_stmt (stmt, id); + stmt = generic_cloning_remap_gimple_stmt (stmt, id); if (gimple_nop_p (stmt)) continue; @@ -1510,8 +2822,8 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, { tree new_rhs; new_rhs = force_gimple_operand_gsi (&seq_gsi, - gimple_assign_rhs1 (stmt), - true, NULL, false, GSI_NEW_STMT); + gimple_assign_rhs1 (stmt), + true, NULL, true, GSI_SAME_STMT); gimple_assign_set_rhs1 (stmt, new_rhs); id->regimplify = false; } @@ -1533,8 +2845,6 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, need to process all of them. */ do { - tree fn; - stmt = gsi_stmt (copy_gsi); if (is_gimple_call (stmt) && gimple_call_va_arg_pack_p (stmt) @@ -1623,80 +2933,88 @@ copy_bb (copy_body_data *id, basic_block bb, int frequency_scale, callgraph edges and update or duplicate them. */ if (is_gimple_call (stmt)) { + struct cgraph_node *node; struct cgraph_edge *edge; int flags; switch (id->transform_call_graph_edges) { - case CB_CGE_DUPLICATE: - edge = cgraph_edge (id->src_node, orig_stmt); - if (edge) - edge = cgraph_clone_edge (edge, id->dst_node, stmt, - gimple_uid (stmt), - REG_BR_PROB_BASE, 1, - edge->frequency, true); - break; - - case CB_CGE_MOVE_CLONES: - cgraph_set_call_stmt_including_clones (id->dst_node, - orig_stmt, stmt); - edge = cgraph_edge (id->dst_node, stmt); - break; - - case CB_CGE_MOVE: - edge = cgraph_edge (id->dst_node, orig_stmt); - if (edge) - cgraph_set_call_stmt (edge, stmt); - break; - - default: - gcc_unreachable (); - } + case CB_CGE_DUPLICATE: + edge = cgraph_edge (id->src_node, orig_stmt); + if (edge) + cgraph_clone_edge (edge, id->dst_node, stmt, + REG_BR_PROB_BASE, 1, + edge->frequency, true); + break; + + case CB_CGE_MOVE_CLONES: + for (node = id->dst_node->next_clone; + node; + node = node->next_clone) + { + edge = cgraph_edge (node, orig_stmt); + if (edge) + cgraph_set_call_stmt (edge, stmt); + } + /* FALLTHRU */ - /* Constant propagation on argument done during inlining - may create new direct call. Produce an edge for it. */ - if ((!edge - || (edge->indirect_call - && id->transform_call_graph_edges == CB_CGE_MOVE_CLONES)) - && is_gimple_call (stmt) - && (fn = gimple_call_fndecl (stmt)) != NULL) - { - struct cgraph_node *dest = cgraph_node (fn); + case CB_CGE_MOVE: + edge = cgraph_edge (id->dst_node, orig_stmt); + if (edge) + cgraph_set_call_stmt (edge, stmt); + break; - /* We have missing edge in the callgraph. This can happen - when previous inlining turned an indirect call into a - direct call by constant propagating arguments. In all - other cases we hit a bug (incorrect node sharing is the - most common reason for missing edges). */ - gcc_assert (dest->needed || !dest->analyzed); - if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES) - cgraph_create_edge_including_clones - (id->dst_node, dest, stmt, bb->count, - compute_call_stmt_bb_frequency (id->dst_node->decl, bb), - bb->loop_depth, CIF_ORIGINALLY_INDIRECT_CALL); - else - cgraph_create_edge (id->dst_node, dest, stmt, - bb->count, CGRAPH_FREQ_BASE, - bb->loop_depth)->inline_failed - = CIF_ORIGINALLY_INDIRECT_CALL; - if (dump_file) - { - fprintf (dump_file, "Created new direct edge to %s", - cgraph_node_name (dest)); - } + default: + gcc_unreachable (); } flags = gimple_call_flags (stmt); + if (flags & ECF_MAY_BE_ALLOCA) cfun->calls_alloca = true; if (flags & ECF_RETURNS_TWICE) cfun->calls_setjmp = true; } - maybe_duplicate_eh_stmt_fn (cfun, stmt, id->src_cfun, orig_stmt, - id->eh_map, id->eh_lp_nr); + /* If you think we can abort here, you are wrong. + There is no region 0 in gimple. */ + gcc_assert (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) != 0); + + if (stmt_could_throw_p (stmt) + /* When we are cloning for inlining, we are supposed to + construct a clone that calls precisely the same functions + as original. However IPA optimizers might've proved + earlier some function calls as non-trapping that might + render some basic blocks dead that might become + unreachable. + + We can't update SSA with unreachable blocks in CFG and thus + we prevent the scenario by preserving even the "dead" eh + edges until the point they are later removed by + fixup_cfg pass. */ + || (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES + && lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) > 0)) + { + int region = lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt); + + /* Add an entry for the copied tree in the EH hashtable. + When cloning or versioning, use the hashtable in + cfun, and just copy the EH number. When inlining, use the + hashtable in the caller, and adjust the region number. */ + if (region > 0) + add_stmt_to_eh_region (stmt, region + id->eh_region_offset); + + /* If this tree doesn't have a region associated with it, + and there is a "current region," + then associate this tree with the current region + and add edges associated with this region. */ + if (lookup_stmt_eh_region_fn (id->src_cfun, orig_stmt) <= 0 + && id->eh_region > 0 + && stmt_could_throw_p (stmt)) + add_stmt_to_eh_region (stmt, id->eh_region); + } - if (gimple_in_ssa_p (cfun) && !is_gimple_debug (stmt)) + if (gimple_in_ssa_p (cfun)) { ssa_op_iter i; tree def; @@ -1755,42 +3073,133 @@ update_ssa_across_abnormal_edges (basic_block bb, basic_block ret_bb, if (!can_throw) gcc_assert (!(e->flags & EDGE_EH)); - for (si = gsi_start_phis (e->dest); !gsi_end_p (si); gsi_next (&si)) - { - edge re; + for (si = gsi_start_phis (e->dest); !gsi_end_p (si); gsi_next (&si)) + { + edge re; + + phi = gsi_stmt (si); + + /* There shouldn't be any PHI nodes in the ENTRY_BLOCK. */ + gcc_assert (!e->dest->aux); + + gcc_assert ((e->flags & EDGE_EH) + || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (PHI_RESULT (phi))); + + if (!is_gimple_reg (PHI_RESULT (phi))) + { + mark_sym_for_renaming (SSA_NAME_VAR (PHI_RESULT (phi))); + continue; + } + + re = find_edge (ret_bb, e->dest); + gcc_assert (re); + gcc_assert ((re->flags & (EDGE_EH | EDGE_ABNORMAL)) + == (e->flags & (EDGE_EH | EDGE_ABNORMAL))); + + SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e), + USE_FROM_PTR (PHI_ARG_DEF_PTR_FROM_EDGE (phi, re))); + } + } +} + + +/* Copy edges from BB into its copy constructed earlier, scale profile + accordingly. Edges will be taken care of later. Assume aux + pointers to point to the copies of each BB. */ + +static void +copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) +{ + basic_block new_bb = (basic_block) bb->aux; + edge_iterator ei; + edge old_edge; + gimple_stmt_iterator si; + int flags; + + /* Use the indices from the original blocks to create edges for the + new ones. */ + FOR_EACH_EDGE (old_edge, ei, bb->succs) + if (!(old_edge->flags & EDGE_EH)) + { + edge new_edge; + + flags = old_edge->flags; + + /* Return edges do get a FALLTHRU flag when the get inlined. */ + if (old_edge->dest->index == EXIT_BLOCK && !old_edge->flags + && old_edge->dest->aux != EXIT_BLOCK_PTR) + flags |= EDGE_FALLTHRU; + new_edge = make_edge (new_bb, (basic_block) old_edge->dest->aux, flags); + new_edge->count = old_edge->count * count_scale / REG_BR_PROB_BASE; + new_edge->probability = old_edge->probability; + } + + if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK) + return; + + for (si = gsi_start_bb (new_bb); !gsi_end_p (si);) + { + gimple copy_stmt; + bool can_throw, nonlocal_goto; + + copy_stmt = gsi_stmt (si); + if (!is_gimple_debug (copy_stmt)) + { + update_stmt (copy_stmt); + if (gimple_in_ssa_p (cfun)) + mark_symbols_for_renaming (copy_stmt); + } + + /* Do this before the possible split_block. */ + gsi_next (&si); - phi = gsi_stmt (si); + /* If this tree could throw an exception, there are two + cases where we need to add abnormal edge(s): the + tree wasn't in a region and there is a "current + region" in the caller; or the original tree had + EH edges. In both cases split the block after the tree, + and add abnormal edge(s) as needed; we need both + those from the callee and the caller. + We check whether the copy can throw, because the const + propagation can change an INDIRECT_REF which throws + into a COMPONENT_REF which doesn't. If the copy + can throw, the original could also throw. */ + can_throw = stmt_can_throw_internal (copy_stmt); + nonlocal_goto = stmt_can_make_abnormal_goto (copy_stmt); - /* There shouldn't be any PHI nodes in the ENTRY_BLOCK. */ - gcc_assert (!e->dest->aux); + if (can_throw || nonlocal_goto) + { + if (!gsi_end_p (si)) + /* Note that bb's predecessor edges aren't necessarily + right at this point; split_block doesn't care. */ + { + edge e = split_block (new_bb, copy_stmt); - gcc_assert ((e->flags & EDGE_EH) - || SSA_NAME_OCCURS_IN_ABNORMAL_PHI (PHI_RESULT (phi))); + new_bb = e->dest; + new_bb->aux = e->src->aux; + si = gsi_start_bb (new_bb); + } + } - if (!is_gimple_reg (PHI_RESULT (phi))) - { - mark_sym_for_renaming (SSA_NAME_VAR (PHI_RESULT (phi))); - continue; - } + if (gimple_code (copy_stmt) == GIMPLE_EH_DISPATCH) + make_eh_dispatch_edges (copy_stmt); + else if (can_throw) + make_eh_edges (copy_stmt); - re = find_edge (ret_bb, e->dest); - gcc_assert (re); - gcc_assert ((re->flags & (EDGE_EH | EDGE_ABNORMAL)) - == (e->flags & (EDGE_EH | EDGE_ABNORMAL))); + if (nonlocal_goto) + make_abnormal_goto_edges (gimple_bb (copy_stmt), true); - SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e), - USE_FROM_PTR (PHI_ARG_DEF_PTR_FROM_EDGE (phi, re))); - } - } + if ((can_throw || nonlocal_goto) + && gimple_in_ssa_p (cfun)) + update_ssa_across_abnormal_edges (gimple_bb (copy_stmt), ret_bb, + can_throw, nonlocal_goto); + } } -/* Copy edges from BB into its copy constructed earlier, scale profile - accordingly. Edges will be taken care of later. Assume aux - pointers to point to the copies of each BB. */ static void -copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) +generic_cloning_copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) { basic_block new_bb = (basic_block) bb->aux; edge_iterator ei; @@ -1808,12 +3217,18 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) flags = old_edge->flags; /* Return edges do get a FALLTHRU flag when the get inlined. */ + /* if (old_edge->dest->index == EXIT_BLOCK && !old_edge->flags && old_edge->dest->aux != EXIT_BLOCK_PTR) flags |= EDGE_FALLTHRU; + */ new_edge = make_edge (new_bb, (basic_block) old_edge->dest->aux, flags); + new_edge->count = old_edge->count; + /* new_edge->count = old_edge->count * count_scale / REG_BR_PROB_BASE; + */ new_edge->probability = old_edge->probability; + } if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK) @@ -1825,12 +3240,9 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) bool can_throw, nonlocal_goto; copy_stmt = gsi_stmt (si); - if (!is_gimple_debug (copy_stmt)) - { - update_stmt (copy_stmt); - if (gimple_in_ssa_p (cfun)) - mark_symbols_for_renaming (copy_stmt); - } + update_stmt (copy_stmt); + if (gimple_in_ssa_p (cfun)) + mark_symbols_for_renaming (copy_stmt); /* Do this before the possible split_block. */ gsi_next (&si); @@ -1863,9 +3275,7 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) } } - if (gimple_code (copy_stmt) == GIMPLE_EH_DISPATCH) - make_eh_dispatch_edges (copy_stmt); - else if (can_throw) + if (can_throw) make_eh_edges (copy_stmt); if (nonlocal_goto) @@ -1878,6 +3288,8 @@ copy_edges_for_bb (basic_block bb, gcov_type count_scale, basic_block ret_bb) } } + + /* Copy the PHIs. All blocks and edges are copied, some blocks was possibly split and new outgoing EH edges inserted. BB points to the block of original function and AUX pointers links @@ -1942,6 +3354,12 @@ remap_decl_1 (tree decl, void *data) return remap_decl (decl, (copy_body_data *) data); } +static tree +generic_cloning_remap_decl_1 (tree decl, void *data) +{ + return generic_cloning_remap_decl (decl, (copy_body_data *) data); +} + /* Build struct function and associated datastructures for the new clone NEW_FNDECL to be build. CALLEE_FNDECL is the original */ @@ -2023,6 +3441,115 @@ initialize_cfun (tree new_fndecl, tree callee_fndecl, gcov_type count, pop_cfun (); } + + +static void +generic_cloning_initialize_cfun (tree new_fndecl, tree callee_fndecl, gcov_type count, + int frequency) +{ + struct function *src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl); + /* gcov_type count_scale=0, frequency_scale=0;*/ + + /* + + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count) + count_scale = (REG_BR_PROB_BASE * count + / ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count); + else + count_scale = 1; + + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency) + frequency_scale = (REG_BR_PROB_BASE * frequency + / + ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency); + else + frequency_scale = count_scale; +*/ + + /* Register specific tree functions. */ + gimple_register_cfg_hooks (); + + /* Get clean struct function. */ + push_struct_function (new_fndecl); + + /* We will rebuild these, so just sanity check that they are empty. */ + gcc_assert (VALUE_HISTOGRAMS (cfun) == NULL); + gcc_assert (cfun->local_decls == NULL); + gcc_assert (cfun->cfg == NULL); + gcc_assert (cfun->decl == new_fndecl); + + /* Copy items we preserve during clonning. */ + cfun->static_chain_decl = src_cfun->static_chain_decl; + cfun->nonlocal_goto_save_area = src_cfun->nonlocal_goto_save_area; + + cfun->function_start_locus = src_cfun->function_start_locus; + + cfun->function_end_locus = src_cfun->function_end_locus; + cfun->curr_properties = src_cfun->curr_properties; + cfun->last_verified = src_cfun->last_verified; + if (src_cfun->ipa_transforms_to_apply) + cfun->ipa_transforms_to_apply = VEC_copy (ipa_opt_pass, heap, + src_cfun->ipa_transforms_to_apply); + cfun->va_list_gpr_size = src_cfun->va_list_gpr_size; + cfun->va_list_fpr_size = src_cfun->va_list_fpr_size; + cfun->function_frequency = src_cfun->function_frequency; + cfun->has_nonlocal_label = src_cfun->has_nonlocal_label; + cfun->stdarg = src_cfun->stdarg; + cfun->dont_save_pending_sizes_p = src_cfun->dont_save_pending_sizes_p; + cfun->after_inlining = src_cfun->after_inlining; + cfun->returns_struct = src_cfun->returns_struct; + cfun->returns_pcc_struct = src_cfun->returns_pcc_struct; + cfun->after_tree_profile = src_cfun->after_tree_profile; + + cfun->calls_setjmp = src_cfun->calls_setjmp; + cfun->calls_alloca = src_cfun->calls_alloca; + cfun->always_inline_functions_inlined = src_cfun->always_inline_functions_inlined; + cfun->has_local_explicit_reg_vars = src_cfun->has_local_explicit_reg_vars; + /*cfun->funcdef_no = src_cfun->funcdef_no;*/ + /*cfun->is_thunk = src_cfun->is_thunk;*/ + gcc_assert(0 == src_cfun->is_thunk); + + init_empty_tree_cfg (); + + /* copy the profile status of cfg */ + profile_status = profile_status_for_function(src_cfun); + /* do a faithful copy */ + ENTRY_BLOCK_PTR->count = + ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count; + ENTRY_BLOCK_PTR->frequency = + ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency; + EXIT_BLOCK_PTR->count = + EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count; + EXIT_BLOCK_PTR->frequency = + EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency; + +/* + ENTRY_BLOCK_PTR->count = + (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale / + REG_BR_PROB_BASE); + ENTRY_BLOCK_PTR->frequency = + (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency * + frequency_scale / REG_BR_PROB_BASE); + EXIT_BLOCK_PTR->count = + (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count * count_scale / + REG_BR_PROB_BASE); + EXIT_BLOCK_PTR->frequency = + (EXIT_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency * + frequency_scale / REG_BR_PROB_BASE); + */ + + if (src_cfun->eh) + init_eh_for_function (); + + if (src_cfun->gimple_df) + { + init_tree_ssa (cfun); + cfun->gimple_df->in_ssa_p = true; + init_ssa_operands (); + } + pop_cfun (); +} + /* Make a copy of the body of FN so that it can be inserted inline in another function. Walks FN via CFG, returns new fndecl. */ @@ -2152,60 +3679,164 @@ copy_debug_stmt (gimple stmt, copy_body_data *id) gimple_debug_bind_set_var (stmt, t); - if (gimple_debug_bind_has_value_p (stmt)) - walk_tree (gimple_debug_bind_get_value_ptr (stmt), - remap_gimple_op_r, &wi, NULL); + if (gimple_debug_bind_has_value_p (stmt)) + walk_tree (gimple_debug_bind_get_value_ptr (stmt), + remap_gimple_op_r, &wi, NULL); + + /* Punt if any decl couldn't be remapped. */ + if (processing_debug_stmt < 0) + gimple_debug_bind_reset_value (stmt); + + processing_debug_stmt = 0; + + update_stmt (stmt); + if (gimple_in_ssa_p (cfun)) + mark_symbols_for_renaming (stmt); +} + +/* Process deferred debug stmts. In order to give values better odds + of being successfully remapped, we delay the processing of debug + stmts until all other stmts that might require remapping are + processed. */ + +static void +copy_debug_stmts (copy_body_data *id) +{ + size_t i; + gimple stmt; + + if (!id->debug_stmts) + return; + + for (i = 0; VEC_iterate (gimple, id->debug_stmts, i, stmt); i++) + copy_debug_stmt (stmt, id); + + VEC_free (gimple, heap, id->debug_stmts); +} + +/* Make a copy of the body of SRC_FN so that it can be inserted inline in + another function. */ + +static tree +copy_tree_body (copy_body_data *id) +{ + tree fndecl = id->src_fn; + tree body = DECL_SAVED_TREE (fndecl); + + walk_tree (&body, copy_tree_body_r, id, NULL); + + return body; +} + +/* Make a copy of the body of FN so that it can be inserted inline in + another function. */ + +static tree +copy_body (copy_body_data *id, gcov_type count, int frequency, + basic_block entry_block_map, basic_block exit_block_map) +{ + tree fndecl = id->src_fn; + tree body; + + /* If this body has a CFG, walk CFG and copy. */ + gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fndecl))); + body = copy_cfg_body (id, count, frequency, entry_block_map, exit_block_map); + copy_debug_stmts (id); + + return body; +} +/* Make a copy of the body of FN so that it can be inserted inline in + another function. Walks FN via CFG, returns new fndecl. */ + +static tree +generic_cloning_copy_cfg_body (copy_body_data * id, gcov_type count, int frequency, + basic_block entry_block_map, basic_block exit_block_map) +{ + tree callee_fndecl = id->src_fn; + /* Original cfun for the callee, doesn't change. */ + struct function *src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl); + struct function *cfun_to_copy; + basic_block bb; + tree new_fndecl = NULL; + gcov_type count_scale, frequency_scale; + int last; +/* + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count) + count_scale = (REG_BR_PROB_BASE * count + / ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count); + else + count_scale = 1; + + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency) + frequency_scale = (REG_BR_PROB_BASE * frequency + / + ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency); + else + frequency_scale = count_scale; +*/ + count_scale = 1; /*ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->count;*/ + frequency_scale = 1; /*ENTRY_BLOCK_PTR_FOR_FUNCTION (src_cfun)->frequency;*/ - /* Punt if any decl couldn't be remapped. */ - if (processing_debug_stmt < 0) - gimple_debug_bind_reset_value (stmt); + /* Register specific tree functions. */ + gimple_register_cfg_hooks (); - processing_debug_stmt = 0; + /* Must have a CFG here at this point. */ + gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION + (DECL_STRUCT_FUNCTION (callee_fndecl))); - update_stmt (stmt); - if (gimple_in_ssa_p (cfun)) - mark_symbols_for_renaming (stmt); -} + cfun_to_copy = id->src_cfun = DECL_STRUCT_FUNCTION (callee_fndecl); -/* Process deferred debug stmts. In order to give values better odds - of being successfully remapped, we delay the processing of debug - stmts until all other stmts that might require remapping are - processed. */ + ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = entry_block_map; + EXIT_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = exit_block_map; + entry_block_map->aux = ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy); + exit_block_map->aux = EXIT_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy); -static void -copy_debug_stmts (copy_body_data *id) -{ - size_t i; - gimple stmt; + /* Duplicate any exception-handling regions. */ + if (cfun->eh) + { + id->eh_region_offset + = duplicate_eh_regions (cfun_to_copy, generic_cloning_remap_decl_1, id, + 0, id->eh_region); + } - if (!id->debug_stmts) - return; + /* Use aux pointers to map the original blocks to copy. */ + FOR_EACH_BB_FN (bb, cfun_to_copy) + { + basic_block new_bb = generic_cloning_copy_bb (id, bb, frequency_scale, count_scale); + bb->aux = new_bb; + new_bb->aux = bb; + } - for (i = 0; VEC_iterate (gimple, id->debug_stmts, i, stmt); i++) - copy_debug_stmt (stmt, id); + last = last_basic_block; - VEC_free (gimple, heap, id->debug_stmts); -} + /* Now that we've duplicated the blocks, duplicate their edges. */ + FOR_ALL_BB_FN (bb, cfun_to_copy) + generic_cloning_copy_edges_for_bb (bb, count_scale, exit_block_map); -/* Make a copy of the body of SRC_FN so that it can be inserted inline in - another function. */ + gcc_assert(!gimple_in_ssa_p(cfun)); + + if (gimple_in_ssa_p (cfun)) + FOR_ALL_BB_FN (bb, cfun_to_copy) + copy_phis_for_bb (bb, id); -static tree -copy_tree_body (copy_body_data *id) -{ - tree fndecl = id->src_fn; - tree body = DECL_SAVED_TREE (fndecl); + FOR_ALL_BB_FN (bb, cfun_to_copy) + { + ((basic_block)bb->aux)->aux = NULL; + bb->aux = NULL; + } - walk_tree (&body, copy_tree_body_r, id, NULL); + /* Zero out AUX fields of newly created block during EH edge + insertion. */ + for (; last < last_basic_block; last++) + BASIC_BLOCK (last)->aux = NULL; + entry_block_map->aux = NULL; + exit_block_map->aux = NULL; - return body; + return new_fndecl; } -/* Make a copy of the body of FN so that it can be inserted inline in - another function. */ - static tree -copy_body (copy_body_data *id, gcov_type count, int frequency, +generic_cloning_copy_body (copy_body_data *id, gcov_type count, int frequency, basic_block entry_block_map, basic_block exit_block_map) { tree fndecl = id->src_fn; @@ -2213,12 +3844,10 @@ copy_body (copy_body_data *id, gcov_type count, int frequency, /* If this body has a CFG, walk CFG and copy. */ gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fndecl))); - body = copy_cfg_body (id, count, frequency, entry_block_map, exit_block_map); - copy_debug_stmts (id); + body = generic_cloning_copy_cfg_body (id, count, frequency, entry_block_map, exit_block_map); return body; } - /* Return true if VALUE is an ADDR_EXPR of an automatic variable defined in function FN, or of a data member thereof. */ @@ -2322,6 +3951,40 @@ insert_init_stmt (copy_body_data *id, basic_block bb, gimple init_stmt) } } + + +static void +generic_cloning_insert_init_stmt (basic_block bb, gimple init_stmt) +{ + /* If VAR represents a zero-sized variable, it's possible that the + assignment statement may result in no gimple statements. */ + if (init_stmt) + { + gimple_stmt_iterator si = gsi_last_bb (bb); + + /* We can end up with init statements that store to a non-register + from a rhs with a conversion. Handle that here by forcing the + rhs into a temporary. gimple_regimplify_operands is not + prepared to do this for us. */ + if (!is_gimple_reg (gimple_assign_lhs (init_stmt)) + && is_gimple_reg_type (TREE_TYPE (gimple_assign_lhs (init_stmt))) + && gimple_assign_rhs_class (init_stmt) == GIMPLE_UNARY_RHS) + { + tree rhs = build1 (gimple_assign_rhs_code (init_stmt), + gimple_expr_type (init_stmt), + gimple_assign_rhs1 (init_stmt)); + rhs = force_gimple_operand_gsi (&si, rhs, true, NULL_TREE, false, + GSI_NEW_STMT); + gimple_assign_set_rhs_code (init_stmt, TREE_CODE (rhs)); + gimple_assign_set_rhs1 (init_stmt, rhs); + } + gsi_insert_after (&si, init_stmt, GSI_NEW_STMT); + gimple_regimplify_operands (init_stmt, &si); + mark_symbols_for_renaming (init_stmt); + } +} + + /* Initialize parameter P with VALUE. If needed, produce init statement at the end of BB. When BB is NULL, we return init statement to be output later. */ @@ -3935,6 +5598,40 @@ fold_marked_statements (int first, struct pointer_set_t *statements) } } + +static void +generic_cloning_fold_marked_statements (int first, struct pointer_set_t *statements) +{ + for (; first < n_basic_blocks; first++) + if (BASIC_BLOCK (first)) + { + gimple_stmt_iterator gsi; + + for (gsi = gsi_start_bb (BASIC_BLOCK (first)); + !gsi_end_p (gsi); + gsi_next (&gsi)) + if (pointer_set_contains (statements, gsi_stmt (gsi))) + { + gimple old_stmt = gsi_stmt (gsi); + + if (fold_stmt (&gsi)) + { + /* Re-read the statement from GSI as fold_stmt() may + have changed it. */ + gimple new_stmt = gsi_stmt (gsi); + update_stmt (new_stmt); + + if (is_gimple_call (old_stmt)) + cgraph_update_edges_for_call_stmt (old_stmt, new_stmt); + + if (maybe_clean_or_replace_eh_stmt (old_stmt, new_stmt)) + gimple_purge_dead_eh_edges (BASIC_BLOCK (first)); + } + } + } +} + + /* Return true if BB has at least one abnormal outgoing edge. */ static inline bool @@ -4115,6 +5812,79 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) return NULL_TREE; } + + +/* Passed to walk_tree. Copies the node pointed to, if appropriate. */ + +tree +generic_cloning_copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) +{ + enum tree_code code = TREE_CODE (*tp); + enum tree_code_class cl = TREE_CODE_CLASS (code); + + /* We make copies of most nodes. */ + if (IS_EXPR_CODE_CLASS (cl) + || code == TREE_LIST + || code == TREE_VEC + || code == TYPE_DECL + || code == OMP_CLAUSE) + { + /* Because the chain gets clobbered when we make a copy, we save it + here. */ + tree chain = NULL_TREE, new_tree; + + chain = TREE_CHAIN (*tp); + + /* Copy the node. */ + new_tree = copy_node (*tp); + + /* Propagate mudflap marked-ness. */ + if (flag_mudflap && mf_marked_p (*tp)) + mf_mark (new_tree); + + *tp = new_tree; + + /* Now, restore the chain, if appropriate. That will cause + walk_tree to walk into the chain as well. */ + if (code == PARM_DECL + || code == TREE_LIST + || code == OMP_CLAUSE) + TREE_CHAIN (*tp) = chain; + + /* For now, we don't update BLOCKs when we make copies. So, we + have to nullify all BIND_EXPRs. */ + if (TREE_CODE (*tp) == BIND_EXPR) + BIND_EXPR_BLOCK (*tp) = NULL_TREE; + } + else if (code == CONSTRUCTOR) + { + /* CONSTRUCTOR nodes need special handling because + we need to duplicate the vector of elements. */ + tree new_tree; + + new_tree = copy_node (*tp); + + /* Propagate mudflap marked-ness. */ + if (flag_mudflap && mf_marked_p (*tp)) + mf_mark (new_tree); + + CONSTRUCTOR_ELTS (new_tree) = VEC_copy (constructor_elt, gc, + CONSTRUCTOR_ELTS (*tp)); + *tp = new_tree; + } + else if (TREE_CODE_CLASS (code) == tcc_type) + *walk_subtrees = 0; + else if (TREE_CODE_CLASS (code) == tcc_declaration) + *walk_subtrees = 0; + else if (TREE_CODE_CLASS (code) == tcc_constant) + *walk_subtrees = 0; + else + gcc_assert (code != STATEMENT_LIST); + return NULL_TREE; +} + + + /* The SAVE_EXPR pointed to by TP is being copied. If ST contains information indicating to what new SAVE_EXPR this one should be mapped, use that one. Otherwise, create a new node and enter it in ST. FN is @@ -4473,16 +6243,61 @@ declare_inline_vars (tree block, tree vars) cfun->local_decls = tree_cons (NULL_TREE, t, cfun->local_decls); } - if (block) - BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars); + if (block) + BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars); +} + +/* Copy NODE (which must be a DECL). The DECL originally was in the FROM_FN, + but now it will be in the TO_FN. PARM_TO_VAR means enable PARM_DECL to + VAR_DECL translation. */ + +static tree +copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy) +{ + /* Don't generate debug information for the copy if we wouldn't have + generated it for the copy either. */ + DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl); + DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl); + + /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what + declaration inspired this copy. */ + DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl); + + /* The new variable/label has no RTL, yet. */ + if (CODE_CONTAINS_STRUCT (TREE_CODE (copy), TS_DECL_WRTL) + && !TREE_STATIC (copy) && !DECL_EXTERNAL (copy)) + SET_DECL_RTL (copy, NULL_RTX); + + /* These args would always appear unused, if not for this. */ + TREE_USED (copy) = 1; + + /* Set the context for the new declaration. */ + if (!DECL_CONTEXT (decl)) + /* Globals stay global. */ + ; + else if (DECL_CONTEXT (decl) != id->src_fn) + /* Things that weren't in the scope of the function we're inlining + from aren't in the scope we're inlining to, either. */ + ; + else if (TREE_STATIC (decl)) + /* Function-scoped static variables should stay in the original + function. */ + ; + else + /* Ordinary automatic local variables are now in the scope of the + new function. */ + DECL_CONTEXT (copy) = id->dst_fn; + + return copy; } + /* Copy NODE (which must be a DECL). The DECL originally was in the FROM_FN, but now it will be in the TO_FN. PARM_TO_VAR means enable PARM_DECL to VAR_DECL translation. */ static tree -copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy) +generic_cloning_copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy) { /* Don't generate debug information for the copy if we wouldn't have generated it for the copy either. */ @@ -4491,13 +6306,26 @@ copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy) /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what declaration inspired this copy. */ - DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl); + /*DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl);*/ + gcc_assert(NULL_TREE == DECL_ABSTRACT_ORIGIN(decl)); + DECL_ABSTRACT_ORIGIN(copy) = NULL_TREE; /* The new variable/label has no RTL, yet. */ + if (CODE_CONTAINS_STRUCT (TREE_CODE (copy), TS_DECL_WRTL) && !TREE_STATIC (copy) && !DECL_EXTERNAL (copy)) SET_DECL_RTL (copy, NULL_RTX); + /* + if (NULL_RTX != DECL_RTL(decl)) + { + SET_DECL_RTL(copy, copy_rtx(DECL_RTL(decl))); + } else + { + gcc_assert(NULL_RTX == DECL_RTL(copy)); + } + */ + /* These args would always appear unused, if not for this. */ TREE_USED (copy) = 1; @@ -4521,6 +6349,8 @@ copy_decl_for_dup_finish (copy_body_data *id, tree decl, tree copy) return copy; } + + static tree copy_decl_to_var (tree decl, copy_body_data *id) { @@ -4591,6 +6421,42 @@ copy_decl_no_change (tree decl, copy_body_data *id) return copy_decl_for_dup_finish (id, decl, copy); } + +tree +generic_cloning_copy_decl_no_change (tree decl, copy_body_data *id) +{ + tree copy; + + copy = copy_node (decl); + + if (TREE_CODE(decl) == CONST_DECL + || TREE_CODE(decl) == RESULT_DECL + || TREE_CODE(decl) == LABEL_DECL + || TREE_CODE(decl) == VAR_DECL + || TREE_CODE(decl) == PARM_DECL + || TREE_CODE(decl) == FUNCTION_DECL + || TREE_CODE(decl) == TYPE_DECL) + { + if (decl->decl_with_rtl.rtl != NULL_RTX) + printf("generic_cloning_copy_decl: %s: %s rtl not null!\n", IDENTIFIER_POINTER(DECL_NAME(current_function_decl)), IDENTIFIER_POINTER(DECL_NAME(decl))); + } + + /* The COPY is not abstract; it will be generated in DST_FN. */ + DECL_ABSTRACT (copy) = 0; + lang_hooks.dup_lang_specific_decl (copy); + + /* TREE_ADDRESSABLE isn't used to indicate that a label's address has + been taken; it's for internal bookkeeping in expand_goto_internal. */ + if (TREE_CODE (copy) == LABEL_DECL) + { + TREE_ADDRESSABLE (copy) = 0; + LABEL_DECL_UID (copy) = -1; + } + + return generic_cloning_copy_decl_for_dup_finish (id, decl, copy); +} + + static tree copy_decl_maybe_to_var (tree decl, copy_body_data *id) { @@ -4635,6 +6501,44 @@ copy_arguments_for_versioning (tree orig_parm, copy_body_data * id, return new_parm; } + +static tree +generic_cloning_copy_arguments_for_versioning (tree orig_parm, copy_body_data * id, + bitmap args_to_skip, tree *vars) +{ + tree arg, *parg; + tree new_parm = NULL; + int i = 0; + + parg = &new_parm; + + gcc_assert(!args_to_skip); + + for (arg = orig_parm; arg; arg = TREE_CHAIN (arg), i++) + if (!args_to_skip || !bitmap_bit_p (args_to_skip, i)) + { + tree new_tree = generic_cloning_remap_decl (arg, id); + lang_hooks.dup_lang_specific_decl (new_tree); + *parg = new_tree; + parg = &TREE_CHAIN (new_tree); + } + else if (!pointer_map_contains (id->decl_map, arg)) + { + /* Make an equivalent VAR_DECL. If the argument was used + as temporary variable later in function, the uses will be + replaced by local variable. */ + tree var = copy_decl_to_var (arg, id); + get_var_ann (var); + add_referenced_var (var); + insert_decl_map (id, arg, var); + /* Declare this new variable. */ + TREE_CHAIN (var) = *vars; + *vars = var; + } + return new_parm; +} + + /* Return a copy of the function's static chain. */ static tree copy_static_chain (tree static_chain, copy_body_data * id) @@ -4652,6 +6556,22 @@ copy_static_chain (tree static_chain, copy_body_data * id) return static_chain; } +static tree +generic_cloning_copy_static_chain (tree static_chain, copy_body_data * id) +{ + tree *chain_copy, *pvar; + + chain_copy = &static_chain; + for (pvar = chain_copy; *pvar; pvar = &TREE_CHAIN (*pvar)) + { + tree new_tree = remap_decl (*pvar, id); + lang_hooks.dup_lang_specific_decl (new_tree); + TREE_CHAIN (new_tree) = TREE_CHAIN (*pvar); + *pvar = new_tree; + } + return static_chain; +} + /* Return true if the function is allowed to be versioned. This is a guard for the versioning functionality. */ @@ -4802,6 +6722,221 @@ update_clone_info (copy_body_data * id) of edges of clones of the function will be updated. */ void tree_function_versioning (tree old_decl, tree new_decl, +generic_cloning_tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map, + bool update_clones, bitmap args_to_skip) +{ + struct cgraph_node *old_version_node; + struct cgraph_node *new_version_node; + copy_body_data id; + tree p; + unsigned i; + struct ipa_replace_map *replace_info; + basic_block old_entry_block; + VEC (gimple, heap) *init_stmts = VEC_alloc (gimple, heap, 10); + + tree t_step; + tree old_current_function_decl = current_function_decl; + tree vars = NULL_TREE; + + gcc_assert (TREE_CODE (old_decl) == FUNCTION_DECL + && TREE_CODE (new_decl) == FUNCTION_DECL); + + + /* DECL_POSSIBLY_INLINED (old_decl) = 1;*/ + + old_version_node = cgraph_node (old_decl); + new_version_node = cgraph_node (new_decl); + + /* Output the inlining info for this abstract function, since it has been + inlined. If we don't do this now, we can lose the information about the + variables in the function when the blocks get blown away as soon as we + remove the cgraph node. */ +/* + (*debug_hooks->outlining_inline_function) (old_decl); + + + DECL_ARTIFICIAL (new_decl) = 1; + DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl); +*/ + + /* Prepare the data structures for the tree copy. */ + memset (&id, 0, sizeof (id)); + + /* Generate a new name for the new version. */ + + if (!update_clones) + { + DECL_NAME (new_decl) = create_tmp_var_name (NULL); + /*gcc_assert(old_decl->decl_with_vis.assembler_name == NULL_TREE);*/ + /*SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));*/ + SET_DECL_ASSEMBLER_NAME (new_decl, NULL_TREE); + /*gcc_assert(old_decl->decl_with_rtl.rtl == NULL_RTX);*/ + SET_DECL_RTL (new_decl, NULL_RTX); + id.statements_to_fold = pointer_set_create (); + } + + id.decl_map = pointer_map_create (); + id.src_fn = old_decl; + id.dst_fn = new_decl; + id.src_node = old_version_node; + id.dst_node = new_version_node; + id.src_cfun = DECL_STRUCT_FUNCTION (old_decl); + + id.copy_decl = generic_cloning_copy_decl_no_change; + id.transform_call_graph_edges + = update_clones ? CB_CGE_MOVE_CLONES : CB_CGE_MOVE; + id.transform_new_cfg = true; + id.transform_return_to_modify = false; + id.transform_lang_insert_block = NULL; + + current_function_decl = new_decl; + old_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION + (DECL_STRUCT_FUNCTION (old_decl)); + generic_cloning_initialize_cfun (new_decl, old_decl, + old_entry_block->count, + old_entry_block->frequency); + push_cfun (DECL_STRUCT_FUNCTION (new_decl)); + + /* Copy the function's static chain. */ + p = DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl; + + /* XXX: deal with nested function */ + gcc_assert( NULL_TREE == p); + /* + if (p) + DECL_STRUCT_FUNCTION (new_decl)->static_chain_decl = + generic_cloning_copy_static_chain (DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl, + &id); + */ + /* If there's a tree_map, prepare for substitution. */ + + /* currently we do not use tree_map parameter */ + gcc_assert (!tree_map); + /* + if (tree_map) + for (i = 0; i < VARRAY_ACTIVE_SIZE (tree_map); i++) + { + gimple init; + replace_info + = (struct ipa_replace_map *) VARRAY_GENERIC_PTR (tree_map, i); + if (replace_info->replace_p) + { + tree op = replace_info->new_tree; + + STRIP_NOPS (op); + + if (TREE_CODE (op) == VIEW_CONVERT_EXPR) + op = TREE_OPERAND (op, 0); + + if (TREE_CODE (op) == ADDR_EXPR) + { + op = TREE_OPERAND (op, 0); + while (handled_component_p (op)) + op = TREE_OPERAND (op, 0); + if (TREE_CODE (op) == VAR_DECL) + add_referenced_var (op); + } + gcc_assert (TREE_CODE (replace_info->old_tree) == PARM_DECL); + init = setup_one_parameter (&id, replace_info->old_tree, + replace_info->new_tree, id.src_fn, + NULL, + &vars); + if (init) + VEC_safe_push (gimple, heap, init_stmts, init); + } + } + */ + + /* Copy the function's arguments. */ + if (DECL_ARGUMENTS (old_decl) != NULL_TREE) + DECL_ARGUMENTS (new_decl) = + generic_cloning_copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id, + args_to_skip, &vars); + + DECL_INITIAL (new_decl) = generic_cloning_remap_blocks (DECL_INITIAL (id.src_fn), &id); + + /* Renumber the lexical scoping (non-code) blocks consecutively. */ + number_blocks (id.dst_fn); + + /* deal with inline vars ? */ + gcc_assert(NULL == vars); + /*declare_inline_vars (DECL_INITIAL (new_decl), vars);*/ + + if (DECL_STRUCT_FUNCTION (old_decl)->local_decls != NULL_TREE) + /* Add local vars. */ + for (t_step = DECL_STRUCT_FUNCTION (old_decl)->local_decls; + t_step; t_step = TREE_CHAIN (t_step)) + { + tree var = TREE_VALUE (t_step); + if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var)) + cfun->local_decls = tree_cons (NULL_TREE, var, cfun->local_decls); + else if (!can_be_nonlocal (var, &id)) + cfun->local_decls = + tree_cons (NULL_TREE, generic_cloning_remap_decl (var, &id), + cfun->local_decls); + } + + /* Copy the Function's body. */ + generic_cloning_copy_body (&id, old_entry_block->count, old_entry_block->frequency, ENTRY_BLOCK_PTR, EXIT_BLOCK_PTR); + + if (DECL_RESULT (old_decl) != NULL_TREE) + { + tree *res_decl = &DECL_RESULT (old_decl); + DECL_RESULT (new_decl) = generic_cloning_remap_decl (*res_decl, &id); + lang_hooks.dup_lang_specific_decl (DECL_RESULT (new_decl)); + } + + /* Renumber the lexical scoping (non-code) blocks consecutively. */ + number_blocks (new_decl); + + if (VEC_length (gimple, init_stmts)) + { + basic_block bb = split_edge (single_succ_edge (ENTRY_BLOCK_PTR)); + while (VEC_length (gimple, init_stmts)) + generic_cloning_insert_init_stmt (bb, VEC_pop (gimple, init_stmts)); + } + + /* Clean up. */ + pointer_map_destroy (id.decl_map); + + gcc_assert(!update_clones); + + + gcc_assert(!update_clones); + if (!update_clones) + { + generic_cloning_fold_marked_statements (0, id.statements_to_fold); + pointer_set_destroy (id.statements_to_fold); + fold_cond_expr_cond (); + } + + gcc_assert(!gimple_in_ssa_p(cfun)); + + if (gimple_in_ssa_p (cfun)) + { + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + if (!update_clones) + delete_unreachable_blocks (); + update_ssa (TODO_update_ssa); + if (!update_clones) + { + fold_cond_expr_cond (); + if (need_ssa_update_p ()) + update_ssa (TODO_update_ssa); + } + } + free_dominance_info (CDI_DOMINATORS); + free_dominance_info (CDI_POST_DOMINATORS); + VEC_free (gimple, heap, init_stmts); + pop_cfun (); + current_function_decl = old_current_function_decl; + gcc_assert (!current_function_decl + || DECL_STRUCT_FUNCTION (current_function_decl) == cfun); + return; +} + +void VEC(ipa_replace_map_p,gc)* tree_map, bool update_clones, bitmap args_to_skip) { @@ -5072,6 +7207,7 @@ build_duplicate_type (tree type) bool tree_can_inline_p (struct cgraph_edge *e) { + #if 0 /* This causes a regression in SPEC in that it prevents a cold function from inlining a hot function. Perhaps this should only apply to functions diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index 29932e84e38..71a06101126 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -180,6 +180,8 @@ int estimate_num_insns (gimple, eni_weights *); int estimate_num_insns_fn (tree, eni_weights *); int count_insns_seq (gimple_seq, eni_weights *); bool tree_versionable_function_p (tree); +void generic_cloning_tree_function_versioning (tree, tree, varray_type, bool, + bitmap); bool tree_can_inline_p (struct cgraph_edge *e); extern gimple_seq remap_gimple_seq (gimple_seq, copy_body_data *); diff --git a/gcc/tree-nrv.c b/gcc/tree-nrv.c index c1e9d605679..e5393a78dad 100644 --- a/gcc/tree-nrv.c +++ b/gcc/tree-nrv.c @@ -136,7 +136,6 @@ tree_nrv (void) { gimple stmt = gsi_stmt (gsi); tree ret_val; - if (gimple_code (stmt) == GIMPLE_RETURN) { /* In a function with an aggregate return value, the diff --git a/gcc/tree-optimize.c b/gcc/tree-optimize.c index 0cc4efa9c7f..c6edd5d89aa 100644 --- a/gcc/tree-optimize.c +++ b/gcc/tree-optimize.c @@ -360,8 +360,7 @@ tree_rest_of_compilation (tree fndecl) { location_t saved_loc; struct cgraph_node *node; - static int ici_all_passes = 0; /* must be forced into memory for - address-of to be meaningful */ + static int ici_passes_substitute_status = 0; timevar_push (TV_EXPAND); @@ -392,18 +391,21 @@ tree_rest_of_compilation (tree fndecl) /* Perform all tree transforms and optimizations. */ - /* ICI Event: Substitution of pass manager. - * ICI Parameter : set to identify when inside this - * region. It is useful when implementing event - * but still identify the passes from all_passes. */ - ici_all_passes = 1; - /* try calling the event - if not successful, fall back on the default - pass ordering */ + /* Signal the start of passes. */ + invoke_plugin_callbacks (PLUGIN_ALL_PASSES_START, NULL); + + /* ICI Event: Substitution of pass manager. */ + /* Try calling the event - if not successful, or if the plugin did not + manipulate passes, fall back on the default pass ordering. */ if (invoke_plugin_va_callbacks - (PLUGIN_ALL_PASSES_MANAGER, "all_passes", &ici_all_passes) - != PLUGEVT_SUCCESS) + (PLUGIN_ALL_PASSES_EXECUTION, + "substitute_status", EP_SILENT, &ici_passes_substitute_status) + != PLUGEVT_SUCCESS || ici_passes_substitute_status == 0) execute_pass_list (all_passes); + /* Signal the end of passes. */ + invoke_plugin_callbacks (PLUGIN_ALL_PASSES_END, NULL); + bitmap_obstack_release (®_obstack); /* Release the default bitmap obstack. */ diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 78ae28a25a5..7237a4aaa28 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -551,6 +551,8 @@ extern struct gimple_opt_pass pass_release_ssa_names; extern struct gimple_opt_pass pass_early_inline; extern struct gimple_opt_pass pass_inline_parameters; extern struct gimple_opt_pass pass_all_early_optimizations; +extern struct gimple_opt_pass pass_clone_functions; +extern struct gimple_opt_pass pass_instrument_functions; extern struct gimple_opt_pass pass_update_address_taken; extern struct gimple_opt_pass pass_convert_switch; @@ -588,4 +590,10 @@ extern void register_pass (struct register_pass_info *); directly in jump threading, and avoid peeling them next time. */ extern bool first_pass_instance; +/* ICI: We have to run IPA pass one by one, so summary is generated before + each executeion. */ +extern bool execute_one_ipa_pass (struct opt_pass *); +/* ICI: ipa pass manager needs to walk through cgraph with this function. */ +extern void do_per_function_toporder (void (*) (void *), void *); + #endif /* GCC_TREE_PASS_H */ -- 2.11.4.GIT