diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 97941831fe3118027eeac783723eddb595db70f9..40b4b5e9bc9d956a3f37ebe423940c8d6ad9316a 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1450,6 +1450,9 @@ OBJS = \ inchash.o \ incpath.o \ init-regs.o \ + ipa-alignment-propagation.o \ + ipa-localize-array.o \ + ipa-array-dse.o \ ipa-hardware-detection.o \ internal-fn.o \ ipa-struct-reorg/ipa-struct-reorg.o \ diff --git a/gcc/ai-optimizer.cc b/gcc/ai-optimizer.cc index 4fad56c1b640c7bd86a97d88a23b81d067b4ce70..9edd935a70a3ac0b0fe69b3853cadaacb3270260 100644 --- a/gcc/ai-optimizer.cc +++ b/gcc/ai-optimizer.cc @@ -411,7 +411,7 @@ graph_infer (int argc1, const char **argv1, const char *mops, return argmax_output; } -int +void get_optimize_decision_from_optimizer (int argc, const char **argv, const char *mops, int argc2, int64_t *argv2) @@ -421,5 +421,4 @@ get_optimize_decision_from_optimizer (int argc, const char **argv, { putenv ("AI_INFER_LEVEL=1"); } - return model_pred; } \ No newline at end of file diff --git a/gcc/ai4c-infer.h b/gcc/ai4c-infer.h index d347ab4c99b06745e8fb38b789f48f911bb9478e..b6a5112836b48c874f88c793ba2c82a36fe06c62 100644 --- a/gcc/ai4c-infer.h +++ b/gcc/ai4c-infer.h @@ -36,7 +36,8 @@ extern void execute_sha256 (const char *, char *, size_t); extern float read_float_from_file (FILE*); -extern int get_optimize_decision_from_optimizer (int, const char **, +extern void get_optimize_decision_from_optimizer (int, const char **, const char *, int , int64_t *); +extern void prepare_native_tune_str (const char *); #endif /* AI4C_INFER_H. */ diff --git a/gcc/common.opt b/gcc/common.opt index c87a4ceae2ee4573529fe8383f99f5e563738b26..d4f391e4824df005213ed566c91f185ab7d4ba2f 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -1971,6 +1971,18 @@ fipa-matrix-reorg Common Ignore Does nothing. Preserved for backward compatibility. +fipa-alignment-propagation +Common Var(flag_ipa_alignment_propagation) Init(0) Optimization +Propagate alignment of local variable's address. + +fipa-localize-array +Common Var(flag_ipa_localize_array) Init(0) Optimization +Transform global calloced array to be specific function local. + +fipa-array-dse +Common Var(flag_ipa_array_dse) Init(0) Optimization +Array dead and redundant store elimination. + fipa-reorder-fields Common Var(flag_ipa_reorder_fields) Init(0) Optimization Perform structure fields reorder optimizations. @@ -1996,6 +2008,18 @@ fipa-struct-sfc-shadow Common Var(flag_ipa_struct_sfc_shadow) Init(0) Optimization Enable field shadowing optimization in static struct field compression. +fipa-struct-dfc +Common Var(flag_ipa_struct_dfc) Init(0) Optimization +Perform dynamic structure field compression. + +fipa-struct-dfc-bitfield +Common Var(flag_ipa_struct_dfc_bitfield) Init(0) Optimization +Enable compressing to bitfield in dynamic struct field compression. + +fipa-struct-dfc-shadow +Common Var(flag_ipa_struct_dfc_shadow) Init(0) Optimization +Enable field shadowing optimization in dynamic struct field compression. + fipa-vrp Common Var(flag_ipa_vrp) Optimization Perform IPA Value Range Propagation. diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 61d0017b325cd66ce7ac75f3daf88ba6ba4734e9..3e4c2957aa183460e81a53e45e8ff4b0de7dc31a 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -16797,15 +16797,21 @@ extern bool lang_c_p (void); static void override_C_optimize_options (struct gcc_options *opts) { + opts->x_flag_ipa_alignment_propagation = 1; + opts->x_flag_ipa_localize_array = 1; + opts->x_flag_ipa_array_dse = 1; opts->x_flag_ipa_reorder_fields = 1; opts->x_flag_ipa_struct_reorg = 5; opts->x_struct_layout_optimize_level = 5; opts->x_flag_ipa_struct_sfc = 1; opts->x_flag_ipa_struct_sfc_bitfield = 1; - opts->x_flag_ipa_struct_sfc_shadow = 1; + opts->x_flag_ipa_struct_dfc = 1; + opts->x_flag_ipa_struct_dfc_bitfield = 1; + opts->x_flag_ipa_struct_dfc_shadow = 1; opts->x_flag_gnu89_inline = 1; opts->x_flag_ccmp2 = 1; opts->x_flag_array_widen_compare = 1; + opts->x_param_pointer_compression_size = 16; opts->x_param_max_inline_insns_auto = 64; opts->x_param_inline_unit_growth = 96; opts->x_flag_tree_slp_transpose_vectorize = 1; @@ -16890,13 +16896,6 @@ override_Fortran_optimize_options (struct gcc_options *opts) static void reset_machine_option (struct gcc_options *opts) { - if (!(opts->x_optimize_maximum) - || (strstr (opts->x_aarch64_tune_string, "hip09") == NULL - && strstr (opts->x_aarch64_tune_string, "hip12") == NULL)) - { - return; - } - const char *ai_infer_level = getenv ("AI_INFER_LEVEL"); if (ai_infer_level) { diff --git a/gcc/gcc.cc b/gcc/gcc.cc index ec452c8262eeb0c751ecf0e91367401ce24c500a..30999b6eef22cb9084a866391c912729b17cfc8f 100644 --- a/gcc/gcc.cc +++ b/gcc/gcc.cc @@ -45,6 +45,7 @@ compilation is specified by a string called a "spec". */ #include "filenames.h" #include "spellcheck.h" #include "opts-jobserver.h" +#include "ai4c-infer.h" @@ -5864,6 +5865,17 @@ do_self_spec (const char *spec) do_spec_2 (spec, NULL); do_spec_1 (" ", 0, NULL); + const char* tune_native = NULL; +#if !defined(CROSS_DIRECTORY_STRUCTURE) && (defined (__x86_64__) || defined (__aarch64__)) + tune_native = eval_spec_function ("local_cpu_detect", "cpu", ""); + +#endif + if (tune_native == NULL) + { + tune_native = "=native+"; + } + setenv ("GCC_AI4C_TUNE_INFO", tune_native, 1); + /* Mark %= sizeof (input)) + len = sizeof (input) - 1; + strncpy (input, start, len); + input[len] = '\0'; + } + } + + bool flag_Om = false; + bool flag_O3 = false; + bool flag_mcpu = false; + bool flag_native = false; + char mcpu_name[64]; + + for (unsigned i = 1; i < argc; i ++) + { + if (strcmp (argv[i], "-Om") == 0) + flag_Om = true; + if (strstr (argv[i], "-O3") != NULL) + flag_O3 = true; + if (strstr (argv[i], "mcpu=native") != NULL) + flag_native = true; + if (strstr (argv[i], "mcpu=") != NULL) + { + flag_mcpu = true; + const char* pos = strchr(argv[i], '='); + int len = sizeof(mcpu_name) - 1; + strncpy(mcpu_name, pos +1, len); + mcpu_name[len] = '\0'; + } + } + + if ((!flag_native) & flag_mcpu) + { + strcpy(input, mcpu_name); + } + + const int argc_hw = 6; + int64_t argv_hw[argc_hw] = { + global_options.x_param_simultaneous_prefetches, + global_options.x_param_l1_cache_size, + global_options.x_param_l1_cache_line_size, + global_options.x_param_l2_cache_size, + global_options.x_param_prefetch_latency, + global_options.x_param_ipa_max_switch_predicate_bounds, + }; + + const char *model_infer_level = secure_getenv ("AI_INFER_LEVEL"); + + if ((flag_O3 || flag_Om) && (!model_infer_level) && (flag_mcpu || flag_native)) + { + get_optimize_decision_from_optimizer (argc, argv, input, argc_hw, argv_hw); + } +} + /* driver::main is implemented as a series of driver:: method calls. */ int @@ -8189,6 +8285,7 @@ driver::main (int argc, char **argv) maybe_putenv_COLLECT_LTO_WRAPPER (); maybe_putenv_OFFLOAD_TARGETS (); handle_unrecognized_options (); + putenv_AI_INFER_LEVEL(argc, const_cast (argv)); if (completion) { diff --git a/gcc/gcc.h b/gcc/gcc.h index 63231ddb3ee7d5e7fcf90559a5d2bb8686519654..9106b3129bb8172d8dd37c562b8ae999d1081c5e 100644 --- a/gcc/gcc.h +++ b/gcc/gcc.h @@ -46,6 +46,7 @@ class driver void maybe_putenv_COLLECT_LTO_WRAPPER () const; void maybe_putenv_OFFLOAD_TARGETS () const; void handle_unrecognized_options (); + void putenv_AI_INFER_LEVEL (int argc, const char **argv); int maybe_print_and_exit () const; bool prepare_infiles (); void do_spec_on_infiles () const; diff --git a/gcc/ipa-alignment-propagation.cc b/gcc/ipa-alignment-propagation.cc new file mode 100644 index 0000000000000000000000000000000000000000..3f14818ae9bd72ac4fd7fe7a2093824adfc5df91 --- /dev/null +++ b/gcc/ipa-alignment-propagation.cc @@ -0,0 +1,478 @@ +/* Copyright (C) 2019-2022 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tree-cfg.h" +#include "tree-pass.h" +#include "tm_p.h" +#include "basic-block.h" +#include "bitmap.h" +#include "function.h" +#include "cfg.h" +#include "cgraph.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "gimple-pretty-print.h" +#include "gimple-ssa.h" +#include "ipa-utils.h" + +class alignment_propagator +{ +public: + alignment_propagator (cgraph_node *node); + + void execute (); + +private: + void propagate_params_alignment (); + void transform (); + size_t get_param_alignment (unsigned param_index); + size_t get_var_alignment (tree var); + bool check_assign (gimple *stmt, auto_vec &worklist, + size_t &alignment); + bool check_param (tree t, auto_vec &worklist, size_t &alignment); + int get_param_index_from_ssa (tree var); + size_t get_arg_alignment (cgraph_node *caller, tree arg); + size_t new_alignment (size_t orig, size_t new_value); + bool pow2_or_zerop (size_t value); + size_t abs_value (tree t); + bool candidate_stmt_p (gimple *stmt); + +private: + cgraph_node *node = nullptr; + hash_map alignment_map; +}; + +alignment_propagator::alignment_propagator (cgraph_node *node) + : node (node) +{ +} + +void +alignment_propagator::execute () +{ + if (dump_file) + { + fprintf (dump_file, "Start to rewrite function: %s\n", + node->dump_asm_name()); + dump_function_to_file (node->decl, dump_file, dump_flags); + } + + cfun_saver save (node); + + propagate_params_alignment (); + + /* If no alignment is propagated, there is no need to continue cause + the rest cases are covered by constant propagation. */ + if (!alignment_map.is_empty ()) + transform (); +} + +void +alignment_propagator::propagate_params_alignment () +{ + unsigned i = 0; + tree param = DECL_ARGUMENTS (node->decl); + while (param) + { + size_t alignment = get_param_alignment (i); + if (alignment) + alignment_map.put (param, alignment); + + param = DECL_CHAIN (param); + i++; + } +} + +void +alignment_propagator::transform () +{ + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (!candidate_stmt_p (stmt)) + continue; + + if (dump_file) + { + fprintf (dump_file, "Rewrite stmt:\n "); + print_gimple_stmt (dump_file, stmt, 0, TDF_NONE); + } + + tree lhs = gimple_assign_lhs (stmt); + tree new_rhs = build_int_cst (TREE_TYPE (lhs), 0); + gimple_assign_set_rhs_from_tree (&gsi, new_rhs); + update_stmt (stmt); + + if (dump_file) + { + fprintf (dump_file, "To:\n "); + print_gimple_stmt (dump_file, stmt, 0, TDF_NONE); + fprintf (dump_file, "\n"); + } + } + } +} + +size_t +alignment_propagator::get_param_alignment (unsigned param_index) +{ + size_t alignment = 0; + for (auto e = node->callers; e; e = e->next_caller) + { + if (e->caller == node) + continue; + + if (gimple_call_num_args (e->call_stmt) <= param_index) + return 0; + + tree arg = gimple_call_arg (e->call_stmt, param_index); + size_t arg_alignment = get_arg_alignment (e->caller, arg); + if (!arg_alignment) + return 0; + + if (!alignment || arg_alignment < alignment) + alignment = arg_alignment; + } + + return alignment; +} + +size_t +alignment_propagator::get_var_alignment (tree var) +{ + size_t alignment = 0; + + auto_bitmap visited; + auto_vec worklist; + worklist.safe_push (var); + + while (!worklist.is_empty ()) + { + tree t = worklist.pop (); + if (TREE_CODE (t) == INTEGER_CST) + { + size_t value = abs_value (t); + if (!pow2_or_zerop (value)) + return 0; + + alignment = new_alignment (alignment, value); + continue; + } + + if (TREE_CODE (t) != SSA_NAME) + return 0; + + if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t))) + continue; + + gimple *stmt = SSA_NAME_DEF_STMT (t); + switch (gimple_code (stmt)) + { + case GIMPLE_PHI: + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + worklist.safe_push (gimple_phi_arg_def (stmt, i)); + break; + case GIMPLE_ASSIGN: + if (!check_assign (stmt, worklist, alignment)) + return 0; + break; + case GIMPLE_NOP: + /* If we reach a default def, try to get the argument's alignment + from caller node. */ + if (!check_param (t, worklist, alignment)) + return 0; + break; + default: + return 0; + } + } + + return alignment; +} + +bool +alignment_propagator::check_assign (gimple *stmt, auto_vec &worklist, + size_t &alignment) +{ + if (gimple_assign_single_p (stmt) || gimple_assign_cast_p (stmt)) + { + worklist.safe_push (gimple_assign_rhs1 (stmt)); + return true; + } + + switch (gimple_assign_rhs_code (stmt)) + { + case NEGATE_EXPR: + worklist.safe_push (gimple_assign_rhs1 (stmt)); + return true; + case MAX_EXPR: + [[fallthrough]]; + case MIN_EXPR: + [[fallthrough]]; + case POINTER_PLUS_EXPR: + [[fallthrough]]; + case POINTER_DIFF_EXPR: + [[fallthrough]]; + case PLUS_EXPR: + [[fallthrough]]; + case MINUS_EXPR: + worklist.safe_push (gimple_assign_rhs1 (stmt)); + worklist.safe_push (gimple_assign_rhs2 (stmt)); + return true; + case MULT_EXPR: + break; + default: + return false; + } + + /* For mult_expr, rhs2 must be an integer constant, so we can simply take + this constant as alignment. Otherwise, return false. */ + tree rhs2 = gimple_assign_rhs2 (stmt); + if (TREE_CODE (rhs2) != INTEGER_CST) + return false; + + alignment = new_alignment (alignment, abs_value (rhs2)); + return true; +} + +bool +alignment_propagator::check_param (tree t, auto_vec &worklist, + size_t &alignment) +{ + int index = get_param_index_from_ssa (t); + if (index == -1) + return false; + + for (cgraph_edge *e = node->callers; e; e = e->next_caller) + { + if (gimple_call_num_args (e->call_stmt) <= index) + return false; + + tree arg = gimple_call_arg (e->call_stmt, index); + if (e->caller == node) + worklist.safe_push (arg); + else + { + auto *align = alignment_map.get (SSA_NAME_VAR (t)); + if (!align) + return false; + + alignment = new_alignment (alignment, *align); + } + } + + return true; +} + +/* Find param from VAR and return its index. Return -1 if fail. */ + +int +alignment_propagator::get_param_index_from_ssa (tree var) +{ + if (!SSA_NAME_IS_DEFAULT_DEF (var) || !SSA_NAME_VAR (var)) + return -1; + + tree param = DECL_ARGUMENTS (cfun->decl); + int index = 0; + while (param && param != SSA_NAME_VAR (var)) + { + param = DECL_CHAIN (param); + index++; + } + + return index; +} + +/* Get alignment of an argument if it is calculated from the address of a + local variable. */ + +size_t +alignment_propagator::get_arg_alignment (cgraph_node *caller, tree arg) +{ + if (!caller || !arg) + return 0; + + cfun_saver save (caller); + + tree base = nullptr; + tree offset = nullptr; + + /* Extract base and offset. */ + if (TREE_CODE (arg) == ADDR_EXPR) + { + base = arg; + tree op0 = TREE_OPERAND (base, 0); + if (TREE_CODE (op0) == MEM_REF) + { + base = TREE_OPERAND (op0, 0); + offset = TREE_OPERAND (op0, 1); + } + } + else + { + if (TREE_CODE (arg) != SSA_NAME) + return 0; + + gimple *stmt = SSA_NAME_DEF_STMT (arg); + if (!is_gimple_assign (stmt)) + return 0; + + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + offset = gimple_assign_rhs2 (stmt); + else if (!gimple_assign_single_p (stmt)) + return 0; + + base = gimple_assign_rhs1 (stmt); + } + + /* Check if ARG uses the address of a local variable. */ + if (TREE_CODE (base) != ADDR_EXPR) + return 0; + + tree decl = TREE_OPERAND (base, 0); + if (!decl || !VAR_P (decl) + || decl_function_context (decl) != current_function_decl) + return 0; + + size_t alignment = LOCAL_DECL_ALIGNMENT (decl) / 8; + + /* Update alignment if there is an offset. */ + if (offset) + { + if (TREE_CODE (offset) != INTEGER_CST) + return 0; + + auto value = abs_value (offset); + if (!pow2_or_zerop (value)) + return 0; + + alignment = new_alignment (alignment, value); + } + + return alignment; +} + +size_t +alignment_propagator::new_alignment (size_t orig_value, size_t new_value) +{ + if (!new_value) + return orig_value; + + if (!orig_value || new_value < orig_value) + return new_value; + + return orig_value; +} + +bool +alignment_propagator::pow2_or_zerop (size_t value) +{ + return !(value & (value - 1)); +} + +size_t +alignment_propagator::abs_value (tree t) +{ + gcc_assert (TREE_CODE (t) == INTEGER_CST); + auto value = TREE_INT_CST_LOW (t); + + return std::abs (static_cast (value)); +} + +bool +alignment_propagator::candidate_stmt_p (gimple *stmt) +{ + if (!is_gimple_assign (stmt) + || gimple_assign_rhs_code (stmt) != BIT_AND_EXPR) + return false; + + tree var = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + + return rhs2 && TREE_CODE (rhs2) == INTEGER_CST + && TREE_INT_CST_LOW (rhs2) < get_var_alignment (var); +} + + +static unsigned +ipa_propagate_alignment (void) +{ + auto_vec candidate_nodes; + cgraph_node *cnode = NULL; + FOR_EACH_FUNCTION (cnode) + { + if (!cnode->real_symbol_p () || !cnode->definition + || !cnode->has_gimple_body_p () || cnode->inlined_to) + continue; + + cnode->get_body (); + candidate_nodes.safe_push (cnode); + } + + for (auto *node : candidate_nodes) + alignment_propagator (node).execute (); + + return 0; +} + +namespace { +const pass_data pass_data_ipa_alignment_propagation = { + SIMPLE_IPA_PASS, + "alignment-propagation", + OPTGROUP_NONE, + TV_IPA_ALIGNMENT_PROPAGATION, + (PROP_cfg | PROP_ssa), + 0, + 0, + (TODO_update_ssa), + (TODO_verify_all), +}; + +class pass_ipa_alignment_propagation + : public simple_ipa_opt_pass +{ +public: + pass_ipa_alignment_propagation (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_alignment_propagation, ctxt) + {} + + virtual bool gate (function *) + { + return optimize >= 3 && flag_ipa_alignment_propagation; + } + + virtual unsigned execute (function *) + { + return ipa_propagate_alignment (); + } +}; +} /* namespace. */ + +simple_ipa_opt_pass * +make_pass_ipa_alignment_propagation (gcc::context *ctxt) +{ + return new pass_ipa_alignment_propagation (ctxt); +} diff --git a/gcc/ipa-array-dse.cc b/gcc/ipa-array-dse.cc new file mode 100644 index 0000000000000000000000000000000000000000..df973e849baa55579f43d86fca134762c1006e69 --- /dev/null +++ b/gcc/ipa-array-dse.cc @@ -0,0 +1,3317 @@ +/* Array dead store elimination + Copyright (C) 2021-2022 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "ipa-array-dse.h" + +#include "basic-block.h" +#include "bitmap.h" +#include "cgraph.h" +#include "cfghooks.h" +#include "cfgloop.h" +#include "cfg.h" +#include "fold-const.h" +#include "gimple.h" +#include "gimple-builder.h" +#include "gimple-iterator.h" +#include "gimple-pretty-print.h" +#include "gimple-ssa.h" +#include "gimple-walk.h" +#include "gimplify-me.h" +#include "ipa-utils.h" +#include "tree-phinodes.h" +#include "ssa-iterators.h" +#include "stringpool.h" +#include "tree-cfg.h" +#include "tree-dfa.h" +#include "tree-inline.h" +#include "tree-pass.h" +#include "tree-pretty-print.h" +#include "tree-ssanames.h" +#include "tree-vrp.h" +#include "tree.h" + +namespace array_dse { + +#define RANGE_TYPE long_long_integer_type_node +#define RANGE_INF LONG_LONG_MAX +#define RANGE_NINF LONG_LONG_MIN + +static inline bool +integer_cst_p (tree t) +{ + return TREE_CODE (t) == INTEGER_CST && !TREE_OVERFLOW (t); +} + +static tree +strip_base (tree addr) +{ + tree base = get_base_address (addr); + return TREE_CODE (base) == MEM_REF ? TREE_OPERAND (base, 0) : nullptr; +} + +static tree +strip_ssa_copy (tree var) +{ + if (!var || TREE_CODE (var) != SSA_NAME) + return var; + + while (true) + { + gimple *stmt = SSA_NAME_DEF_STMT (var); + if (!gimple_assign_single_p (stmt) && !gimple_assign_cast_p (stmt)) + break; + + tree rhs = gimple_assign_rhs1 (stmt); + if (!rhs || TREE_CODE (rhs) != SSA_NAME) + break; + + var = rhs; + } + + return var; +} + +static inline unsigned +greatest_common_divisor (unsigned a, unsigned b) +{ + return b == 0 ? a : greatest_common_divisor (b, a % b); +} + +static compare_result +opposite_compare_result (compare_result result) +{ + switch (result) + { + case COMPARE_ERROR: return COMPARE_ERROR; + case LT: return GT; + case EQ: return NE; + case GT: return LT; + case LE: return GE; + case GE: return LE; + case NE: return EQ; + } +} + +static tree_code +opposite_cond_code (tree_code code) +{ + switch (code) + { + case LT_EXPR: return GE_EXPR; + case LE_EXPR: return GT_EXPR; + case GT_EXPR: return LE_EXPR; + case GE_EXPR: return LT_EXPR; + case EQ_EXPR: return NE_EXPR; + case NE_EXPR: return EQ_EXPR; + default: + return ERROR_MARK; + } +} + +/* Calculate step of a loop variable, record all stmts that plus step. */ + +static int +calc_loop_var_step (tree loop_var, tree iterate_var, + hash_set *iterate_stmts = nullptr) +{ + int step = 0; + auto_bitmap visited; + auto_vec worklist; + worklist.safe_push (iterate_var); + + while (!worklist.is_empty ()) + { + tree t = worklist.pop (); + if (TREE_CODE (t) != SSA_NAME) + return 0; + + if (t == loop_var || !bitmap_set_bit (visited, SSA_NAME_VERSION (t))) + continue; + + gimple *stmt = SSA_NAME_DEF_STMT (t); + if (gimple_code (stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + worklist.safe_push (gimple_phi_arg_def (stmt, i)); + continue; + } + + /* Check iterate stmts' pattern: _2 = _1 + step. */ + if (!is_gimple_assign (stmt) + || (gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR + && gimple_assign_rhs_code (stmt) != PLUS_EXPR)) + return 0; + + tree ptr = gimple_assign_rhs1 (stmt); + tree offset = gimple_assign_rhs2 (stmt); + if (TREE_CODE (offset) != INTEGER_CST) + return 0; + + worklist.safe_push (ptr); + HOST_WIDE_INT offset_val = TREE_INT_CST_LOW (offset); + if (step && offset_val != step) + return 0; + step = offset_val; + + if (iterate_stmts) + iterate_stmts->add (stmt); + } + + return step; +} + +/* VAR is a loop var when: + 1. VAR is defined by a phi in LOOP's header. + 2. The defination phi should have two args, one comes from preheader + and the other comes from latch. */ + +static bool +loop_var_p (loop_p loop, tree var) +{ + if (TREE_CODE (var) != SSA_NAME) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (var); + if (gimple_code (stmt) != GIMPLE_PHI || gimple_bb (stmt) != loop->header) + return false; + + edge preheader_edge = loop_preheader_edge (loop); + edge latch_edge = loop_latch_edge (loop); + + return preheader_edge && latch_edge + && PHI_ARG_DEF_FROM_EDGE (stmt, preheader_edge) + && PHI_ARG_DEF_FROM_EDGE (stmt, latch_edge); +} + +static inline tree +build_value (HOST_WIDE_INT value) +{ + return build_int_cst (RANGE_TYPE, value); +} + +static inline value_range +make_range (HOST_WIDE_INT value) +{ + tree v = build_value (value); + return value_range{v, v}; +} + +static inline value_range +make_range (HOST_WIDE_INT min, HOST_WIDE_INT max) +{ + return value_range{build_value (min), build_value (max)}; +} + +static infinite_kind +infinite_p (tree value) +{ + tree type = TREE_TYPE (value); + if (TREE_CODE (value) != INTEGER_CST || TYPE_PRECISION (type) == 1) + return infinite_kind::NON_INF; + + wide_int type_min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type)); + wide_int type_max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); + + if (INTEGRAL_TYPE_P (type) && !TYPE_UNSIGNED (type) + && wi::to_wide (value) == type_min) + return infinite_kind::NINF; + + if (wi::to_wide (value) == type_max) + return infinite_kind::INF; + + return infinite_kind::NON_INF; +} + +static inline HOST_WIDE_INT +get_multiplier (tree t) +{ + if (TREE_CODE (t) != MULT_EXPR + || TREE_CODE (TREE_OPERAND (t, 1)) != INTEGER_CST) + return 0; + + return TREE_INT_CST_LOW (TREE_OPERAND (t, 1)); +} + +static tree negate_tree (tree t); +static tree minus_tree (tree t1, tree t2); + +/* Convert negative multiplier to positive. */ + +static void +handle_negate_multiplier (tree t) +{ + HOST_WIDE_INT multiplier = get_multiplier (t); + if (multiplier >= 0) + return; + + tree lhs = TREE_OPERAND (t, 0); + if (TREE_CODE (lhs) == PLUS_EXPR) + { + TREE_OPERAND (t, 0) = minus_tree (negate_tree (TREE_OPERAND (lhs, 0)), + TREE_OPERAND (lhs, 1)); + TREE_OPERAND (t, 1) = build_int_cst (RANGE_TYPE, -multiplier); + } + else if (TREE_CODE (lhs) == MINUS_EXPR) + { + TREE_OPERAND (t, 0) = negate_tree (lhs); + TREE_OPERAND (t, 1) = build_int_cst (RANGE_TYPE, -multiplier); + } +} + +static tree +negate_tree (tree t) +{ + if (!t) + return nullptr; + + if (infinite_p (t) == infinite_kind::INF) + return build_value (RANGE_NINF); + else if (infinite_p (t) == infinite_kind::NINF) + return build_value (RANGE_INF); + else + return fold_build1 (NEGATE_EXPR, RANGE_TYPE, t); +} + +static tree +plus_tree (tree t1, tree t2) +{ + if (!t1 || !t2) + return nullptr; + + infinite_kind inf1 = infinite_p (t1); + infinite_kind inf2 = infinite_p (t2); + + if ((inf1 == infinite_kind::INF && inf2 == infinite_kind::NINF) + || (inf1 == infinite_kind::NINF && inf2 == infinite_kind::INF)) + return nullptr; + + if (inf1 == infinite_kind::NINF || inf2 == infinite_kind::NINF) + return build_value (RANGE_NINF); + + if (inf1 == infinite_kind::INF || inf2 == infinite_kind::INF) + return build_value (RANGE_INF); + + tree ret = fold_build2 (PLUS_EXPR, RANGE_TYPE, t1, t2); + handle_negate_multiplier (ret); + + return ret; +} + +static tree +minus_tree (tree t1, tree t2) +{ + if (!t1 || !t2) + return nullptr; + + infinite_kind inf1 = infinite_p (t1); + infinite_kind inf2 = infinite_p (t2); + + if ((inf1 == infinite_kind::INF && inf2 == infinite_kind::INF) + || (inf1 == infinite_kind::NINF && inf2 == infinite_kind::NINF)) + return nullptr; + + if (inf1 == infinite_kind::NINF || inf2 == infinite_kind::INF) + return build_value (RANGE_NINF); + + if (inf1 == infinite_kind::INF || inf2 == infinite_kind::NINF) + return build_value (RANGE_INF); + + tree ret = fold_build2 (MINUS_EXPR, RANGE_TYPE, t1, t2); + handle_negate_multiplier (ret); + + return ret; +} + +/* Callback for walk_tree, usage: + walk_tree (&A, sub_expr_p, B, nullptr) + + Check if B is sub expr of A. + */ + +static tree +sub_expr_p (tree *opnd_ptr, int *walk_subtrees ATTRIBUTE_UNUSED, void *data) +{ + tree opnd = *opnd_ptr; + tree var = static_cast (data); + + if (opnd == var) + return var; + + return NULL_TREE; +} + +static unsigned +get_ptr_layers (tree expr) +{ + unsigned layers = 0; + while (POINTER_TYPE_P (expr) || TREE_CODE (expr) == ARRAY_TYPE) + { + layers++; + expr = TREE_TYPE (expr); + } + + return layers; +} + +static bool +find_base (gimple *stmt ATTRIBUTE_UNUSED, tree base, + tree var ATTRIBUTE_UNUSED, void *data) +{ + return (TREE_CODE (base) == MEM_REF + && TREE_OPERAND (base, 0) == static_cast (data)); +} + +static bool +gimple_phi_arg_p (gimple *stmt, tree var) +{ + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + if (gimple_phi_arg_def (stmt, i) == var) + return true; + + return false; +} + +/* Returns the number of FIELD_DECLs in TYPE. */ + +static unsigned +fields_length (const_tree type) +{ + tree t = TYPE_FIELDS (type); + return list_length (t); +} + +/* Get unique base address of VAR. */ + +tree +addr_analyzer::get_address (tree var) +{ + if (tree *it = address_map.get (var)) + return *it; + + tree addr = analyze_address (var); + address_map.put (var, addr); + + return addr; +} + +/* Try to find the unique base address to which VAR is accessing in + current function. */ + +tree +addr_analyzer::analyze_address (tree var) +{ + tree addr = nullptr; + auto_bitmap visited; + auto_vec worklist; + worklist.safe_push (var); + + while (!worklist.is_empty ()) + { + tree t = worklist.pop (); + if (TREE_CODE (t) != SSA_NAME || !POINTER_TYPE_P (TREE_TYPE (t))) + return nullptr; + + if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t))) + continue; + + if (SSA_NAME_IS_DEFAULT_DEF (t)) + { + tree new_addr = SSA_NAME_VAR (t); + if (!new_addr || (addr && addr != new_addr)) + return nullptr; + + addr = new_addr; + continue; + } + + gimple *def_stmt = SSA_NAME_DEF_STMT (t); + if (is_gimple_assign (def_stmt)) + { + if (!gimple_assign_single_p (def_stmt) + && !gimple_assign_cast_p (def_stmt) + && gimple_assign_rhs_code (def_stmt) != POINTER_PLUS_EXPR) + return nullptr; + + worklist.safe_push (gimple_assign_rhs1 (def_stmt)); + } + else if (gimple_code (def_stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (def_stmt); i++) + worklist.safe_push (gimple_phi_arg_def (def_stmt, i)); + } + else + return nullptr; + } + + return addr; +} + +array_dse_callee::array_dse_callee (cgraph_node *node) + : node (node) +{ +} + +/* Check if the node could be a candidate callee for array dse. */ + +bool +array_dse_callee::analyze () +{ + cfun_saver save (node, LOOPS_NORMAL | LOOPS_HAVE_RECORDED_EXITS); + + return filter_function () && find_candidate_array () + && find_length_param () && check_array_usage (); +} + +unsigned HOST_WIDE_INT +array_dse_callee::get_len_param_max () const +{ + return len_param_max; +} + +tree +array_dse_callee::mult_tree (basic_block bb, tree t1, tree t2) +{ + if (!bb || !t1 || !t2 || !integer_cst_p (t2) + || infinite_p (t2) != infinite_kind::NON_INF) + return nullptr; + + if (integer_zerop (t1) || integer_zerop (t2)) + return integer_zero_node; + + auto range1 = calc_tree_range (bb, t1); + if (tree_to_shwi (range1.min ()) < 0) + return nullptr; + + HOST_WIDE_INT multiplier = tree_to_shwi (t2); + if (infinite_p (t1) != infinite_kind::NON_INF) + return build_value (multiplier > 0 ? RANGE_INF : RANGE_NINF); + + return fold_build2 (MULT_EXPR, RANGE_TYPE, t1, t2); +} + +tree +array_dse_callee::div_tree (basic_block bb, tree t1, tree t2) +{ + if (!bb || !t1 || !t2 || !integer_cst_p (t2) + || infinite_p (t2) != infinite_kind::NON_INF) + return nullptr; + + if (integer_zerop (t2)) + return nullptr; + + if (integer_zerop (t1)) + return integer_zero_node; + + auto range1 = calc_tree_range (bb, t1); + if (tree_to_shwi (range1.min ()) < 0) + return nullptr; + + HOST_WIDE_INT divisor = tree_to_shwi (t2); + + if (infinite_p (t1) != infinite_kind::NON_INF) + return build_value (divisor > 0 ? RANGE_INF : RANGE_NINF); + + return fold_build2 (TRUNC_DIV_EXPR, RANGE_TYPE, t1, t2); +} + +tree +array_dse_callee::lshift_tree (tree t1, tree t2) +{ + if (!t1 || !t2 || !integer_cst_p (t2)) + return nullptr; + + if (infinite_p (t1) != infinite_kind::NON_INF) + return t1; + + return fold_build2 (LSHIFT_EXPR, RANGE_TYPE, t1, t2); +} + +tree +array_dse_callee::rshift_tree (tree t1, tree t2) +{ + if (!t1 || !t2 || !integer_cst_p (t2)) + return nullptr; + + if (infinite_p (t1) != infinite_kind::NON_INF) + return t1; + + return fold_build2 (RSHIFT_EXPR, RANGE_TYPE, t1, t2); +} + +tree +array_dse_callee::max_tree (basic_block bb, tree t1, tree t2) +{ + if (!bb || !t1 || !t2) + return nullptr; + + switch (compare_tree (bb, t1, t2)) + { + case EQ: return t1; + case GT: return t1; + case GE: return t1; + case LT: return t2; + case LE: return t2; + default: return nullptr; + } +} + +tree +array_dse_callee::min_tree (basic_block bb, tree t1, tree t2) +{ + if (!bb || !t1 || !t2) + return nullptr; + + switch (compare_tree (bb, t1, t2)) + { + case EQ: return t2; + case GT: return t2; + case GE: return t2; + case LT: return t1; + case LE: return t1; + default: return nullptr; + } +} + +/* Calculate the value of T, where T is an expression with len_main_var and + N_VALUE is len_main_var's value. */ + +HOST_WIDE_INT +array_dse_callee::calc_tree_value (tree t, HOST_WIDE_INT n_value) +{ + if (TREE_CODE (t) == INTEGER_CST) + return tree_to_shwi (t); + + if (t == len_main_var || t == signed_len_var) + return n_value; + + HOST_WIDE_INT op_value[2]; + for (int i = 0; i < std::min (2, tree_operand_length (t)); i++) + op_value[i] = calc_tree_value (TREE_OPERAND (t, i), n_value); + + switch (TREE_CODE (t)) + { + case NEGATE_EXPR: + return -op_value[0]; + case PLUS_EXPR: + return op_value[0] + op_value[1]; + case MINUS_EXPR: + return op_value[0] - op_value[1]; + case MULT_EXPR: + return op_value[0] * op_value[1]; + case TRUNC_DIV_EXPR: + return op_value[0] / op_value[1]; + case LSHIFT_EXPR: + return op_value[0] * (1 << op_value[1]); + case RSHIFT_EXPR: + return op_value[0] / (1 << op_value[1]); + default: + return 0; + } +} + +/* Calculate expression T's range. */ + +value_range +array_dse_callee::calc_tree_range (basic_block bb, tree t) +{ + if (!t) + return value_range{RANGE_TYPE}; + + if (TREE_CODE (t) == INTEGER_CST) + return make_range (tree_to_shwi (t)); + + if (t == len_main_var || t == signed_len_var) + return len_range_map.get_or_insert (bb); + + int len = tree_operand_length (t); + gcc_assert (len > 0); + value_range range1 = calc_tree_range (bb, TREE_OPERAND (t, 0)); + value_range range2; + if (len == 2) + range2 = calc_tree_range (bb, TREE_OPERAND (t, 1)); + + switch (TREE_CODE (t)) + { + /* Since the variable in both expressions is len_main_var and both + expressions are monotonically increasing, we can just substitute + the maximum and minimum values of len_main_var to calculate the + expression T's range. */ + case PLUS_EXPR: + case MINUS_EXPR: + { + tree op[2] = {TREE_OPERAND (t, 0), TREE_OPERAND (t, 1)}; + if (integer_cst_p (op[0]) || integer_cst_p (op[1])) + break; + + auto len_range = len_range_map.get_or_insert (bb); + auto len_min = tree_to_shwi (len_range.min ()); + auto len_max = tree_to_shwi (len_range.max ()); + auto min1 = calc_tree_value (op[0], len_min); + auto max1 = calc_tree_value (op[0], len_max); + auto min2 = calc_tree_value (op[1], len_min); + auto max2 = calc_tree_value (op[1], len_max); + + auto min = TREE_CODE (t) == PLUS_EXPR ? min1 + min2 : min1 - min2; + auto max = TREE_CODE (t) == PLUS_EXPR ? max1 + max2 : max1 - max2; + + if (min > max) + std::swap (min, max); + + return make_range (min, max); + } + default: + break; + } + + return build_range (bb, TREE_CODE (t), range1, range2); +} + +value_range +array_dse_callee::build_range (basic_block bb, tree_code op, + const value_range &r1, const value_range &r2) +{ + tree min = nullptr; + tree max = nullptr; + switch (op) + { + case NEGATE_EXPR: + min = negate_tree (r1.max ()); + max = negate_tree (r1.min ()); + break; + case PLUS_EXPR: + [[fallthrough]]; + case POINTER_PLUS_EXPR: + min = plus_tree (r1.min (), r2.min ()); + max = plus_tree (r1.max (), r2.max ()); + break; + case MINUS_EXPR: + [[fallthrough]]; + case POINTER_DIFF_EXPR: + min = minus_tree (r1.min(), r2.max ()); + max = minus_tree (r1.max(), r2.min ()); + break; + case MULT_EXPR: + min = mult_tree (bb, r1.min (), r2.min ()); + max = mult_tree (bb, r1.max (), r2.max ()); + break; + case TRUNC_DIV_EXPR: + min = div_tree (bb, r1.min (), r2.max ()); + max = div_tree (bb, r1.max (), r2.min ()); + break; + case LSHIFT_EXPR: + min = lshift_tree (r1.min (), r2.min ()); + max = lshift_tree (r1.max (), r2.max ()); + break; + case RSHIFT_EXPR: + min = rshift_tree (r1.min (), r2.max ()); + max = rshift_tree (r1.max (), r2.min ()); + break; + case MAX_EXPR: + min = max_tree (bb, r1.min (), r2.min ()); + max = max_tree (bb, r1.max (), r2.max ()); + break; + case MIN_EXPR: + min = min_tree (bb, r1.min (), r2.min ()); + max = min_tree (bb, r1.max (), r2.max ()); + break; + default: + break; + } + + return min && max ? value_range{min, max} : value_range{RANGE_TYPE}; +} + +/* Compare two pointer range value in BB. */ + +compare_result +array_dse_callee::compare_tree (basic_block bb, tree t1, tree t2) +{ + if (!bb || !t1 || !t2) + return COMPARE_ERROR; + + if (operand_equal_p (t1, t2)) + return EQ; + + auto ret = compare_tree_by_minus (bb, t1, t2); + if (!ret) + ret = opposite_compare_result (compare_tree_by_minus (bb, t2, t1)); + + return ret; +} + +compare_result +array_dse_callee::compare_tree_by_minus (basic_block bb, tree t1, tree t2) +{ + tree expr = minus_tree (t1, t2); + auto range = calc_tree_range (bb, expr); + HOST_WIDE_INT min = tree_to_shwi (range.min ()); + HOST_WIDE_INT max = tree_to_shwi (range.max ()); + if (min == 0) + return GE; + if (min > 0) + return GT; + if (max == 0) + return LE; + if (max < 0) + return LT; + + return COMPARE_ERROR; +} + +bool +array_dse_callee::filter_function () const +{ + return leaf_recursive_node_p (node) && no_return_p () + /* There must be two params: array and length. */ + && list_length (DECL_ARGUMENTS (node->decl)) == PARAM_NUM; +} + +/* Candidate callee must return no value. Each return block can't have any + stmt except a return stmt. */ + +bool +array_dse_callee::no_return_p () const +{ + tree return_type = TREE_TYPE (TREE_TYPE (cfun->decl)); + if (TREE_CODE (return_type) != VOID_TYPE) + return false; + + for (auto return_edge : EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + basic_block return_bb = return_edge->src; + if (!single_succ_p (return_bb)) + return false; + + gimple *stmt = first_stmt (return_bb); + if (gimple_code (stmt) != GIMPLE_RETURN + || gimple_return_retval (as_a (stmt))) + return false; + } + + return true; +} + +bool +array_dse_callee::find_main_vars () +{ + auto_bitmap visited; + tree default_def[PARAM_NUM] = {nullptr, nullptr}; + + /* Collect all params' default def. */ + unsigned i; + tree name; + FOR_EACH_SSA_NAME (i, name, cfun) + { + if (!SSA_NAME_IS_DEFAULT_DEF (name) + || SSA_NAME_IS_VIRTUAL_OPERAND (name)) + continue; + + gimple *stmt = SSA_NAME_DEF_STMT (name); + if (gimple_code (stmt) != GIMPLE_NOP) + return false; + + /* Each param should have an unique default ssa def. */ + int index = find_param_index (SSA_NAME_VAR (name)); + if (index == -1 || !bitmap_set_bit (visited, index)) + return false; + + default_def[index] = name; + } + + if (bitmap_count_bits (visited) != PARAM_NUM) + return false; + + array_main_var = default_def[array_param_index]; + len_main_var = default_def[len_param_index]; + + find_tail_recursive_loop (default_def); + + signed_len_var = fold_convert (RANGE_TYPE, len_main_var); + + return true; +} + +/* Try to find a tail recursive loop. */ + +void +array_dse_callee::find_tail_recursive_loop (tree *default_def) +{ + tree main_loop_var[PARAM_NUM] = {nullptr, nullptr}; + loop_p unique_loop = nullptr; + + for (unsigned i = 0; i < PARAM_NUM; i++) + { + tree name = default_def[i]; + + use_operand_p use_p; + gimple *stmt = nullptr; + if (!single_imm_use (name, &use_p, &stmt) + || gimple_code (stmt) != GIMPLE_PHI) + return; + + main_loop_var[i] = gimple_phi_result (stmt); + + /* Check if all main vars are defined in the same loop header. */ + basic_block bb = gimple_bb (stmt); + loop_p loop = bb->loop_father; + if (!loop || loop->num == 0 || !bb_loop_header_p (bb) + || (unique_loop && unique_loop != loop)) + return; + + unique_loop = loop; + } + + /* Multiple latch is not allow. */ + if (!unique_loop || !loop_latch_edge (unique_loop)) + return; + + /* The loop header must be the "first" block. There shouldn't be any + stmt before entering main loop. */ + basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); + basic_block preheader = loop_preheader_edge (unique_loop)->src; + if (single_succ_p (preheader) && single_pred_p (preheader) + && single_pred (preheader) == entry_bb && single_succ_p (entry_bb) + && empty_block_p (preheader)) + { + main_loop = unique_loop; + array_main_var = main_loop_var[array_param_index]; + len_main_var = main_loop_var[len_param_index]; + } +} + +/* Check if the function only store to the array passed by its param. */ + +bool +array_dse_callee::find_candidate_array () +{ + tree unique_array = nullptr; + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (gimple_clobber_p (stmt)) + continue; + + /* There are 3 kind of stmts may have store ops: GIMPLE_ASSIGN, + GIMPLE_CALL and GIMPLE_ASM. */ + if (gimple_has_volatile_ops (stmt) + || gimple_code (stmt) == GIMPLE_ASM) + return false; + + /* We have check that current function only has recursive call, + and it doesn't return a value, so we can skip call stmt. */ + if (gimple_code (stmt) != GIMPLE_ASSIGN) + continue; + + tree lhs = gimple_assign_lhs (stmt); + if (TREE_CODE (lhs) == SSA_NAME) + continue; + + tree base = strip_base (lhs); + if (!base || TREE_CODE (base) != SSA_NAME) + return false; + + tree array = analyzer.get_address (base); + if (!array || (unique_array && unique_array != array)) + return false; + + unique_array = array; + } + } + + if (!unique_array) + return false; + + int index = find_param_index (unique_array); + if (index < 0) + return false; + + array_param = unique_array; + array_param_index = index; + + if (dump_file) + { + fprintf (dump_file, "Found unique stored array: "); + print_generic_expr (dump_file, unique_array); + fprintf (dump_file, "\n"); + } + + return true; +} + +/* Check if the function has length param. */ + +bool +array_dse_callee::find_length_param () +{ + collect_read_write_ptrs (); + + tree len = nullptr; + unsigned size = 0; + for (auto ptr : all_ptrs) + if (!check_pointer (ptr, len, size)) + return false; + + if (!len || TREE_CODE (len) != SSA_NAME || !SSA_NAME_VAR (len)) + return false; + + int index = find_param_index (SSA_NAME_VAR (len)); + if (index < 0) + return false; + + len_param = SSA_NAME_VAR (len); + len_param_index = index; + elem_size = build_int_cst (RANGE_TYPE, size); + elem_size_cst = size; + calc_length_param_max (); + + if (len && dump_file) + { + fprintf (dump_file, "Found unique array length: "); + print_generic_expr (dump_file, len_param); + fprintf (dump_file, "\n"); + } + + return true; +} + +void +array_dse_callee::collect_read_write_ptrs () +{ + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + tree op = gimple_op (stmt, i); + if (!op) + continue; + + tree base = strip_base (op); + if (!base || TREE_CODE (base) != SSA_NAME) + continue; + + tree array = analyzer.get_address (base); + if (array != array_param) + continue; + + all_ptrs.add (base); + } + } + } +} + +/* We heuristically set upper bound of length param to the max value with + half bits of its data type. + + TODO: Overflows may still occur, we need a better implement. + */ + +void +array_dse_callee::calc_length_param_max () +{ + unsigned bits = TYPE_PRECISION (TREE_TYPE (len_param)); + len_param_max = 1L << (bits / 2); +} + +/* Check pointer pattern: ptr = ptr1 + offset1 + | + ptr2 + offset2 + | + ... + | + ARRAY + offset3 + + All ptrs we visited must be calculated by adding offset to array_param. + All offset must be an expression with the only variable len_param. + LEN will be set to the unique variable we founded. + SIZE will be set to the minimum offset unit, which will be treated as the + array element size. + */ + +bool +array_dse_callee::check_pointer (tree ptr, tree &len, unsigned &size) +{ + visited_offset.empty (); + auto_bitmap visited; + auto_vec worklist; + worklist.safe_push (ptr); + + while (!worklist.is_empty ()) + { + tree t = worklist.pop (); + if (!POINTER_TYPE_P (TREE_TYPE (t)) || TREE_CODE (t) != SSA_NAME) + return false; + + if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t))) + continue; + + if (SSA_NAME_IS_DEFAULT_DEF (t)) + { + tree var = SSA_NAME_VAR (t); + if (!var || var != array_param) + return false; + + continue; + } + + gimple *stmt = SSA_NAME_DEF_STMT (t); + if (is_gimple_assign (stmt)) + { + worklist.safe_push (gimple_assign_rhs1 (stmt)); + if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR + && !check_offset (gimple_assign_rhs2 (stmt), len, size, + worklist)) + return false; + } + else if (gimple_code (stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + worklist.safe_push (gimple_phi_arg_def (stmt, i)); + } + else + return false; + } + + return true; +} + +/* Check offset part. */ + +bool +array_dse_callee::check_offset (tree var, tree &len, unsigned &size, + auto_vec &worklist) +{ + if (visited_offset.contains (var)) + return true; + visited_offset.add (var); + + if (TREE_CODE (TREE_TYPE (var)) != INTEGER_TYPE) + return false; + + tree offset = strip_ssa_copy (var); + if (TREE_CODE (offset) == INTEGER_CST) + { + HOST_WIDE_INT value = TREE_INT_CST_LOW (offset); + value = std::abs (value); + size = size ? greatest_common_divisor (size, value) : value; + return true; + } + + if (TREE_CODE (offset) != SSA_NAME) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (offset); + if (gimple_code (stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + if (!check_offset (gimple_phi_arg_def (stmt, i), len, size, worklist)) + return false; + } + else if (!is_gimple_assign (stmt)) + return false; + + switch (gimple_assign_rhs_code (stmt)) + { + case MAX_EXPR: + [[fallthrough]]; + case MIN_EXPR: + [[fallthrough]]; + case PLUS_EXPR: + [[fallthrough]]; + case MINUS_EXPR: + return check_offset (gimple_assign_rhs1 (stmt), len, size, worklist) + && check_offset (gimple_assign_rhs2 (stmt), len, size, + worklist); + case POINTER_DIFF_EXPR: + worklist.safe_push(gimple_assign_rhs1 (stmt)); + worklist.safe_push(gimple_assign_rhs2 (stmt)); + return true; + case NEGATE_EXPR: + return check_offset (gimple_assign_rhs1 (stmt), len, size, worklist); + case MULT_EXPR: + return check_mult_expr (stmt, len, size); + default: + return false; + } +} + +/* Handle MULT_EXPR. */ + +bool +array_dse_callee::check_mult_expr (gimple *stmt, tree &len, unsigned &size) +{ + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + + /* Handle size. */ + if (TREE_CODE (rhs2) != INTEGER_CST) + return false; + + HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs2); + size = greatest_common_divisor (size, std::abs (value)); + + /* Handle index. */ + rhs1 = strip_ssa_copy (rhs1); + if (TREE_CODE (rhs1) != SSA_NAME) + return false; + + gimple *index_stmt = SSA_NAME_DEF_STMT (rhs1); + if (is_gimple_assign (index_stmt) && gimple_num_ops (index_stmt) > 2) + { + if (TREE_CODE (gimple_assign_rhs2 (index_stmt)) != INTEGER_CST) + return false; + rhs1 = gimple_assign_rhs1 (index_stmt); + } + + if (len && len != rhs1) + return false; + len = rhs1; + + return true; +} + +/* Find the param index of VAR in current function. + Return -1 if not found. */ + +int +array_dse_callee::find_param_index (tree var) +{ + if (TREE_CODE (var) != PARM_DECL) + return -1; + + tree param = DECL_ARGUMENTS (node->decl); + int index = 0; + while (param) + { + if (param == var) + return index; + + param = DECL_CHAIN (param); + index++; + } + + return -1; +} + +bool +array_dse_callee::check_array_usage () +{ + find_main_vars (); + + return calc_ptr_range () && check_ptr_range () + && check_recursive_call_arg (); +} + +/* Calculate len_param's value range in each block. + We assume its initial range is [1, len_param_max], we will validate this + range at each call to this callee. */ + +void +array_dse_callee::calc_len_range () +{ + /* Init all blocks' len_range. */ + auto full_len_range = make_range (len_param_min, len_param_max); + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + len_range_map.put (bb, full_len_range); + + /* Calculate new range according to condition. */ + FOR_EACH_BB_FN (bb, cfun) + { + gimple *stmt = gsi_stmt (gsi_last_bb (bb)); + auto cond_range = get_range_from_cond (stmt); + if (cond_range.undefined_p ()) + continue; + + edge true_edge = nullptr; + edge false_edge = nullptr; + extract_true_false_edges_from_block (bb, &true_edge, &false_edge); + update_len_range (true_edge->dest, cond_range); + update_len_range (false_edge->dest, invert_range (cond_range)); + } +} + +/* Update len_param's range in all block dominated by START. */ + +void +array_dse_callee::update_len_range (basic_block start, + const value_range &new_range) +{ + for (auto bb : get_all_dominated_blocks (CDI_DOMINATORS, start)) + (*len_range_map.get (bb)).intersect (new_range); +} + +value_range +array_dse_callee::invert_range (const value_range &range) const +{ + auto new_range = range; + new_range.invert (); + + return new_range; +} + +/* Get range of len_param from a condition. */ + +value_range +array_dse_callee::get_range_from_cond (gimple *stmt) +{ + if (!stmt || gimple_code (stmt) != GIMPLE_COND) + return value_range{}; + + gcond *cond = as_a (stmt); + tree_code code = gimple_cond_code (cond); + tree lhs = gimple_cond_lhs (cond); + tree rhs = gimple_cond_rhs (cond); + + if (lhs != len_main_var || TREE_CODE (rhs) != INTEGER_CST) + return value_range{}; + + HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs); + + switch (code) + { + case LT_EXPR: + return make_range (RANGE_NINF, value - 1); + case LE_EXPR: + return make_range (RANGE_NINF, value); + case GT_EXPR: + return make_range (value + 1, RANGE_INF); + case GE_EXPR: + return make_range (value, RANGE_INF); + case EQ_EXPR: + return make_range (value); + case NE_EXPR: + return invert_range (make_range (value)); + default: + return value_range{}; + } +} + +/* Get range of a variable, represented by len_param. If variable is a + pointer, return the range of its offset from array_param. */ + +value_range +array_dse_callee::get_var_range (basic_block bb, tree var) +{ + if (var == array_main_var) + return make_range (0, 0); + + if (var == len_main_var) + return value_range{signed_len_var, signed_len_var}; + + if (find_var_range (var, bb)) + return var_range[var][bb]; + + /* If we can't calculate its range, keep it varying. */ + auto &range = var_range[var][bb]; + range.set_varying (RANGE_TYPE); + + if (TREE_CODE (var) == INTEGER_CST) + { + HOST_WIDE_INT value = TREE_INT_CST_LOW (var); + range = make_range (value); + return range; + } + + if (TREE_CODE (var) != SSA_NAME) + return range; + + /* Build range expression recursively. */ + gimple *stmt = SSA_NAME_DEF_STMT (var); + if (gimple_code (stmt) == GIMPLE_PHI) + { + range = get_var_range (bb, gimple_phi_arg_def (stmt, 0)); + for (unsigned i = 1; i < gimple_phi_num_args (stmt); i++) + { + tree arg = gimple_phi_arg_def (stmt, i); + auto arg_range = get_var_range (bb, arg); + tree min = min_tree (bb, range.min (), arg_range.min ()); + tree max = max_tree (bb, range.max (), arg_range.max ()); + if (!min || !max) + { + range.set_varying (RANGE_TYPE); + break; + } + + range = value_range{min, max}; + } + return range; + } + + if (!is_gimple_assign (stmt)) + return range; + + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_num_ops (stmt) > 2 ? gimple_assign_rhs2 (stmt) : nullptr; + value_range range1 = get_var_range (bb, rhs1); + value_range range2 = value_range{RANGE_TYPE}; + if (rhs2) + range2 = get_var_range (bb, rhs2); + + if (gimple_assign_single_p (stmt) || gimple_assign_cast_p (stmt)) + range = range1; + else + range = build_range (bb, gimple_assign_rhs_code (stmt), range1, range2); + + return range; +} + +/* Calculate pointer's offset range by checking loop condition. */ + +bool +array_dse_callee::calc_ptr_range () +{ + calc_len_range (); + + auto_bitmap visited; + auto_vec worklist; + loop_p l = main_loop ? main_loop : current_loops->tree_root; + worklist.safe_push (l->header); + + while (!worklist.is_empty ()) + { + basic_block bb = worklist.pop (); + if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun) + || (main_loop && bb == main_loop->latch) + || unreachable_blocks.contains (bb)) + continue; + + if (bb->flags & BB_IRREDUCIBLE_LOOP) + return false; + + if (!bitmap_set_bit (visited, bb->index)) + continue; + + if (loop_header_p (bb) && !calc_loop_var_range (bb->loop_father)) + return false; + + for (auto succ : bb->succs) + worklist.safe_push (succ->dest); + } + + return true; +} + +/* Check if offset range of all pointers calculated by array_param are + within [0, (len_param -1) * elem_size]. */ + +bool +array_dse_callee::check_ptr_range () +{ + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + if (unreachable_blocks.contains (bb)) + continue; + + for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + tree op = gimple_op (stmt, i); + if (!op) + continue; + + tree base = strip_base (op); + if (!base || TREE_CODE (base) != SSA_NAME + || !all_ptrs.contains (base)) + continue; + + auto range = get_var_range (bb, base); + /* offset >= 0. */ + auto ret = compare_tree (bb, range.min (), integer_zero_node); + if (!ret || ret & LT) + return false; + + /* offset <= (n - 1) * elem_size. */ + tree tmp = minus_tree (signed_len_var, integer_one_node); + tmp = mult_tree (bb, tmp, elem_size); + ret = compare_tree (bb, range.max (), tmp); + if (!ret || ret & GT) + return false; + } + } + } + + return true; +} + +/* Check range of recursive call arguments: + void func(a, n) { + ... + func(a1, n1); + } + + 1. (1) a1 >= a (tail recursive call) + (2) a1 == a (normal recursive call) + 2. n1 >= 1 + 3. a1 + n1 * elem_size <= a + n * elem_size + */ + +bool +array_dse_callee::check_recursive_call_arg () +{ + auto_vec array_args; + auto_vec len_args; + auto_vec blocks; + auto_vec is_tail_recursive_call; + + collect_recursive_call_args (array_args, len_args, blocks, + is_tail_recursive_call); + + for (unsigned i = 0; i < array_args.length (); i++) + { + basic_block bb = blocks[i]; + update_branch_range (bb); + auto array_range = get_var_range (bb, array_args[i]); + auto len_range = get_var_range (bb, len_args[i]); + + /* Check requirement 2. */ + auto ret = compare_tree (bb, len_range.min (), integer_one_node); + if (!ret || ret & ~GE) + return false; + + if (is_tail_recursive_call[i]) + { + /* Check requirement 1.1. */ + ret = compare_tree (bb, array_range.min (), integer_zero_node); + if (!ret || ret & ~GE) + return false; + } + else + { + /* Check requirement 1.2. */ + if (!integer_zerop (array_range.min ()) + || !integer_zerop (array_range.max ())) + return false; + } + + /* Check requirement 3. */ + tree offset = build_recursive_offset (len_args[i]); + if (!offset) + return false; + + tree recursive_ptr_max + = build_recursive_ptr_range_max (bb, array_args[i], offset); + if (!recursive_ptr_max) + return false; + + tree upper_bound = mult_tree (bb, signed_len_var, elem_size); + ret = compare_tree (bb, recursive_ptr_max, upper_bound); + if (!ret || ret & ~LE) + return false; + } + + return true; +} + +void +array_dse_callee::collect_recursive_call_args ( + auto_vec &array_args, auto_vec &len_args, + auto_vec &blocks, auto_vec &is_tail_recursive_call) +{ + for (cgraph_edge *edge = node->callees; edge; edge = edge->next_callee) + { + if (node != edge->callee) + continue; + + gcall *call = edge->call_stmt; + tree array_arg = gimple_call_arg (call, array_param_index); + tree len_arg = gimple_call_arg (call, len_param_index); + + array_args.safe_push (array_arg); + len_args.safe_push (len_arg); + blocks.safe_push (gimple_bb (call)); + is_tail_recursive_call.safe_push (tail_recursive_call_p (call)); + } + + if (main_loop) + { + gimple *array_def_stmt = SSA_NAME_DEF_STMT (array_main_var); + gimple *len_def_stmt = SSA_NAME_DEF_STMT (len_main_var); + edge latch_edge = loop_latch_edge (main_loop); + tree array_arg = PHI_ARG_DEF_FROM_EDGE (array_def_stmt, latch_edge); + tree len_arg = PHI_ARG_DEF_FROM_EDGE (len_def_stmt, latch_edge); + array_args.safe_push (array_arg); + len_args.safe_push (len_arg); + blocks.safe_push (latch_edge->src); + is_tail_recursive_call.safe_push (true); + } +} + +/* If BB is first block after a condition jump, try to update range according + to the condition. */ + +void +array_dse_callee::update_branch_range (basic_block bb) +{ + if (!single_pred_p (bb)) + return; + + basic_block pred = single_pred (bb); + gimple *stmt = gsi_stmt (gsi_last_bb (pred)); + if (!stmt || gimple_code (stmt) != GIMPLE_COND) + return; + + tree lhs = gimple_cond_lhs (stmt); + tree rhs = gimple_cond_rhs (stmt); + if (!integer_cst_p (rhs)) + return; + + tree_code code = gimple_cond_code (stmt); + if (single_pred_edge (bb)->flags & EDGE_FALSE_VALUE) + code = opposite_cond_code (code); + + auto range = get_var_range (bb, lhs); + tree min = range.min (); + tree max = range.max (); + HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs); + + switch (code) + { + case LT_EXPR: + value--; + [[fallthrough]]; + case LE_EXPR: + max = min_tree (bb, max, build_value (value)); + break; + case GT_EXPR: + value++; + [[fallthrough]]; + case GE_EXPR: + min = max_tree (bb, min, build_value (value)); + break; + case EQ_EXPR: + var_range[lhs][bb] = make_range (value); + return; + default: + return; + } + + var_range[lhs][bb] = value_range{min, max}; +} + +/* If LEN = (ptr1 - ptr2) / elem_size, + then recursive_offset = LEN * elem_size = (ptr1 - ptr2). + + We can do this only when ptr1 and ptr2 comes from array_param, + so (ptr1 - ptr2) is an integer multiple of elem_size. + */ + +tree +array_dse_callee::build_recursive_offset (tree len_arg) +{ + if (TREE_CODE (len_arg) != SSA_NAME) + return nullptr; + + gimple *stmt = SSA_NAME_DEF_STMT (len_arg); + if (!is_gimple_assign (stmt)) + return nullptr; + + /* Check pattern: (ptr1 - ptr2) / elem_size. */ + tree_code code = gimple_assign_rhs_code (stmt); + if (code != TRUNC_DIV_EXPR && code != RSHIFT_EXPR) + return nullptr; + + tree rhs1 = gimple_assign_rhs1 (stmt); + if (TREE_CODE (rhs1) != SSA_NAME) + return nullptr; + + gimple *def = SSA_NAME_DEF_STMT (strip_ssa_copy (rhs1)); + if (!is_gimple_assign (def) + || gimple_assign_rhs_code (def) != POINTER_DIFF_EXPR) + return nullptr; + + /* Check ptr1 and ptr2. */ + tree len = nullptr; + unsigned size = 0; + if (!check_pointer (gimple_assign_rhs1 (def), len, size) + || !check_pointer (gimple_assign_rhs2 (def), len, size) + || len != len_main_var || size != elem_size_cst) + return nullptr; + + tree rhs2 = gimple_assign_rhs2 (stmt); + if (!integer_cst_p (rhs2)) + return nullptr; + + HOST_WIDE_INT value = TREE_INT_CST_LOW (rhs2); + if (code == RSHIFT_EXPR) + value = 1 << value; + + if (value != elem_size_cst) + return nullptr; + + return rhs1; +} + +/* Build expression of recursive pointer range max. */ + +tree +array_dse_callee::build_recursive_ptr_range_max (basic_block bb, + tree array_arg, + tree offset) +{ + if (TREE_CODE (array_arg) != SSA_NAME) + return nullptr; + + tree recursive_ptr_max = nullptr; + gimple *stmt = SSA_NAME_DEF_STMT (array_arg); + + /* If ARRAY_ARG = rhs1 - offset, return rhs1's range max directly. */ + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR) + { + tree rhs1 = gimple_assign_rhs1 (stmt); + tree rhs2 = gimple_assign_rhs2 (stmt); + if (TREE_CODE (rhs2) == SSA_NAME) + { + stmt = SSA_NAME_DEF_STMT (rhs2); + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == NEGATE_EXPR + && gimple_assign_rhs1 (stmt) == offset) + recursive_ptr_max = get_var_range (bb, rhs1).max (); + } + } + + if (!recursive_ptr_max) + { + auto range1 = get_var_range (bb, array_arg); + auto range2 = get_var_range (bb, offset); + recursive_ptr_max = plus_tree (range1.max (), range2.max ()); + } + + return recursive_ptr_max; +} + +bool +array_dse_callee::tail_recursive_call_p (gimple *stmt) +{ + if (stmt->next) + return false; + + basic_block bb = gimple_bb (stmt); + return single_succ_p (bb) && return_bb_p (single_succ (bb)); +} + +bool +array_dse_callee::return_bb_p (basic_block bb) const +{ + return bb && single_succ_p (bb) && + single_succ (bb) == EXIT_BLOCK_PTR_FOR_FN (cfun); +} + +/* Calculate the range of a loop variable according to initial value and + loop exit condition. */ + +bool +array_dse_callee::calc_loop_var_range (loop_p loop) +{ + if (!loops_state_satisfies_p (LOOPS_HAVE_PREHEADERS) + && loops_state_satisfies_p (LOOPS_MAY_HAVE_MULTIPLE_LATCHES)) + return false; + + basic_block header = loop->header; + for (auto gsi = gsi_start_phis (header); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gphi *phi = as_a (gsi_stmt (gsi)); + tree result = gimple_phi_result (phi); + if (!loop_var_p (loop, result)) + continue; + + tree iterate_var = PHI_ARG_DEF_FROM_EDGE (phi, loop_latch_edge (loop)); + int step = calc_loop_var_step (result, iterate_var); + unsigned abs_step = static_cast (std::abs (step)); + if (!step) + continue; + + if (POINTER_TYPE_P (TREE_TYPE (result))) + { + if (abs_step != elem_size_cst) + return false; + loop_ptrs[loop].add (result); + } + else if (TREE_CODE (TREE_TYPE (result)) != INTEGER_TYPE + || abs_step != 1) + return false; + + tree init_var = PHI_ARG_DEF_FROM_EDGE (phi, loop_preheader_edge (loop)); + auto init_range = get_var_range (header, init_var); + tree min = step > 0 ? init_range.min () : build_value (RANGE_NINF); + tree max = step > 0 ? build_value (RANGE_INF) : init_range.max (); + auto new_range = value_range{min, max}; + for (auto bb : get_all_dominated_blocks (CDI_DOMINATORS, header)) + { + if (!find_var_range (result, bb)) + { + var_range[result][bb] = new_range; + continue; + } + + auto &range = var_range[result][bb]; + min = max_tree (bb, min, range.min ()); + max = min_tree (bb, max, range.max ()); + if (!min || !max) + return false; + range = value_range {min, max}; + } + } + + if (!check_loop_exits (loop)) + return false; + + return true; +} + +bool +array_dse_callee::check_loop_exits (loop_p loop) +{ + for (auto edge : get_loop_exit_edges (loop)) + { + gimple *stmt = gsi_stmt (gsi_last_bb (edge->src)); + if (gimple_code (stmt) != GIMPLE_COND) + continue; + + gcond *cond = as_a (stmt); + tree lhs = gimple_cond_lhs (cond); + tree rhs = gimple_cond_rhs (cond); + + bool lhs_cand_p = loop_var_p (loop, lhs) || iterate_var_p (loop, lhs); + bool rhs_cand_p = loop_var_p (loop, rhs) || iterate_var_p (loop, rhs); + if (!lhs_cand_p && !rhs_cand_p) + continue; + + tree step = nullptr; + if (POINTER_TYPE_P (TREE_TYPE (lhs)) + && POINTER_TYPE_P (TREE_TYPE (rhs))) + { + if (TREE_CODE (lhs) != SSA_NAME || TREE_CODE (rhs) != SSA_NAME) + return false; + step = elem_size; + } + else if (TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE + && TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE) + { + if (!lhs_cand_p && !integer_cst_p (rhs)) + return false; + step = integer_one_node; + } + else + return false; + + tree_code code = gimple_cond_code (cond); + if (edge->flags & EDGE_TRUE_VALUE) + code = opposite_cond_code (code); + + if (!fill_loop_var_range (loop, code, lhs, rhs, step)) + return false; + + if (iterate_var_p (loop, lhs)) + lhs = get_loop_var (loop, lhs); + + if (!fill_loop_var_range (loop, code, lhs, rhs, step)) + return false; + } + + return true; +} + +/* fill loop variable range according to loop exit's condition and step. */ + +bool +array_dse_callee::fill_loop_var_range (loop_p loop, tree_code code, + tree lhs, tree rhs, tree step) +{ + for (auto bb : get_all_dominated_blocks (CDI_DOMINATORS, loop->header)) + { + auto lhs_range = get_var_range (bb, lhs); + auto rhs_range = get_var_range (bb, rhs); + tree lhs_min = lhs_range.min (); + tree lhs_max = lhs_range.max (); + tree rhs_min = rhs_range.min (); + tree rhs_max = rhs_range.max (); + bool in_loop = flow_bb_inside_loop_p (loop, bb); + + switch (code) + { + case LT_EXPR: + lhs_max = in_loop ? minus_tree (rhs_max, step) : rhs_max; + rhs_min = in_loop ? plus_tree (lhs_min, step) : lhs_min; + break; + case LE_EXPR: + lhs_max = in_loop ? rhs_max : plus_tree (rhs_max, step); + rhs_min = in_loop ? lhs_min : minus_tree (lhs_min, step); + break; + case GT_EXPR: + lhs_min = in_loop ? plus_tree (rhs_min, step) : rhs_min; + rhs_max = in_loop ? minus_tree (lhs_max, step) : lhs_max; + break; + case GE_EXPR: + lhs_min = in_loop ? rhs_min : minus_tree (rhs_min, step); + rhs_max = in_loop ? lhs_max : plus_tree (lhs_max, step); + break; + default: + return false; + } + + if (loop_var_p (loop, lhs) || iterate_var_p (loop, lhs)) + var_range[lhs][bb] = value_range{lhs_min, lhs_max}; + if (loop_var_p (loop, rhs) || iterate_var_p (loop, rhs)) + var_range[rhs][bb] = value_range{rhs_min, rhs_max}; + + if (integer_onep (step) && loop_var_p (loop, lhs) + && !fill_loop_ptr_range (loop, bb, minus_tree (lhs_max, lhs_min))) + return false; + } + + return true; +} + +/* If the variable in loop exit's condition is a integer, like + for (i = 0; i < n; i++) + + fill other pointers' range in the same loop. + */ + +bool +array_dse_callee::fill_loop_ptr_range (loop_p loop, basic_block bb, + tree loop_length) +{ + if (!loop_length) + return false; + + auto ret = compare_tree (bb, loop_length, integer_zero_node); + if (!ret) + return false; + + if (ret == LT) + { + unreachable_blocks.add (bb); + return true; + } + + tree length = mult_tree (bb, loop_length, elem_size); + if (!length) + return false; + + for (auto ptr : loop_ptrs[loop]) + { + auto &range = var_range[ptr][bb]; + tree min = range.min (); + tree max = range.max (); + if (infinite_p (min) != infinite_kind::NON_INF) + { + if (infinite_p (max) != infinite_kind::NON_INF) + return false; + min = minus_tree (max, length); + } + else if (infinite_p (max) != infinite_kind::NON_INF) + { + if (infinite_p (min) != infinite_kind::NON_INF) + return false; + max = plus_tree (min, length); + } + else + return false; + + range = value_range{min, max}; + } + + return true; +} + +bool +array_dse_callee::loop_header_p (basic_block bb) +{ + return bb_loop_header_p (bb) + && (!main_loop || bb->loop_father != main_loop); +} + +bool +array_dse_callee::iterate_var_p (loop_p loop, tree var) +{ + if (!var) + return false; + + tree loop_var = get_loop_var (loop, var); + return loop_var && loop_var != var; +} + +/* Find the loop variable from the GIVEN var throught its def chain. */ + +tree +array_dse_callee::get_loop_var (loop_p loop, tree var) +{ + if (TREE_CODE (var) != SSA_NAME || !SSA_NAME_VAR (var)) + return nullptr; + + tree result = nullptr; + + auto_bitmap visited; + auto_vec worklist; + worklist.safe_push (var); + + while (!worklist.is_empty ()) + { + tree t = worklist.pop (); + if (TREE_CODE (var) != SSA_NAME) + return nullptr; + + if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t))) + continue; + + if (loop_var_p (loop, t) && SSA_NAME_VAR (t) == SSA_NAME_VAR (var)) + { + if (result && result != t) + return nullptr; + result = t; + continue; + } + + gimple *stmt = SSA_NAME_DEF_STMT (t); + basic_block bb = gimple_bb (stmt); + if (!bb || !flow_bb_inside_loop_p (loop, gimple_bb (stmt))) + return nullptr; + + if (gimple_code (stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + worklist.safe_push (gimple_phi_arg_def (stmt, i)); + continue; + } + + if (!is_gimple_assign (stmt) + || (gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR + && gimple_assign_rhs_code (stmt) != PLUS_EXPR)) + return nullptr; + + worklist.safe_push (gimple_assign_rhs1 (stmt)); + } + + return result; +} + +bool +array_dse_callee::find_var_range (tree var, basic_block bb) +{ + auto iter1 = var_range.find (var); + if (iter1 == var_range.end ()) + return false; + + auto iter2 = iter1->second.find (bb); + return iter2 != iter1->second.end (); +} + +array_dse_edge::array_dse_edge (cgraph_edge *edge, array_dse_callee *callee) + : call_edge (edge), + callee (callee) +{ +} + +bool +array_dse_edge::analyze () +{ + cfun_saver save (call_edge->caller, LOOPS_NORMAL); + + if (gimple_call_num_args (call_edge->call_stmt) != callee->PARAM_NUM) + return false; + + return find_local_array_from_arg () && check_array_usage () + && check_len_arg_range (); +} + +bool +array_dse_edge::fully_redundant () +{ + return array_arg_start > read_upper_bound; +} + +tree +array_dse_edge::get_bound_addr () +{ + unsigned HOST_WIDE_INT bound_size = (read_upper_bound + 1) * elem_size; + tree bound_size_expr = build_int_cst (size_type_node, bound_size); + + tree addr_type = build_pointer_type (TREE_TYPE (array)); + tree array_addr = build1 (ADDR_EXPR, addr_type, array); + + return build2 (POINTER_PLUS_EXPR, addr_type, array_addr, bound_size_expr); +} + +/* Find the local array used by call argument. */ + +bool +array_dse_edge::find_local_array_from_arg () +{ + tree arg = gimple_call_arg (call_edge->call_stmt, callee->array_param_index); + + while (TREE_CODE (arg) == ADDR_EXPR || TREE_CODE (arg) == MEM_REF) + arg = TREE_OPERAND (arg, 0); + + if (!arg || !VAR_P (arg) || TREE_CODE (TREE_TYPE (arg)) != ARRAY_TYPE + || decl_function_context (arg) != current_function_decl) + return false; + + array = arg; + elem_size = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array)))); + array_size = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (array))) / elem_size; + + return true; +} + +bool +array_dse_edge::check_array_usage () +{ + if (!collect_array_accesses ()) + return false; + + if (!find_inner_array ()) + return false; + + for (auto [var, stmt] : array_accesses) + if (!check_access_kind (stmt, var)) + return false; + + collect_call_block_succs (); + if (!calc_read_bound () || !calc_array_arg_start ()) + return false; + + if (call_block_succs.contains (gimple_bb (call_edge->call_stmt)) + && !check_optimized_area_rewrite ()) + return false; + + return true; +} + +bool +array_dse_edge::collect_array_accesses () +{ + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + if (bb->flags & BB_IRREDUCIBLE_LOOP) + return false; + + for (auto gsi = gsi_start_phis (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gphi *phi = as_a (gsi_stmt (gsi)); + tree result = gimple_phi_result (phi); + if (walk_tree (&result, sub_expr_p, array, nullptr)) + if (!check_array_access (phi, result)) + return false; + + for (unsigned i = 0; i < gimple_phi_num_args (phi); i++) + { + tree arg = gimple_phi_arg_def (phi, i); + if (!walk_tree (&arg, sub_expr_p, array, nullptr)) + continue; + + if (!check_array_access (phi, arg)) + return false; + } + } + + for (auto gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *stmt = gsi_stmt (gsi); + if (gimple_clobber_p (stmt) || call_stmt_p (stmt)) + continue; + + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + tree var = gimple_op (stmt, i); + if (!var) + continue; + + if (!walk_tree (&var, sub_expr_p, array, nullptr)) + continue; + + if (!is_gimple_assign (stmt)) + return false; + + if (!check_array_access (stmt, var)) + return false; + } + } + + } + + renumber_gimple_stmt_uids (cfun); + + return !array_accesses.is_empty (); +} + +bool +array_dse_edge::check_array_access (gimple *stmt, tree var) +{ + if (array_ref_p (var)) + return gimple_assign_single_p (stmt) && !array_accesses.put (var, stmt); + + if (array_addr_p (var)) + return check_array_address (stmt, var); + + return false; +} + +bool +array_dse_edge::check_array_address (gimple *stmt, tree addr) +{ + if (gimple_code (stmt) == GIMPLE_PHI) + return check_array_address (as_a (stmt), addr); + + if (is_gimple_assign (stmt)) + return check_array_address (as_a (stmt), addr); + + return false; +} + +bool +array_dse_edge::check_array_address (gphi *phi, tree addr) +{ + tree result = gimple_phi_result (phi); + if (TREE_CODE (result) != SSA_NAME) + return false; + + if (array_address_vars.contains (result)) + return true; + + for (unsigned i = 0; i < gimple_phi_num_args (phi); i++) + { + tree arg = gimple_phi_arg_def (phi, i); + if (arg == addr) + continue; + + if (TREE_CODE (arg) != SSA_NAME) + return false; + + /* Only support simple loop variable: VAR is the initial address of + phi RESULT and other ARG must be defined by RESULT + offset. */ + gimple *stmt = SSA_NAME_DEF_STMT (arg); + if (!is_gimple_assign (stmt) + || gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR + || gimple_assign_rhs1 (stmt) != result) + return false; + } + + array_address_vars.add (result); + + return check_access_from_address (result); +} + +bool +array_dse_edge::check_array_address (gassign *assign, tree addr) +{ + if (!gimple_assign_single_p (assign) + && gimple_assign_rhs_code (assign) != POINTER_PLUS_EXPR + && gimple_assign_rhs1 (assign) != addr) + return false; + + tree lhs = gimple_assign_lhs (assign); + if (TREE_CODE (lhs) != SSA_NAME) + return false; + + array_address_vars.add (lhs); + + return check_access_from_address (lhs); +} + +bool +array_dse_edge::check_access_from_address (tree addr) +{ + gimple *stmt; + imm_use_iterator iter; + FOR_EACH_IMM_USE_STMT (stmt, iter, addr) + { + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + { + tree op = gimple_op (stmt, i); + if (walk_tree (&op, sub_expr_p, addr, nullptr) + && !check_array_access (stmt, op)) + return false; + } + } + + return true; +} + +bool +array_dse_edge::check_access_kind (gimple *stmt, tree var) +{ + gcc_assert (gimple_assign_single_p (stmt)); + + auto &kind = access_kinds.get_or_insert (var); + + tree lhs = gimple_assign_lhs (stmt); + if (var == lhs) + { + kind = WRITE; + return true; + } + + gcc_assert (var == gimple_assign_rhs1 (stmt)); + + if (!inner_array) + { + kind = READ; + return true; + } + + auto_bitmap visited; + kind = check_access_kind_iterate (lhs, visited); + return kind != ACCESS_ERROR; +} + +access_kind +array_dse_edge::check_access_kind_iterate (tree var, auto_bitmap &visited) +{ + if (!var || TREE_CODE (var) != SSA_NAME) + return ACCESS_ERROR; + + if (!bitmap_set_bit (visited, SSA_NAME_VERSION (var))) + return NONE; + + int kind = NONE; + + imm_use_iterator iter; + gimple *stmt = nullptr; + FOR_EACH_IMM_USE_STMT (stmt, iter, var) + { + if (walk_stmt_load_store_ops (stmt, var, find_base, nullptr)) + kind |= READ; + + if (walk_stmt_load_store_ops (stmt, var, nullptr, find_base)) + kind |= WRITE; + + if (kind) + continue; + + tree next_var = nullptr; + if (is_gimple_assign (stmt)) + { + if ((!gimple_assign_single_p (stmt) && !gimple_assign_cast_p (stmt)) + || gimple_assign_rhs1 (stmt) != var) + return ACCESS_ERROR; + + tree lhs = gimple_assign_lhs (stmt); + if (array_ref_p (lhs)) + { + kind |= READ; + continue; + } + + next_var = lhs; + } + else if (gimple_code (stmt) == GIMPLE_PHI) + { + if (gimple_phi_arg_p (stmt, var)) + next_var = gimple_phi_result (stmt); + } + else if (gimple_code (stmt) == GIMPLE_COND) + { + if (gimple_cond_lhs (stmt) == var || gimple_cond_rhs (stmt) == var) + { + kind |= READ; + continue; + } + } + + access_kind next_kind = check_access_kind_iterate (next_var, visited); + if (next_kind == ACCESS_ERROR) + return ACCESS_ERROR; + + kind |= next_kind; + } + + return static_cast (kind); +} + +bool +array_dse_edge::find_inner_array () +{ + tree type = TREE_TYPE (array); + unsigned ptr_layers = get_ptr_layers (type); + gcc_assert (ptr_layers); + + /* No inner source array. */ + if (ptr_layers == 1) + { + inner_elem_type = TREE_TYPE (array); + return true; + } + + /* It's hard to trace all source of array. */ + if (ptr_layers > 2 + || TREE_CODE (TREE_TYPE (TREE_TYPE (type))) != RECORD_TYPE) + return false; + + inner_elem_type = TREE_TYPE (TREE_TYPE (type)); + + for (auto [var, stmt] : array_accesses) + { + tree lhs = gimple_get_lhs (stmt); + if (lhs != var) + continue; + + if (!array_ref_p (lhs) || !is_gimple_assign (stmt)) + return false; + + tree rhs = gimple_assign_rhs1 (stmt); + if (array_ref_p (rhs)) + continue; + + if (TREE_CODE (rhs) != SSA_NAME) + return false; + + tree base = rhs; + gimple *def_stmt = SSA_NAME_DEF_STMT (base); + while (is_gimple_assign (def_stmt) + && gimple_assign_rhs_code (def_stmt) == POINTER_PLUS_EXPR) + { + base = gimple_assign_rhs1 (def_stmt); + if (TREE_CODE (base) != SSA_NAME) + return false; + def_stmt = SSA_NAME_DEF_STMT (base); + } + + if (!gimple_call_builtin_p (def_stmt, BUILT_IN_CALLOC)) + return false; + + /* Only support unique source. The inner_array must be used only once, + assigned its address to the candidate array. */ + if (inner_array) + return false; + + /* array: T *[], base: T *. */ + if (TREE_TYPE (TREE_TYPE (array)) != TREE_TYPE (base)) + return false; + + if (!unique_use_p (base, stmt) || !initialize_assign_p (stmt)) + return false; + + inner_array = base; + } + + return true; +} + +bool +array_dse_edge::unique_use_p (tree var, gimple *unique_assign) const +{ + auto_vec worklist; + auto_bitmap visited; + worklist.safe_push (var); + + while (!worklist.is_empty ()) + { + tree t = worklist.pop (); + if (TREE_CODE (t) != SSA_NAME) + return false; + + if (!bitmap_set_bit (visited, SSA_NAME_VERSION (t))) + continue; + + imm_use_iterator iter; + gimple *stmt = nullptr; + FOR_EACH_IMM_USE_STMT (stmt, iter, t) + { + if (gimple_call_builtin_p (stmt, BUILT_IN_FREE)) + continue; + + if (!is_gimple_assign (stmt)) + return false; + + if (stmt == unique_assign) + continue; + + worklist.safe_push (gimple_assign_lhs (stmt)); + } + } + + return true; +} + +bool +array_dse_edge::initialize_assign_p (gimple *stmt) const +{ + if (!stmt || !gimple_bb (stmt)) + return false; + + hash_set preds; + auto_vec worklist; + worklist.safe_push (gimple_bb (stmt)); + + while (!worklist.is_empty ()) + { + basic_block bb = worklist.pop (); + if (preds.add (bb)) + continue; + + for (auto e : bb->preds) + worklist.safe_push (e->src); + } + + for (auto [var, access_stmt] : array_accesses) + { + if (access_stmt == stmt) + continue; + + if (preds.contains (gimple_bb (access_stmt))) + return false; + } + + return true; +} + +bool +array_dse_edge::calc_read_bound () +{ + for (auto [var, stmt] : array_accesses) + { + if (!after_call_stmt_p (stmt) || !read_array_p (var)) + continue; + + auto range = calc_ref_range (var); + if (!integer_cst_p (range.max ())) + return false; + + auto max = tree_to_shwi (range.max ()); + if (max % elem_size) + return false; + + if (max / elem_size > read_upper_bound) + read_upper_bound = max / elem_size; + } + + return true; +} + +value_range +array_dse_edge::calc_ref_range (tree var) +{ + tree_code code = TREE_CODE (var); + /* Array_ref's second op is an index. Convert it to address offset. */ + if (code == ARRAY_REF) + { + auto r = calc_offset_range (TREE_OPERAND (var, 1)); + if (r.varying_p ()) + return r; + + gcc_assert (integer_cst_p (r.min ()) && integer_cst_p (r.max ())); + return make_range (tree_to_shwi (r.min ()) * elem_size, + tree_to_shwi (r.max ()) * elem_size); + } + + gcc_assert (code == MEM_REF); + auto r1 = calc_addr_range (TREE_OPERAND (var, 0)); + auto r2 = calc_offset_range (TREE_OPERAND (var, 1)); + + return value_range{plus_tree (r1.min (), r2.min ()), + plus_tree (r1.max (), r2.max ())}; +} + +value_range +array_dse_edge::calc_addr_range (tree var) +{ + if (array_address_vars.contains (var)) + { + gcc_assert (TREE_CODE (var) == SSA_NAME); + gimple *stmt = SSA_NAME_DEF_STMT (var); + if (is_gimple_assign (stmt)) + { + auto r1 = calc_addr_range (gimple_assign_rhs1 (stmt)); + auto r2 = calc_offset_range (gimple_assign_rhs2 (stmt)); + return value_range{plus_tree (r1.min (), r2.min ()), + plus_tree (r1.max (), r2.max ())}; + } + + return calc_simple_loop_range (var); + } + + if (TREE_CODE (var) != ADDR_EXPR) + return value_range{RANGE_TYPE}; + + tree op = TREE_OPERAND (var, 0); + if (op == array) + return make_range (0); + + if (!array_ref_p (op)) + return value_range{RANGE_TYPE}; + + return calc_ref_range (op); +} + +value_range +array_dse_edge::calc_offset_range (tree offset) +{ + tree var = strip_ssa_copy (offset); + if (integer_cst_p (var)) + return make_range (tree_to_shwi (var)); + + if (TREE_CODE (var) != SSA_NAME) + return value_range{RANGE_TYPE}; + + gimple *stmt = SSA_NAME_DEF_STMT (var); + if (gimple_code (stmt) == GIMPLE_PHI) + return calc_simple_loop_range (var); + + if (!is_gimple_assign (stmt) || gimple_assign_rhs_code (stmt) != MULT_EXPR + || !integer_cst_p (gimple_assign_rhs2 (stmt)) + || TREE_INT_CST_LOW (gimple_assign_rhs2 (stmt)) != elem_size) + return value_range{RANGE_TYPE}; + + auto range = calc_offset_range (gimple_assign_rhs1 (stmt)); + if (!integer_cst_p (range.min ()) || !integer_cst_p (range.max ())) + return value_range{RANGE_TYPE}; + + return make_range (tree_to_shwi (range.min ()) * elem_size, + tree_to_shwi (range.max ()) * elem_size); +} + +value_range +array_dse_edge::calc_simple_loop_range (tree var) +{ + gimple *stmt = SSA_NAME_DEF_STMT (var); + basic_block bb = gimple_bb (stmt); + loop_p loop = bb->loop_father; + + if (!loop || loop->header != bb || !loop->any_upper_bound) + return value_range{RANGE_TYPE}; + + tree init_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_preheader_edge (loop)); + tree iterate_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_latch_edge (loop)); + + value_range init_range; + if (array_addr_p (init_var)) + init_range = calc_addr_range (init_var); + else + init_range = calc_offset_range (init_var); + + if (!init_range.singleton_p () || !integer_cst_p (init_range.min ())) + return value_range{RANGE_TYPE}; + + HOST_WIDE_INT init_value = tree_to_shwi (init_range.min ()); + int step = calc_loop_var_step (var, iterate_var); + int upper_bound = loop->nb_iterations_upper_bound.to_shwi (); + + return make_range (init_value, init_value + step * upper_bound); +} + +void +array_dse_edge::collect_call_block_succs () +{ + basic_block call_block = gimple_bb (call_edge->call_stmt); + auto_vec worklist; + for (auto e : call_block->succs) + worklist.safe_push (e->dest); + + while (!worklist.is_empty ()) + { + basic_block bb = worklist.pop (); + if (call_block_succs.add (bb)) + continue; + + for (auto e : bb->succs) + worklist.safe_push (e->dest); + } +} + +bool +array_dse_edge::calc_array_arg_start () +{ + tree array_arg = gimple_call_arg (call_edge->call_stmt, + callee->array_param_index); + if (!array_addr_p (array_arg)) + return false; + + auto range = calc_addr_range (array_arg); + if (!range.singleton_p () || !integer_cst_p (range.min ())) + return false; + + auto value = tree_to_shwi (range.min ()); + if (value % elem_size) + return false; + + array_arg_start = value / elem_size; + return true; +} + +bool +array_dse_edge::check_optimized_area_rewrite () +{ + tree arg = gimple_call_arg (call_edge->call_stmt, callee->len_param_index); + if (!arg) + return false; + + tree var = strip_ssa_copy (arg); + if (!var || TREE_CODE (var) != SSA_NAME) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (var); + loop_p loop = gimple_bb (stmt)->loop_father; + if (!loop || !loop_var_p (loop, var)) + return false; + + /* To make sure the optimized area of array is fully rewritten, the loop + step must be 1. We only support one iterate stmt now. */ + tree iterate_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_latch_edge (loop)); + hash_set iterate_stmts; + if (calc_loop_var_step (var, iterate_var, &iterate_stmts) != 1 + || iterate_stmts.elements () != 1) + return false; + + gimple *iter_stmt = *iterate_stmts.begin (); + if (gimple_assign_rhs1 (iter_stmt) != var) + return false; + + /* Check if the element has been fully written. */ + basic_block bb = gimple_bb (iter_stmt); + if (!check_full_write_elem (bb, gimple_assign_lhs (iter_stmt))) + return false; + + /* Check if the start address is less equal than the read_upper_bound. */ + tree init_var = PHI_ARG_DEF_FROM_EDGE (stmt, loop_preheader_edge (loop)); + init_var = strip_init_var (init_var, var); + if (!init_var) + return false; + + auto range = calc_offset_range (init_var); + if (!integer_cst_p (range.min ()) || !integer_cst_p (range.max ())) + return false; + + len_arg_min = tree_to_shwi (range.min ()); + + return tree_to_shwi (range.max ()) + array_arg_start <= read_upper_bound; +} + +bool +array_dse_edge::check_full_write_elem (basic_block bb, tree index) +{ + hash_set visited_fields; + + for (auto [var, stmt] : array_accesses) + { + /* Must in the same block. */ + if (gimple_bb (stmt) != bb) + continue; + + if (!write_array_p (var)) + continue; + + if (TREE_CODE (var) != MEM_REF + || !array_index_of_addr_p (index, TREE_OPERAND (var, 0)) + || !integer_zerop (TREE_OPERAND (var, 1))) + continue; + + /* Directly write to array. */ + tree lhs = gimple_assign_lhs (stmt); + if (var == lhs) + return true; + else if (!inner_array) + continue; + + imm_use_iterator iter; + gimple *use_stmt = nullptr; + FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) + { + tree ref = gimple_get_lhs (use_stmt); + if (!ref || TREE_CODE (ref) != COMPONENT_REF) + continue; + + visited_fields.add (TREE_OPERAND (ref, 1)); + } + } + + return inner_array + && visited_fields.elements () == fields_length (inner_elem_type); +} + +bool +array_dse_edge::array_index_of_addr_p (tree index, tree addr) +{ + if (TREE_CODE (index) != SSA_NAME || TREE_CODE (addr) != SSA_NAME) + return false; + + /* Check pattern: addr = &array + offset. */ + gimple *stmt = SSA_NAME_DEF_STMT (addr); + if (!is_gimple_assign (stmt) + || gimple_assign_rhs_code (stmt) != POINTER_PLUS_EXPR) + return false; + + tree rhs1 = gimple_assign_rhs1 (stmt); + if (TREE_CODE (rhs1) != ADDR_EXPR || TREE_OPERAND (rhs1, 0) != array) + return false; + + tree rhs2 = gimple_assign_rhs2 (stmt); + if (TREE_CODE (rhs2) != SSA_NAME) + return false; + + /* Check pattern: offset = index * elem_size. */ + stmt = SSA_NAME_DEF_STMT (rhs2); + if (!is_gimple_assign (stmt) || gimple_assign_rhs_code (stmt) != MULT_EXPR) + return false; + + rhs1 = gimple_assign_rhs1 (stmt); + rhs2 = gimple_assign_rhs2 (stmt); + + return strip_ssa_copy (rhs1) == index && integer_cst_p (rhs2) + && TREE_INT_CST_LOW (rhs2) == elem_size; +} + +tree +array_dse_edge::strip_init_var (tree init_var, tree var) +{ + tree last = var; + while (true) + { + if (TREE_CODE (init_var) != SSA_NAME) + break; + + gimple *stmt = SSA_NAME_DEF_STMT (init_var); + loop_p loop = gimple_bb (stmt)->loop_father; + if (!loop || !loop_var_p (loop, init_var)) + break; + + auto latch_edge = loop_latch_edge (loop); + auto preheader_edge = loop_preheader_edge (loop); + if (!latch_edge || !preheader_edge + || PHI_ARG_DEF_FROM_EDGE (stmt, latch_edge) != last) + break; + + last = init_var; + init_var = PHI_ARG_DEF_FROM_EDGE (stmt, preheader_edge); + } + + return strip_ssa_copy (init_var); +} + +bool +array_dse_edge::check_len_arg_range () +{ + return check_len_arg_lower_bound () + && check_len_arg_upper_bound (); +} + +/* Check: ARG >= 1 (assumption in callee analysis). */ + +bool +array_dse_edge::check_len_arg_lower_bound () +{ + if (len_arg_min >= 1) + return true; + + /* If the len_arg_min recorded previous doesn't meet the condition, try to + update it by checking condition jump. */ + tree arg = gimple_call_arg (call_edge->call_stmt, callee->len_param_index); + if (!arg) + return false; + + tree var = strip_ssa_copy (arg); + if (!var || TREE_CODE (var) != SSA_NAME) + return false; + + basic_block call_block = gimple_bb (call_edge->call_stmt); + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + gimple *stmt = last_stmt (bb); + if (!stmt || gimple_code (stmt) != GIMPLE_COND) + continue; + + tree_code code = gimple_cond_code (stmt); + tree lhs = gimple_cond_lhs (stmt); + tree rhs = gimple_cond_rhs (stmt); + + if (lhs != var || !integer_cst_p (rhs)) + continue; + + edge true_edge; + edge false_edge; + extract_true_false_edges_from_block (bb, &true_edge, &false_edge); + if (!true_edge || !false_edge) + continue; + + if (dominated_by_p (CDI_DOMINATORS, call_block, false_edge->dest)) + code = opposite_cond_code (code); + else if (!dominated_by_p (CDI_DOMINATORS, call_block, true_edge->dest)) + continue; + + HOST_WIDE_INT rvalue = TREE_INT_CST_LOW (rhs); + switch (code) + { + case GT_EXPR: + len_arg_min = std::max (len_arg_min, rvalue + 1); + break; + case GE_EXPR: + [[fallthrough]]; + case EQ_EXPR: + len_arg_min = std::max (len_arg_min, rvalue); + break; + case NE_EXPR: + if (len_arg_min == rvalue) + len_arg_min++; + break; + default: + break; + } + } + + return len_arg_min >= 1; +} + +/* We can assume that the array will not be accessed out of bounds. + So we use array_size as the upper bound of len arg. */ + +bool +array_dse_edge::check_len_arg_upper_bound () +{ + return array_size <= callee->get_len_param_max (); +} + +bool +array_dse_edge::after_call_stmt_p (gimple *stmt) +{ + if (call_stmt_p (stmt)) + return false; + + basic_block bb = gimple_bb (stmt); + if (bb == gimple_bb (call_edge->call_stmt) + && gimple_uid (stmt) > gimple_uid (call_edge->call_stmt)) + return true; + + return call_block_succs.contains (bb); +} + +bool +array_dse_edge::write_array_p (tree var) +{ + auto *kind = access_kinds.get (var); + if (!kind) + return false; + + return *kind & WRITE; +} + +bool +array_dse_edge::read_array_p (tree var) +{ + auto *kind = access_kinds.get (var); + if (!kind) + return false; + + return *kind & READ; +} + +bool +array_dse_edge::call_stmt_p (gimple *stmt) const +{ + return stmt == call_edge->call_stmt; +} + +bool +array_dse_edge::array_ref_p (tree var) +{ + if (!var) + return false; + + if (TREE_CODE (var) == ARRAY_REF) + return TREE_OPERAND (var, 0) == array; + + return (TREE_CODE (var) == MEM_REF && array_addr_p (TREE_OPERAND (var, 0))); +} + +bool +array_dse_edge::array_addr_p (tree var) +{ + if (array_address_vars.contains (var)) + return true; + + if (TREE_CODE (var) != ADDR_EXPR) + return false; + + tree op = TREE_OPERAND (var, 0); + return op == array || array_ref_p (op); +} + +unsigned +ipa_array_dse::execute () +{ + cgraph_node *node; + FOR_EACH_FUNCTION (node) + { + if (!node->real_symbol_p () || !node->definition + || !node->has_gimple_body_p () || node->inlined_to) + continue; + node->get_body (); + + if (!DECL_STRUCT_FUNCTION (node->decl)) + continue; + + nodes.safe_push (node); + } + + if (!find_array_dse_candidate_callees ()) + { + if (dump_file) + fprintf (dump_file, "Fail finding array dse candidate callees\n"); + return 0; + } + + if (!find_array_dse_candidate_edges ()) + { + if (dump_file) + fprintf (dump_file, "Fail finding array dse candidate edges\n"); + return 0; + } + + for (auto edge : candidate_edges) + apply_array_dse (edge); + + symtab->remove_unreachable_nodes (dump_file); + + return TODO_update_ssa; +} + +bool +ipa_array_dse::find_array_dse_candidate_callees () +{ + if (dump_file) + fprintf (dump_file, "Finding array dse candidate callees\n\n"); + + for (auto node : nodes) + { + if (!tree_versionable_function_p (node->decl) + || !opt_for_fn (node->decl, optimize)) + continue; + + const char *fn_name = node->dump_asm_name (); + if (dump_file) + fprintf (dump_file, "Analyzing callee: %s\n", fn_name); + + auto *callee = new array_dse_callee (node); + if (!callee->analyze ()) + { + delete callee; + continue; + } + + if (dump_file) + { + fprintf (dump_file, "Found candidate callee: %s\n", fn_name); + if (dump_flags & TDF_DETAILS) + dump_function_to_file (node->decl, dump_file, dump_flags); + fprintf (dump_file, "\n"); + } + + candidate_callees.safe_push (callee); + } + + return !candidate_callees.is_empty (); +} + +bool +ipa_array_dse::find_array_dse_candidate_edges () +{ + if (dump_file) + fprintf (dump_file, "Finding array dse candidate call edges\n\n"); + + for (auto *callee : candidate_callees) + { + cgraph_edge *e = callee->node->callers; + while (e && e->caller == callee->node) + e = e->next_caller; + + for (auto *c : candidate_callees) + if (e->caller == c->node) + return false; + + auto *edge = new array_dse_edge (e, callee); + if (!edge->analyze ()) + { + delete edge; + continue; + } + + if (dump_file) + { + fprintf (dump_file, "Found candidate call edge: "); + print_gimple_stmt (dump_file, e->call_stmt, 0, TDF_NONE); + if (dump_flags & TDF_DETAILS) + dump_function_to_file (e->caller->decl, dump_file, dump_flags); + fprintf (dump_file, "\n"); + } + + candidate_edges.safe_push (edge); + } + + return !candidate_edges.is_empty (); +} + +bool +ipa_array_dse::apply_array_dse (array_dse_edge *ad_edge) +{ + /* Remove call stmt if it's fully redundant. */ + if (ad_edge->fully_redundant ()) + { + cgraph_node *caller = ad_edge->call_edge->caller; + gimple *call_stmt = ad_edge->call_edge->call_stmt; + + cfun_saver save (caller); + + auto gsi = gsi_for_stmt (call_stmt); + basic_block call_bb = gimple_bb (call_stmt); + tree fndecl = gimple_call_fndecl (call_stmt); + caller->remove_stmt_references (call_stmt); + unlink_stmt_vdef (call_stmt); + if (gsi_remove (&gsi, true)) + gimple_purge_dead_eh_edges (call_bb); + cgraph_update_edges_for_call_stmt (call_stmt, fndecl, nullptr); + + return true; + } + + /* Insert array redundant bound check to callee. */ + array_dse_callee *callee = ad_edge->callee; + cgraph_node *orig_callee = ad_edge->callee->node; + cgraph_node *new_callee + = orig_callee->create_version_clone_with_body (vNULL, NULL, NULL, NULL, + NULL, "array_dse", NULL); + + if (!transform_new_callee (callee, new_callee)) + return false; + + tree bound_addr = ad_edge->get_bound_addr (); + rewrite_call_edge (ad_edge->call_edge, new_callee, bound_addr); + + return true; +} + +tree +ipa_array_dse::add_bound_param (tree param) +{ + vec *new_params = NULL; + auto_vec arg_decls; + + push_function_arg_decls (&arg_decls, cfun->decl); + gcc_checking_assert (!arg_decls.is_empty ()); + vec_safe_reserve (new_params, arg_decls.length () + 1); + + for (unsigned i = 0; i < arg_decls.length (); ++i) + { + ipa_adjusted_param adj; + + memset (&adj, 0, sizeof (adj)); + + adj.type = TREE_TYPE (arg_decls[i]); + adj.base_index = i; + adj.prev_clone_index = i; + adj.op = IPA_PARAM_OP_COPY; + new_params->quick_push (adj); + } + + tree param_name = DECL_NAME (param); + const char *name = concat (IDENTIFIER_POINTER (param_name), ".bound", NULL); + ipa_adjusted_param adj; + adj.type = TREE_TYPE (param); + adj.base_index = arg_decls.length (); + adj.prev_clone_index = arg_decls.length (); + adj.op = IPA_PARAM_OP_NEW; + new_params->quick_push (adj); + + auto adjustments = new ipa_param_body_adjustments (new_params, cfun->decl); + adjustments->modify_formal_parameters (); + delete adjustments; + + arg_decls.truncate (0); + push_function_arg_decls (&arg_decls, cfun->decl); + + tree new_param = arg_decls.last (); + DECL_NAME (new_param) = get_identifier (name); + + return get_or_create_ssa_default_def (cfun, new_param); +} + +tree +ipa_array_dse::find_array_main_var (array_dse_callee *callee) +{ + int i = 0; + tree param = DECL_ARGUMENTS (cfun->decl); + while (i++ < callee->array_param_index) + param = DECL_CHAIN (param); + + tree name; + FOR_EACH_SSA_NAME (i, name, cfun) + { + if (!SSA_NAME_IS_DEFAULT_DEF (name) + || SSA_NAME_VAR (name) != param) + continue; + + if (!callee->main_loop) + return name; + + use_operand_p use_p; + gimple *stmt; + if (!single_imm_use (name, &use_p, &stmt) + || gimple_code (stmt) != GIMPLE_PHI) + return nullptr; + + return gimple_phi_result (stmt); + } + + return nullptr; +} + +bool +ipa_array_dse::transform_new_callee (array_dse_callee *callee, + cgraph_node *new_node) +{ + cfun_saver save (new_node); + + tree bound_ssa = add_bound_param (callee->array_param); + tree array = find_array_main_var (callee); + if (!array) + return false; + + edge e; + if (callee->main_loop) + { + gimple *array_def_stmt = SSA_NAME_DEF_STMT (array); + basic_block array_def_bb = gimple_bb (array_def_stmt); + gcc_assert (gimple_code (array_def_stmt) == GIMPLE_PHI); + e = split_block_after_labels (array_def_bb); + } + else + { + basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); + gcc_assert (single_succ_p (entry_bb)); + basic_block bb = split_edge (single_succ_edge (entry_bb)); + e = single_succ_edge (bb); + } + + auto gsi = gsi_last_bb (e->src); + gimple *cond = gimple_build_cond (GE_EXPR, array, bound_ssa, nullptr, + nullptr); + gsi_insert_after (&gsi, cond, GSI_NEW_STMT); + + edge return_edge = make_edge (e->src, EXIT_BLOCK_PTR_FOR_FN (cfun), 0); + basic_block return_bb = split_edge (return_edge); + auto return_gsi = gsi_last_bb (return_bb); + gsi_insert_after (&return_gsi, gimple_build_return (nullptr), GSI_NEW_STMT); + + e->flags &= ~EDGE_FALLTHRU; + e->flags |= EDGE_FALSE_VALUE; + single_pred_edge (return_bb)->flags |= EDGE_TRUE_VALUE; + single_succ_edge (return_bb)->flags = 0; + + for (auto call_edge = new_node->callees; call_edge; + call_edge = call_edge->next_callee) + rewrite_call_edge (call_edge, new_node, bound_ssa); + + return true; +} + +void +ipa_array_dse::rewrite_call_edge (cgraph_edge *edge, cgraph_node *new_node, + tree bound_addr) +{ + auto_vec args; + gcall *call_stmt = edge->call_stmt; + gimple_stmt_iterator gsi = gsi_for_stmt (call_stmt); + cgraph_node *caller = edge->caller; + cfun_saver save (caller); + + for (unsigned i = 0; i < gimple_call_num_args (call_stmt); i++) + args.safe_push (gimple_call_arg (call_stmt, i)); + + bound_addr = force_gimple_operand_gsi (&gsi, bound_addr, true, + NULL_TREE, true, + GSI_SAME_STMT); + args.safe_push (bound_addr); + + gcall *new_call = gimple_build_call_vec (new_node->decl, args); + + if (tree vdef = gimple_vdef (call_stmt)) + { + gimple_set_vdef (new_call, vdef); + SSA_NAME_DEF_STMT (vdef) = new_call; + } + + gimple_set_vuse (new_call, gimple_vuse (call_stmt)); + gimple_call_copy_flags (new_call, call_stmt); + gimple_call_set_chain (new_call, gimple_call_chain (call_stmt)); + gsi_replace (&gsi, new_call, false); + + cgraph_update_edges_for_call_stmt (call_stmt, + gimple_call_fndecl (call_stmt), new_call); + + caller->remove_stmt_references (call_stmt); + caller->record_stmt_references (new_call); +} + +} // namespace array_dse + +namespace { + +const pass_data pass_data_ipa_array_dse = +{ + SIMPLE_IPA_PASS, /* type */ + "array-dse", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_IPA_ARRAY_DSE, /* tv_id */ + PROP_cfg | PROP_ssa, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_ipa_array_dse : public simple_ipa_opt_pass +{ +public: + pass_ipa_array_dse (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_array_dse, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return optimize >= 3 && flag_ipa_array_dse; + } + + virtual unsigned int execute (function *) + { + return array_dse::ipa_array_dse ().execute (); + } + +}; // class pass_ipa_array_dse + +} // anon namespace + +simple_ipa_opt_pass * +make_pass_ipa_array_dse (gcc::context *ctxt) +{ + return new pass_ipa_array_dse (ctxt); +} diff --git a/gcc/ipa-array-dse.h b/gcc/ipa-array-dse.h new file mode 100644 index 0000000000000000000000000000000000000000..b1f5ee611d5d1092172cddcc5bd9c6ca8cceef59 --- /dev/null +++ b/gcc/ipa-array-dse.h @@ -0,0 +1,263 @@ +/* Array dead store elimination + Copyright (C) 2021-2022 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef IPA_ARRAY_DSE_H +#define IPA_ARRAY_DSE_H + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "options.h" +#include "function.h" +#include "cfgloop.h" +#include "hash-map.h" +#include "tree-core.h" +#include "tree.h" +#include "bitmap.h" +#include "value-range.h" +#include + +namespace array_dse { + +enum compare_result +{ + COMPARE_ERROR = 0, + LT = 1, + EQ = 2, + GT = 4, + LE = LT | EQ, + GE = GT | EQ, + NE = LT | GT +}; + +enum access_kind +{ + NONE = 0, + READ = 1, + WRITE = 2, + ACCESS_ERROR = 4 +}; + +enum class infinite_kind +{ + NON_INF, + INF, + NINF +}; + +/* Address analyzer. */ +class addr_analyzer +{ +public: + tree get_address (tree var); + +private: + tree analyze_address (tree var); + +private: + hash_map address_map; +}; + +class array_dse_callee +{ +public: + array_dse_callee (cgraph_node *node); + + bool analyze (); + unsigned HOST_WIDE_INT get_len_param_max () const; + +private: + tree mult_tree (basic_block bb, tree t1, tree t2); + tree div_tree (basic_block bb, tree t1, tree t2); + tree lshift_tree (tree t1, tree t2); + tree rshift_tree (tree t1, tree t2); + tree max_tree (basic_block bb, tree t1, tree t2); + tree min_tree (basic_block bb, tree t1, tree t2); + HOST_WIDE_INT calc_tree_value (tree t, HOST_WIDE_INT n_value); + value_range calc_tree_range (basic_block bb, tree t); + bool get_factor (tree t, double &factor); + bool positive_factor_p (tree t); + value_range build_range (basic_block bb, tree_code op, + const value_range &r1, const value_range &r2); + compare_result compare_tree (basic_block bb, tree t1, tree t2); + compare_result compare_tree_by_minus (basic_block bb, tree t1, tree t2); + bool filter_function () const; + bool no_return_p () const; + bool find_main_vars (); + void find_tail_recursive_loop (tree *default_def); + bool find_candidate_array (); + bool find_length_param (); + void collect_read_write_ptrs (); + void calc_length_param_max (); + bool check_pointer (tree var, tree &len, unsigned &size); + bool check_offset (tree var, tree &len, unsigned &size, + auto_vec &worklist); + bool check_mult_expr (gimple *stmt, tree &len, unsigned &size); + int find_param_index (tree base); + bool check_array_usage (); + void calc_len_range (); + void update_len_range (basic_block start, const value_range &new_range); + value_range invert_range (const value_range &range) const; + value_range get_range_from_cond (gimple *stmt); + value_range get_var_range (basic_block bb, tree offset); + bool calc_ptr_range (); + bool check_ptr_range (); + bool check_recursive_call_arg (); + void collect_recursive_call_args (auto_vec &array_args, + auto_vec &len_args, + auto_vec &blocks, + auto_vec &is_tail_recursive_call); + void update_branch_range (basic_block bb); + tree build_recursive_offset (tree len); + tree build_recursive_ptr_range_max (basic_block bb, tree array, + tree offset); + bool tail_recursive_call_p (gimple *stmt); + bool return_bb_p (basic_block bb) const; + bool calc_loop_var_range (loop_p loop); + bool check_loop_exits (loop_p loop); + bool fill_loop_var_range (loop_p loop, tree_code code, tree lhs, + tree rhs, tree step); + bool fill_loop_ptr_range (loop_p loop, basic_block bb, + tree count_length); + bool loop_header_p (basic_block bb); + bool iterate_var_p (loop_p loop, tree var); + tree get_loop_var (loop_p loop, tree iterate_var); + bool find_var_range (tree var, basic_block bb); + +public: + cgraph_node *node = nullptr; + tree array_param = nullptr; + tree len_param = nullptr; + int array_param_index = -1; + int len_param_index = -1; + tree array_main_var = nullptr; + tree len_main_var = nullptr; + tree signed_len_var = nullptr; + tree elem_size = nullptr; + unsigned elem_size_cst = 0; + + loop_p main_loop = nullptr; + + static constexpr unsigned PARAM_NUM = 2; + +private: + addr_analyzer analyzer; + + hash_set all_ptrs; + hash_set visited_offset; + hash_map branch_start_map; + hash_map len_range_map; + std::map> var_range; + std::map> loop_ptrs; + hash_set unreachable_blocks; + + static constexpr unsigned HOST_WIDE_INT len_param_min = 1; + unsigned HOST_WIDE_INT len_param_max = 0; +}; + +class array_dse_edge +{ +public: + array_dse_edge (cgraph_edge *edge, array_dse_callee *callee); + + bool analyze (); + bool fully_redundant (); + tree get_bound_addr (); + +private: + bool find_local_array_from_arg (); + bool check_array_usage (); + bool collect_array_accesses (); + bool check_array_access (gimple *stmt, tree var); + bool check_array_address (gimple *stmt, tree addr); + bool check_array_address (gphi *phi, tree addr); + bool check_array_address (gassign *assign, tree addr); + bool check_access_from_address (tree addr); + bool check_access_kind (gimple *stmt, tree var); + access_kind check_access_kind_iterate (tree var, auto_bitmap &visited); + bool find_inner_array (); + bool unique_use_p (tree source, gimple *unique_assign) const; + bool initialize_assign_p (gimple *stmt) const; + bool calc_read_bound (); + value_range calc_ref_range (tree var); + value_range calc_addr_range (tree var); + value_range calc_offset_range (tree var); + value_range calc_simple_loop_range (tree var); + void collect_call_block_succs (); + bool calc_array_arg_start (); + bool check_optimized_area_rewrite (); + bool check_full_write_elem (basic_block bb, tree var); + bool array_index_of_addr_p (tree index, tree addr); + tree strip_init_var (tree var, tree last_var); + bool check_len_arg_range (); + bool check_len_arg_lower_bound (); + bool check_len_arg_upper_bound (); + bool after_call_stmt_p (gimple *stmt); + bool write_array_p (tree var); + bool read_array_p (tree var); + bool call_stmt_p (gimple *stmt) const; + bool array_ref_p (tree var); + bool array_addr_p (tree var); + +public: + cgraph_edge *call_edge = nullptr; + array_dse_callee *callee = nullptr; + + tree array = nullptr; + +private: + unsigned array_size = 0; + unsigned elem_size = 0; + tree inner_array = nullptr; + tree inner_elem_type = nullptr; + + hash_map array_accesses; + hash_map access_kinds; + hash_set array_address_vars; + hash_set call_block_succs; + + HOST_WIDE_INT read_upper_bound = 0; + HOST_WIDE_INT array_arg_start = 0; + HOST_WIDE_INT len_arg_min = 0; +}; + +class ipa_array_dse +{ +public: + unsigned execute (); + +private: + bool find_array_dse_candidate_callees (); + bool find_array_dse_candidate_edges (); + bool apply_array_dse (array_dse_edge *edge); + tree add_bound_param (tree param); + tree find_array_main_var (array_dse_callee *callee); + bool transform_new_callee (array_dse_callee *callee, cgraph_node *new_node); + void rewrite_call_edge (cgraph_edge *edge, cgraph_node *new_node, + tree bound_ssa); + +private: + auto_vec nodes; + auto_delete_vec candidate_callees; + auto_delete_vec candidate_edges; +}; + +} + +#endif diff --git a/gcc/ipa-hardware-detection.cc b/gcc/ipa-hardware-detection.cc index 64ef8a01153dc1479156e73cb2a774394366fb19..3ad7cea6ea882c716c46146155dfddb5f096cd27 100644 --- a/gcc/ipa-hardware-detection.cc +++ b/gcc/ipa-hardware-detection.cc @@ -192,7 +192,6 @@ pass_ipa_hardware_detection::gate (function *) { const char *ai_infer_level = getenv ("AI_INFER_LEVEL"); return (ai_infer_level - && optimize_maximum > 0 /* Only enable in lto or whole_program. */ && (in_lto_p || flag_whole_program)); } diff --git a/gcc/ipa-localize-array.cc b/gcc/ipa-localize-array.cc new file mode 100644 index 0000000000000000000000000000000000000000..4678756b20ad05a7ff1b0461c7ef3f03f395d71b --- /dev/null +++ b/gcc/ipa-localize-array.cc @@ -0,0 +1,614 @@ +/* IPA optimization to transform global calloced array to be + specific function local. + Copyright (C) 2021-2022 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "gimple-iterator.h" +#include "ssa.h" +#include "tree-pass.h" +#include "tree-cfg.h" +#include "gimplify.h" +#include "gimple-pretty-print.h" +#include "tree-into-ssa.h" +#include "ipa-utils.h" +#include "fold-const.h" +#include "tree-dfa.h" +#include "cfgloop.h" + +class array_localizer +{ +public: + array_localizer (varpool_node *var); + void localize (); + +private: + bool scalar_type_p (tree type); + bool scalar_memop_p (tree ref_val, gimple *use_stmt); + bool stmt_dominated_by_p (enum cdi_direction dir, gimple *stmt0, + gimple *stmt1); + gimple *find_calloc_stmt (gimple *stmt); + gimple *find_free_stmt (tree var); + bool check_var_store (); + bool check_var_load (); + bool find_call_edge (); + void remove_referring_stmt (gimple *stmt); + void replace_store_with_ssa (gimple *stmt, tree var_ssa); + void replace_load_with_ssa (gimple *stmt, tree var_ssa); + gimple *copy_call_without_location (gimple *stmt); + void rewrite_array (); + void insert_new_init (tree var_ssa); + void insert_new_alloc_free (tree var_ssa); + void rewrite_access_in_callee (tree var_ssa); + void remove_orig_alloc_free (); + +private: + varpool_node *var = nullptr; + tree var_type = nullptr; + ipa_ref *alloc_ref = nullptr; + ipa_ref *free_ref = nullptr; + gimple *alloc_stmt = nullptr; + gimple *free_stmt = nullptr; + cgraph_node *caller = nullptr; + cgraph_node *callee = nullptr; + cgraph_edge *call_edge = nullptr; + gimple *call_stmt = nullptr; + + bool scalar_alloc_p = false; + + auto_vec removed_stmts; +}; + +array_localizer::array_localizer (varpool_node *var) + : var (var) +{ +} + +void +array_localizer::localize () +{ + if (DECL_EXTERNAL (var->decl) || var->in_other_partition + || !var->can_remove_if_no_refs_p ()) + return; + + var_type = TREE_TYPE (var->decl); + + /* Only care about pointer variable. */ + if (!POINTER_TYPE_P (var_type)) + return; + + if (!check_var_store () || !check_var_load ()) + return; + + if (callee->used_from_other_partition + || callee->cannot_return_p () + || callee->get_availability () != AVAIL_LOCAL + || callee->has_aliases_p ()) + return; + + if (!find_call_edge ()) + return; + + { + cfun_saver save (caller); + if (!stmt_dominated_by_p (CDI_DOMINATORS, free_stmt, alloc_stmt) + || !stmt_dominated_by_p (CDI_POST_DOMINATORS, alloc_stmt, free_stmt) + || !stmt_dominated_by_p (CDI_POST_DOMINATORS, alloc_stmt, call_stmt) + || !stmt_dominated_by_p (CDI_DOMINATORS, free_stmt, call_stmt)) + return; + } + + rewrite_array (); +} + +bool +array_localizer::scalar_type_p (tree type) +{ + if (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type) + || SCALAR_FLOAT_TYPE_P (type)) + return true; + return false; +} + +bool +array_localizer::scalar_memop_p (tree ref_val, gimple *use_stmt) +{ + if (gimple_has_volatile_ops (use_stmt)) + return false; + + if (!gimple_assign_load_p (use_stmt) && !gimple_store_p (use_stmt)) + return false; + + tree type = TREE_TYPE (ref_val); + if (!POINTER_TYPE_P (type)) + return false; + + tree lhs = gimple_get_lhs (use_stmt); + tree rhs1 = gimple_assign_rhs1 (use_stmt); + tree memref = gimple_store_p (use_stmt) ? lhs : rhs1; + + HOST_WIDE_INT offset, size; + bool reverse; + memref = get_ref_base_and_extent_hwi (memref, &offset, &size, &reverse); + + if (!memref || offset || TREE_CODE (memref) != MEM_REF + || !operand_equal_p (TREE_OPERAND (memref, 0), ref_val) + || !integer_zerop (TREE_OPERAND (memref, 1)) + || !types_compatible_p (TREE_TYPE (lhs), TREE_TYPE (type))) + return false; + + /* Exclude address-escape case like *var = var */ + ssa_op_iter iter; + tree use = nullptr; + int use_count = 0; + FOR_EACH_SSA_TREE_OPERAND (use, use_stmt, iter, SSA_OP_USE) + if (operand_equal_p (use, ref_val) && use_count++) + return false; + + return true; +} + +bool +array_localizer::stmt_dominated_by_p (enum cdi_direction dir, gimple *stmt0, + gimple *stmt1) +{ + basic_block bb0 = gimple_bb (stmt0); + basic_block bb1 = gimple_bb (stmt1); + + if (bb0 == bb1) + { + renumber_gimple_stmt_uids_in_blocks (&bb0, 1); + + if (dir == CDI_DOMINATORS) + return stmt0->uid > stmt1->uid; + else + return stmt0->uid < stmt1->uid; + } + + return dominated_by_p (dir, bb0, bb1); +} + +gimple * +array_localizer::find_calloc_stmt (gimple *stmt) +{ + if (!gimple_assign_single_p (stmt)) + return nullptr; + + tree rhs = gimple_assign_rhs1 (stmt); + if (TREE_CODE (rhs) != SSA_NAME || !has_single_use (rhs)) + return nullptr; + + gimple *def_stmt = SSA_NAME_DEF_STMT (rhs); + if (!gimple_call_builtin_p (def_stmt, BUILT_IN_CALLOC)) + return nullptr; + + return def_stmt; +} + +gimple * +array_localizer::find_free_stmt (tree var) +{ + use_operand_p use_p = nullptr; + gimple *use_stmt = nullptr; + if (TREE_CODE (var) != SSA_NAME || !single_imm_use (var, &use_p, &use_stmt)) + return nullptr; + + if (!gimple_call_builtin_p (use_stmt, BUILT_IN_FREE)) + return nullptr; + + return use_stmt; +} + +bool +array_localizer::check_var_store () +{ + ipa_ref *ref = nullptr; + for (unsigned i = 0; var->iterate_referring (i, ref); i++) + { + cgraph_node *node = dyn_cast (ref->referring); + if (!node) + return false; + + if (!ref->stmt || gimple_has_volatile_ops (ref->stmt)) + return false; + + /* Only allow calloc. */ + if (ref->use == IPA_REF_STORE) + { + /* Multiple alloc is not supported yet. */ + if (alloc_ref) + return false; + + if (!gimple_store_p (ref->stmt) + || !operand_equal_p (var->decl, gimple_get_lhs (ref->stmt))) + return false; + + alloc_stmt = find_calloc_stmt (ref->stmt); + if (!alloc_stmt) + return false; + + tree arg0 = gimple_call_arg (alloc_stmt, 0); + tree arg1 = gimple_call_arg (alloc_stmt, 1); + if (TREE_CODE (arg0) != INTEGER_CST + || TREE_CODE (arg1) != INTEGER_CST) + return false; + + tree elem_size = TYPE_SIZE_UNIT (TREE_TYPE (var_type)); + if (scalar_type_p (TREE_TYPE (var_type)) + && integer_onep (arg0) + && tree_int_cst_equal (arg1, elem_size)) + scalar_alloc_p = true; + + alloc_ref = ref; + caller = node; + } + } + + return alloc_ref != nullptr; +} + +bool +array_localizer::check_var_load () +{ + ipa_ref *ref = nullptr; + for (unsigned i = 0; var->iterate_referring (i, ref); i++) + { + if (ref->use == IPA_REF_STORE) + continue; + + if (ref->use != IPA_REF_LOAD) + return false; + + if (!gimple_assign_load_p (ref->stmt) + || !operand_equal_p (var->decl, gimple_assign_rhs1 (ref->stmt))) + return false; + + tree lhs = gimple_assign_lhs (ref->stmt); + if (TREE_CODE (lhs) != SSA_NAME) + return false; + + if (!free_ref) + { + gimple *stmt = find_free_stmt (lhs); + if (stmt) + { + if (!operand_equal_p (gimple_call_arg (stmt, 0), lhs) + || ref->referring != caller) + return false; + + free_ref = ref; + free_stmt = stmt; + continue; + } + } + + gimple *use_stmt = nullptr; + imm_use_iterator iter; + FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) + { + if (is_gimple_debug (use_stmt)) + continue; + + if (!is_gimple_assign (use_stmt)) + return false; + + if (scalar_alloc_p + && !scalar_memop_p (lhs, use_stmt)) + scalar_alloc_p = false; + + /* All other reference must be in the same callee. */ + cgraph_node *node = dyn_cast (ref->referring); + if (!node || (callee && callee != node)) + return false; + + callee = node; + } + } + + return callee && callee != caller; +} + +/* Now we only allow function that is called only once by other + function (non-recursive call). */ + +bool +array_localizer::find_call_edge () +{ + cgraph_edge *e = callee->callers; + if (!e || e->next_caller || e->caller != caller) + return false; + + call_edge = e; + call_stmt = e->call_stmt; + return true; +} + +void +array_localizer::remove_referring_stmt (gimple *stmt) +{ + gimple_stmt_iterator gsi; + + if (dump_file) + { + fprintf (dump_file, "Remove statement:\n"); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS); + fprintf (dump_file, "\n"); + } + + gsi = gsi_for_stmt (stmt); + unlink_stmt_vdef (stmt); + gsi_remove (&gsi, true); + release_defs (stmt); +} + +void +array_localizer::replace_store_with_ssa (gimple *stmt, tree var_ssa) +{ + if (dump_file) + { + fprintf (dump_file, "Update store statement:\n"); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS); + } + + create_new_def_for (var_ssa, stmt, NULL); + update_stmt (stmt); + + if (dump_file) + { + fprintf (dump_file, "->\n"); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS); + fprintf (dump_file, "\n"); + } +} + +void +array_localizer::replace_load_with_ssa (gimple *stmt, tree var_ssa) +{ + if (dump_file) + { + fprintf (dump_file, "Update load statement:\n"); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS); + } + + gimple_assign_set_rhs1 (stmt, var_ssa); + update_stmt (stmt); + + if (dump_file) + { + fprintf (dump_file, "->\n"); + print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS); + fprintf (dump_file, "\n"); + } +} + +gimple * +array_localizer::copy_call_without_location (gimple *stmt) +{ + tree callee = unshare_expr_without_location (gimple_call_fndecl (stmt)); + auto_vec args; + + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + { + tree arg = gimple_call_arg (stmt, i); + args.safe_push (unshare_expr_without_location (arg)); + } + + return gimple_build_call_vec (callee, args); +} + +void +array_localizer::rewrite_array () +{ + if (dump_file) + { + fprintf (dump_file, "Localize global array: "); + print_generic_expr (dump_file, var->decl); + fprintf (dump_file, "\n\n"); + } + + cfun_saver save (callee); + + tree type = TREE_TYPE (scalar_alloc_p ? var_type : var->decl); + const char *name = get_name (var->decl); + tree var_ssa = make_temp_ssa_name (type, NULL, name ? name : ""); + + if (scalar_alloc_p) + insert_new_init (var_ssa); + else + insert_new_alloc_free (var_ssa); + + rewrite_access_in_callee (var_ssa); + remove_orig_alloc_free (); + + for (auto stmt : removed_stmts) + caller->remove_stmt_references (stmt); +} + +void +array_localizer::insert_new_init (tree var_ssa) +{ + tree init_value = build_zero_cst (TREE_TYPE (var_ssa)); + gimple *init = gimple_build_assign (var_ssa, init_value); + + basic_block entry_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); + gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (entry_bb); + gsi_insert_before (&gsi, init, GSI_SAME_STMT); +} + +void +array_localizer::insert_new_alloc_free (tree var_ssa) +{ + gimple *new_alloc = copy_call_without_location (alloc_stmt); + gimple *new_free = copy_call_without_location (free_stmt); + basic_block entry_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); + gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (entry_bb); + + gimple_set_lhs (new_alloc, var_ssa); + gsi_insert_before (&gsi, new_alloc, GSI_SAME_STMT); + + if (dump_file) + { + fprintf (dump_file, "Insert calloc statement:\n"); + print_gimple_stmt (dump_file, new_alloc, 0, TDF_VOPS); + fprintf (dump_file, "\n"); + } + + bool free_used = false; + for (auto e : EXIT_BLOCK_PTR_FOR_FN (cfun)->preds) + { + gimple *stmt = last_stmt (e->src); + if (gimple_code (stmt) != GIMPLE_RETURN) + continue; + + if (free_used) + new_free = gimple_copy (new_free); + else + free_used = true; + + auto gsi = gsi_for_stmt (stmt); + gimple_call_set_arg (new_free, 0, var_ssa); + gsi_insert_before (&gsi, new_free, GSI_SAME_STMT); + + if (dump_file) + { + fprintf (dump_file, "Insert free statement:\n"); + print_gimple_stmt (dump_file, new_free, 0, TDF_VOPS); + fprintf (dump_file, "\n"); + } + } +} + +void +array_localizer::rewrite_access_in_callee (tree var_ssa) +{ + ipa_ref *ref = nullptr; + for (unsigned i = 0; var->iterate_referring (i, ref); i++) + { + if (ref == alloc_ref || ref == free_ref) + continue; + + gcc_assert (ref->referring == callee && ref->use == IPA_REF_LOAD); + + if (scalar_alloc_p) + { + tree lhs = gimple_assign_lhs (ref->stmt); + gimple *use_stmt = nullptr; + imm_use_iterator iter; + FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) + { + if (is_gimple_debug (use_stmt)) + remove_referring_stmt (use_stmt); + else if (gimple_store_p (use_stmt)) + replace_store_with_ssa (use_stmt, var_ssa); + else if (gimple_assign_load_p (use_stmt)) + replace_load_with_ssa (use_stmt, var_ssa); + else + gcc_unreachable (); + } + remove_referring_stmt (ref->stmt); + } + else + replace_load_with_ssa (ref->stmt, var_ssa); + + removed_stmts.safe_push (ref->stmt); + } + + update_ssa (TODO_update_ssa); +} + +void +array_localizer::remove_orig_alloc_free () +{ + cfun_saver save (caller); + + /* Remove calloc() and free(). */ + remove_referring_stmt (alloc_stmt); + remove_referring_stmt (alloc_ref->stmt); + remove_referring_stmt (free_stmt); + remove_referring_stmt (free_ref->stmt); + removed_stmts.safe_push (alloc_ref->stmt); + removed_stmts.safe_push (free_ref->stmt); + + update_ssa (TODO_update_ssa); +} + +/* Execute the driver for IPA variable localization. */ + +static unsigned int +ipa_localize_array (void) +{ + cgraph_node *node = nullptr; + FOR_EACH_FUNCTION (node) + { + if (!node->real_symbol_p () || !node->definition + || !node->has_gimple_body_p () || node->inlined_to) + continue; + node->get_body (); + } + + varpool_node *var = nullptr; + FOR_EACH_VARIABLE (var) + array_localizer (var).localize (); + + return 0; +} + +namespace { + +const pass_data pass_data_ipa_localize_array = +{ + SIMPLE_IPA_PASS, /* type */ + "localize-array", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_IPA_LOCALIZE_ARRAY, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ +}; + +class pass_ipa_localize_array : public simple_ipa_opt_pass +{ +public: + pass_ipa_localize_array (gcc::context *ctxt) + : simple_ipa_opt_pass (pass_data_ipa_localize_array, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return optimize >= 3 && flag_ipa_localize_array; + } + + virtual unsigned int execute (function *) { return ipa_localize_array (); } + +}; // class pass_ipa_localize_array + +} // anon namespace + +simple_ipa_opt_pass * +make_pass_ipa_localize_array (gcc::context *ctxt) +{ + return new pass_ipa_localize_array (ctxt); +} diff --git a/gcc/ipa-struct-reorg/escapes.def b/gcc/ipa-struct-reorg/escapes.def index 996a09bace265dfd1ee69fc51d1ed49a242afd3e..4ba9cc2d02d7f6f4f4abb22bb6da6ff191c3ace6 100644 --- a/gcc/ipa-struct-reorg/escapes.def +++ b/gcc/ipa-struct-reorg/escapes.def @@ -61,5 +61,6 @@ DEF_ESCAPE (escape_unhandled_rewrite, "Type escapes via a unhandled rewrite stmt DEF_ESCAPE (escape_via_orig_escape, "Type escapes via a original escape type") DEF_ESCAPE (escape_instance_field, "Type escapes via a field of instance") DEF_ESCAPE (escape_via_empty_no_orig, "Type escapes via empty and no original") +DEF_ESCAPE (escape_no_record_var, "Type escapes via no record var") #undef DEF_ESCAPE diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc index 8e2728a472040d937e0616b52b4c89fc4738f76a..9e38fafe74d146159f951304a3a3419a848570f5 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc @@ -75,7 +75,6 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" -#include "tm.h" #include "tree.h" #include "tree-pass.h" #include "cgraph.h" @@ -99,16 +98,17 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfg.h" #include "alloc-pool.h" #include "symbol-summary.h" -#include "ipa-prop.h" +#include "bitmap.h" #include "ipa-struct-reorg.h" #include "tree-eh.h" -#include "bitmap.h" #include "tree-ssa-live.h" /* For remove_unused_locals. */ #include "ipa-param-manipulation.h" #include "gimplify-me.h" #include "cfgloop.h" #include "langhooks.h" #include "cfgexpand.h" +#include "gimplify.h" +#include /* Check whether in C language or LTO with only C language. */ @@ -149,17 +149,31 @@ using namespace struct_relayout; #define VOID_POINTER_P(type) \ (POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (type))) -#define FC_DUMP_MSG(message) \ +#define FC_DUMP_MSG(...) \ do \ { \ if (dump_file && (dump_flags & TDF_DETAILS)) \ - fprintf (dump_file, "[field compress] %s", (message)); \ + { \ + fprintf (dump_file, "[field compress] "); \ + fprintf (dump_file, __VA_ARGS__); \ + } \ } while (0) +#define STRING_STARTS_WITH(s, suffix) \ + (strncmp (s, suffix, sizeof (suffix) - 1) == 0) + /* Flags for operand_equal_p to treat decls with the same name equal. */ #define COMPARE_DECL_FLAGS (OEP_DECL_NAME | OEP_LEXICOGRAPHIC) +#define APPEND_GASSIGN_1(gsi, lhs, op, rhs) \ + gsi_insert_after (&gsi, gimple_build_assign (lhs, op, rhs), \ + GSI_NEW_STMT) + +#define APPEND_GASSIGN_2(gsi, lhs, op, rhs1, rhs2) \ + gsi_insert_after (&gsi, gimple_build_assign (lhs, op, rhs1, rhs2), \ + GSI_NEW_STMT) + static void set_var_attributes (tree var) { @@ -349,6 +363,212 @@ gimple_assign_rhs_code_p (gimple *stmt, enum tree_code code) && gimple_assign_rhs_code (stmt) == code; } +static fc_field * +find_fc_field (const auto_vec &fc_fields, tree field) +{ + for (auto *fc_f : fc_fields) + if (fc_f->field == field) + return fc_f; + + return NULL; +} + +/* Return true if the stmt is a copy/convert to integer. */ + +static bool +is_copy_int (const gimple *stmt) +{ + if (!is_gimple_assign (stmt)) + return NULL_TREE; + + tree rhs = gimple_assign_rhs1 (stmt); + + if (gimple_assign_single_p (stmt)) + if (TREE_CODE (rhs) == SSA_NAME) + return true; + + if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))) + { + tree lhs = gimple_assign_lhs (stmt); + + if (TREE_CODE (rhs) == SSA_NAME + && TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE) + return true; + } + + return false; +} + +/* Strip the copy with typecasting of int or unsigned int. */ + +static gimple * +strip_copy_stmts (gimple *stmt) +{ + while (is_copy_int (stmt)) + stmt = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)); + + return stmt; +} + +const char * +get_func_name (tree decl) +{ + if (!decl || TREE_CODE (decl) != FUNCTION_DECL || !DECL_NAME (decl)) + return NULL; + + tree decl_name = DECL_NAME (decl); + if (TREE_CODE (decl_name) != IDENTIFIER_NODE) + return NULL; + + return IDENTIFIER_POINTER (decl_name); +} + +/* Compare the gimple order of input_ssa for fc fields. */ + +static int +input_order_cmp (const void *p, const void *q) +{ + const fc_field *a = *static_cast (p); + const fc_field *b = *static_cast (q); + + gimple *ga = SSA_NAME_DEF_STMT (a->input_ssa); + gimple *gb = SSA_NAME_DEF_STMT (b->input_ssa); + + if (gimple_uid (ga) < gimple_uid (gb)) + return -1; + else if (gimple_uid (ga) > gimple_uid (gb)) + return 1; + else + return 0; +} + +/* Called by walk_tree to check if ssa_name DATA exists in an expression. */ + +static tree +check_for_ssa (tree *opnd_ptr, int *walk_subtrees ATTRIBUTE_UNUSED, void *data) +{ + tree ssa = (tree) data; + if (*opnd_ptr == ssa) + return ssa; + + return NULL_TREE; +} + +/* Helper to create a function declaration together with arguments and result + declarations. */ + +static tree +create_new_fn_decl (char *fn_name, int n_args, tree *arg_types, + tree return_type) +{ + tree fn_type = build_function_type_array (return_type, n_args, arg_types); + tree fndecl = build_fn_decl (fn_name, fn_type); + tree id = get_identifier (fn_name); + SET_DECL_ASSEMBLER_NAME (fndecl, id); + DECL_NAME (fndecl) = id; + DECL_ARTIFICIAL (fndecl) = 1; + DECL_EXTERNAL (fndecl) = 0; + DECL_CONTEXT (fndecl) = NULL_TREE; + DECL_INITIAL (fndecl) = make_node (BLOCK); + DECL_STATIC_CONSTRUCTOR (fndecl) = 0; + + /* Function result declairation. */ + tree resdecl + = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type); + DECL_RESULT (fndecl) = resdecl; + + /* Function arguments. */ + tree prev_arg = NULL_TREE; + for (int i = 0; i < n_args; i++) + { + tree arg_decl + = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL, arg_types[i]); + DECL_ARTIFICIAL (arg_decl) = 1; + DECL_IGNORED_P (arg_decl) = 1; + TREE_USED (arg_decl) = 1; + DECL_CONTEXT (arg_decl) = fndecl; + DECL_ARG_TYPE (arg_decl) = arg_types[i]; + TREE_READONLY (arg_decl) = 1; + if (prev_arg) + TREE_CHAIN (prev_arg) = arg_decl; + else + DECL_ARGUMENTS (fndecl) = arg_decl; + prev_arg = arg_decl; + } + + return fndecl; +} + +static void +release_srdecl_ssa_name (srdecl *srd) +{ + if (!srd->has_new_decl ()) + return; + + tree ssa_name = NULL_TREE; + if (srd->argumentnum >= 0) + ssa_name = ssa_default_def (cfun, srd->decl); + else if (TREE_CODE (srd->decl) == SSA_NAME) + ssa_name = srd->decl; + + if (ssa_name && num_imm_uses (ssa_name) == 0) + release_ssa_name (ssa_name); +} + +static char * +append_suffix (const char *s1, unsigned suffix) +{ + char s2[32]; + sprintf (s2, "%u", suffix); + return concat (s1, s2, NULL); +} + +static unsigned HOST_WIDE_INT +get_bitsize (tree field) +{ + tree bitsize = DECL_BIT_FIELD (field) ? DECL_SIZE (field) + : TYPE_SIZE (TREE_TYPE (field)); + return tree_to_uhwi (bitsize); +} + +/* Generate SSA_NAME for the given var. */ + +static tree +generate_ssa_name (tree var, gimple_stmt_iterator *gsi) +{ + if (TREE_CODE (var) == SSA_NAME) + return var; + + tree name = make_ssa_name (TREE_TYPE (var)); + gimple *stmt = gimple_build_assign (name, var); + gsi_insert_after (gsi, stmt, GSI_NEW_STMT); + + return name; +} + +/* Get type node by bit precision and sign. */ + +static tree +get_integer_type_node (unsigned precision, bool is_unsigned) +{ + switch (precision) + { + case 64: + return is_unsigned ? long_long_unsigned_type_node + : long_long_integer_type_node; + case 32: + return is_unsigned ? unsigned_type_node : integer_type_node; + case 16: + return is_unsigned ? short_unsigned_type_node + : short_integer_type_node; + case 8: + return is_unsigned ? unsigned_char_type_node + : signed_char_type_node; + default: + return NULL_TREE; + } +} + /* Enum the struct layout optimize level, which should be the same as the option -fstruct-reorg=. */ @@ -364,6 +584,15 @@ enum struct_layout_opt_level SEMI_RELAYOUT = 1 << 6 }; +enum class fc_level +{ + NONE, + STATIC, + DYNAMIC +}; + +fc_level current_fc_level; + srfunction *current_function; vec csrfun_stack; @@ -387,6 +616,31 @@ public: #define SET_CFUN(srfn) csrfun_context csrfn_ctx(srfn); +/* RAII class to change current dump_file and dump_flags, + and restore when the object goes out of scope. */ + +class dump_file_saver +{ +public: + dump_file_saver (FILE *file, dump_flags_t flags) + { + old_dump_file = dump_file; + old_dump_flags = dump_flags; + dump_file = file; + dump_flags = flags; + } + ~dump_file_saver () + { + dump_file = old_dump_file; + dump_flags = old_dump_flags; + } +private: + FILE *old_dump_file; + dump_flags_t old_dump_flags; +}; + +#define SET_DUMP_FILE(file, flags) dump_file_saver fd_saver(file, flags); + /* Defines the target pointer size of compressed pointer, which should be 8, 16, 32. */ @@ -502,7 +756,7 @@ srfield::srfield (tree field, srtype *base) type (NULL), clusternum (0), field_access (EMPTY_FIELD), - static_fc_field (NULL), + fc_f (NULL), field_class (NULL) { for (int i = 0; i < max_split; i++) @@ -531,7 +785,8 @@ srtype::srtype (tree type) { if (TREE_CODE (field) == FIELD_DECL) { - if (DECL_BIT_FIELD (field)) + if (current_fc_level != fc_level::DYNAMIC + && DECL_BIT_FIELD (field)) { escapes = escape_bitfields; continue; @@ -698,6 +953,20 @@ srfunction::record_decl (srtype *type, tree decl, int arg, tree orig_type) return decl1; } +/* A function is either partially cloned or fully cloned (versioning). */ + +bool +srfunction::partial_clone_p () +{ + return fc_path.start_stmt != NULL; +} + +bool +srfunction::entry_function_p () +{ + return strcmp (node->name (), "main") == 0 && !node->callers; +} + /* Find the field at OFF offset. */ srfield * @@ -716,6 +985,17 @@ srtype::find_field (unsigned HOST_WIDE_INT off) return NULL; } +/* Find the field according to field decl. */ +srfield * +srtype::find_field_by_decl (tree fielddecl) +{ + for (auto *field : fields) + if (operand_equal_p (fielddecl, field->fielddecl, COMPARE_DECL_FLAGS)) + return field; + + return NULL; +} + /* Add the function FN to the list of functions if it is there not already. */ @@ -900,8 +1180,9 @@ srfield::reorder_fields (tree newfields[max_split], tree newlast[max_split], else { tree tmp = newfields[clusternum]; - if (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (field))) - > tree_to_uhwi (TYPE_SIZE (TREE_TYPE (tmp)))) + auto field_bitsize = get_bitsize (field); + auto tmp_bitsize = get_bitsize (tmp); + if (field_bitsize > tmp_bitsize) { DECL_CHAIN (field) = tmp; newfields[clusternum] = field; @@ -909,9 +1190,7 @@ srfield::reorder_fields (tree newfields[max_split], tree newlast[max_split], else { while (DECL_CHAIN (tmp) - && (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (field))) - <= tree_to_uhwi ( - TYPE_SIZE (TREE_TYPE (DECL_CHAIN (tmp)))))) + && field_bitsize <= get_bitsize (DECL_CHAIN (tmp))) tmp = DECL_CHAIN (tmp); /* Now tmp size > field size @@ -932,11 +1211,18 @@ srfield::create_new_reorder_fields (tree newtype[max_split], tree newfields[max_split], tree newlast[max_split]) { + /* For dynamic shadow. */ + if (current_fc_level == fc_level::DYNAMIC && fc_f && fc_f->original) + { + newfield[0] = NULL_TREE; + return; + } + /* newtype, corresponding to newtype[max_split] in srtype. */ tree nt = NULL_TREE; if (type == NULL) /* Common var. */ - nt = static_fc_field ? static_fc_field->new_type : fieldtype; + nt = fc_f ? fc_f->new_type : fieldtype; else /* RECORD_TYPE var. */ nt = type->has_escaped () ? type->type : type->newtype[0]; @@ -986,24 +1272,28 @@ srfield::create_new_reorder_fields (tree newtype[max_split], TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (fielddecl); DECL_CONTEXT (field) = newtype[clusternum]; - if (flag_ipa_struct_sfc && base->fc_info && base->fc_info->static_fc_p) + fc_type_info *info = base->fc_info; + if (info && (info->static_fc_p || info->dynamic_fc_p)) { DECL_PACKED (field) = 1; + DECL_BIT_FIELD (field) = DECL_BIT_FIELD (fielddecl); + if (DECL_BIT_FIELD (field)) + DECL_SIZE (field) = DECL_SIZE (fielddecl); - if (static_fc_field) + if (fc_f) { /* Always not align compressed fields. */ SET_DECL_ALIGN (field, 0); - if (static_fc_field->bits) + if (fc_f->bits) { DECL_BIT_FIELD (field) = 1; - DECL_SIZE (field) = bitsize_int (static_fc_field->bits); + DECL_SIZE (field) = bitsize_int (fc_f->bits); DECL_NONADDRESSABLE_P (field) = 1; /* Build unsigned bitfield integer type. */ - nt = build_nonstandard_integer_type (static_fc_field->bits, 1); + nt = build_nonstandard_integer_type (fc_f->bits, 1); TREE_TYPE (field) = nt; - static_fc_field->new_type = nt; + fc_f->new_type = nt; } } } @@ -1022,6 +1312,19 @@ srfield::dead_field_p () && !FUNCTION_POINTER_TYPE_P (fieldtype); } +bool +srfield::dfc_type_change_p () +{ + return fc_f && fc_f->cond + && fc_f->cond->old_type != TREE_TYPE (newfield[0]); +} + +fc_closure * +srfield::get_closure () +{ + return &(field_class->closure); +} + /* Given a struct s whose fields has already reordered by size, we try to combine fields less than 8 bytes together to 8 bytes. Example: struct s { @@ -1095,22 +1398,38 @@ srtype::has_recursive_field_type () void srtype::check_fc_fields () { - if (!fc_info || !fc_info->static_fc_p) + if (!fc_info || (!fc_info->static_fc_p && !fc_info->dynamic_fc_p)) return; - for (unsigned i = 0; i < fields.length (); i++) + for (auto *srf : fields) { - fc_field *fc_f; - unsigned j; - FOR_EACH_VEC_ELT (fc_info->static_fc_fields, j, fc_f) - if (fields[i]->fielddecl == fc_f->field) + tree field = srf->fielddecl; + if (fc_info->static_fc_p) + srf->fc_f = find_fc_field (fc_info->static_fc_fields, field); + else { - fields[i]->static_fc_field = fc_f; - break; + srf->fc_f = find_fc_field (fc_info->dynamic_shadow_fields, field); + if (!srf->fc_f) + srf->fc_f = find_fc_field (fc_info->dynamic_fc_fields, field); } } } +bool +srtype::reorg_name_p () +{ + const char *name = get_type_name (type); + return name && strstr (name, ".reorg"); +} + +bool +srtype::has_escaped () +{ + return escapes != does_not_escape + && (current_fc_level != fc_level::DYNAMIC + || !reorg_name_p ()); +} + /* Create the new TYPE corresponding to THIS type. */ bool @@ -1124,7 +1443,7 @@ srtype::create_new_type (void) visited = true; - if (escapes != does_not_escape) + if (has_escaped ()) { newtype[0] = type; return false; @@ -1176,10 +1495,10 @@ srtype::create_new_type (void) if (tname) { name = concat (tname, ".reorg.", id, NULL); - TYPE_NAME (newtype[i]) = build_decl (UNKNOWN_LOCATION, - TYPE_DECL, - get_identifier (name), - newtype[i]); + tree name_id = get_identifier (name); + TYPE_STUB_DECL (newtype[i]) + = build_decl (UNKNOWN_LOCATION, TYPE_DECL, name_id, newtype[i]); + TYPE_NAME (newtype[i]) = name_id; free (name); } } @@ -1206,8 +1525,12 @@ srtype::create_new_type (void) { TYPE_FIELDS (newtype[i]) = newfields[i]; layout_type (newtype[i]); - if (TYPE_NAME (newtype[i]) != NULL) - layout_decl (TYPE_NAME (newtype[i]), 0); + } + + if (current_fc_level == fc_level::DYNAMIC) + { + gcc_assert (maxclusters == 1); + fc_info->variant->new_type = newtype[0]; } warn_padded = save_warn_padded; @@ -1277,7 +1600,7 @@ srfunction::create_new_decls (void) return; if (node) - set_cfun (DECL_STRUCT_FUNCTION (node->decl)); + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); for (unsigned i = 0; i < decls.length (); i++) { @@ -1388,7 +1711,8 @@ srfunction::create_new_decls (void) } } - set_cfun (NULL); + if (node) + pop_cfun (); } /* Dump out the field structure to FILE. */ @@ -1459,34 +1783,34 @@ sraccess::dump (FILE *f) const fprintf (f, "}\n"); } -/* Check if it's an assignment to the given type. */ +/* Check if it's an assignment to the given field(fielddecl != NULL_TREE) + or any field(fielddecl == NULL_TREE). */ bool -sraccess::write_type_p (tree type) const +sraccess::write_field_p (tree fielddecl) const { - return this->type && this->type->type == type - && is_gimple_assign (stmt) - && index == 0; + return write_p () && field && (!fielddecl || field->fielddecl == fielddecl); } -/* Check if it's an assignment to the given field. */ +/* Check if it's an assignment that read the given + field(fielddecl != NULL_TREE) or any field(fielddecl == NULL_TREE). */ bool -sraccess::write_field_p (tree fielddecl) const +sraccess::read_field_p (tree fielddecl) const { - return field && field->fielddecl == fielddecl - && is_gimple_assign (stmt) - && index == 0; + return read_p () && field && (!fielddecl || field->fielddecl == fielddecl); } -/* Check if it's an assignment that read the given field. */ +bool +sraccess::write_p () const +{ + return is_gimple_assign (stmt) && index == 0; +} bool -sraccess::read_field_p (tree fielddecl) const +sraccess::read_p () const { - return field && field->fielddecl == fielddecl - && is_gimple_assign (stmt) - && index > 0; + return is_gimple_assign (stmt) && index > 0; } /* Dump out the decl structure to FILE. */ @@ -1504,6 +1828,133 @@ srdecl::dump (FILE *file) type->simple_dump (file); } +void +fc_closure::add_read_change (gimple *stmt) +{ + if (!read_change_set.contains (stmt)) + read_change_set.add (stmt); +} + +bool +fc_closure::read_change_p (gimple *stmt) +{ + return read_change_set.contains (stmt); +} + +void +fc_closure::add_read_unchange (gimple *stmt) +{ + if (!read_unchange_set.contains (stmt)) + read_unchange_set.add (stmt); +} + +bool +fc_closure::read_unchange_p (gimple *stmt) +{ + return read_unchange_set.contains (stmt); +} + +void +fc_closure::add_write_change (gimple *stmt) +{ + if (!write_change_set.contains (stmt)) + write_change_set.add (stmt); +} + +bool +fc_closure::write_change_p (gimple *stmt) +{ + return write_change_set.contains (stmt); +} + +void +fc_closure::add_write_unchange (gimple *stmt) +{ + if (!write_unchange_set.contains (stmt)) + write_unchange_set.add (stmt); +} + +bool +fc_closure::write_unchange_p (gimple *stmt) +{ + return write_unchange_set.contains (stmt); +} + +bool +fc_closure::change_p (gimple *stmt) +{ + return write_change_p (stmt) || read_change_p (stmt); +} + +bool +fc_closure::unchange_p (gimple *stmt) +{ + return write_unchange_p (stmt) || read_unchange_p (stmt); +} + +/* Call compress/decompress function for rhs. */ + +tree +fc_closure::convert_rhs (tree rhs, tree fn) +{ + tree newrhs = build_call_expr (fn, 1, rhs); + cgraph_node *callee = cgraph_node::get (fn); + cgraph_node *node = cgraph_node::get (current_function_decl); + node->create_edge (callee, NULL, profile_count::zero ()); + + return newrhs; +} + +void +closure_helper::record_origin_closure (basic_block bb) +{ + for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + if (!is_gimple_assign (stmt)) + continue; + + uid++; + + if (cinfo->read_change_p (stmt)) + bitmap_set_bit (read_change_map, uid); + else if (cinfo->write_change_p (stmt)) + bitmap_set_bit (write_change_map, uid); + else if (cinfo->read_unchange_p (stmt)) + bitmap_set_bit (read_unchange_map, uid); + else if (cinfo->write_unchange_p (stmt)) + bitmap_set_bit (write_unchange_map, uid); + } +} + +void +closure_helper::add_cloned_closure (basic_block bb) +{ + for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + if (!is_gimple_assign (stmt)) + continue; + + uid++; + + if (bitmap_bit_p (read_change_map, uid)) + cinfo->add_read_change (stmt); + else if (bitmap_bit_p (write_change_map, uid)) + cinfo->add_write_change (stmt); + else if (bitmap_bit_p (read_unchange_map, uid)) + cinfo->add_read_unchange (stmt); + else if (bitmap_bit_p (write_unchange_map, uid)) + cinfo->add_write_unchange (stmt); + } +} + +void +closure_helper::reset_uid () +{ + uid = 0; +} + void fc_field_class::dump (FILE *file) const { @@ -1545,6 +1996,37 @@ fc_field_class::get_field_index (srfield *field) const return -1; } +void +fc_ref::dump (FILE *file) const +{ + fprintf (file, "var: "); + print_generic_expr (dump_file, var); + fprintf (dump_file, ", type: "); + print_generic_expr (dump_file, orig_type ? orig_type : TREE_TYPE (var)); + fprintf (dump_file, ", array: "); + print_generic_expr (dump_file, source->var); + if (size) + { + fprintf (dump_file, ", array size: "); + print_generic_expr (dump_file, size); + } + if (field) + { + fprintf (dump_file, ", field: "); + print_generic_expr (dump_file, field); + } + fprintf (dump_file, "\n"); +} + +fc_type_info::~fc_type_info () +{ + if (variant) + { + delete variant; + variant = NULL; + } +} + fc_field_class * fc_type_info::find_field_class_by_type (tree type) const { @@ -1577,12 +2059,131 @@ fc_type_info::record_field_class (srfield *srf) return field_class; } -} // namespace struct_reorg +fc_cond * +fc_type_info::find_cond (tree type) const +{ + for (auto *cond : fc_conds) + { + if (cond->old_type == type) + return cond; + } + return NULL; +} -namespace struct_relayout { +fc_cond * +fc_type_info::create_cond (tree type) +{ + fc_cond *cond = find_cond (type); + if (cond) + return cond; -/* Complete Structure Relayout Optimization. + /* New cond will be stored in an auto_delete_vec(fc_conds). */ + cond = new fc_cond (type); + fc_conds.safe_push (cond); + + /* Record the fc_cond to corresponding fc_field_class. */ + fc_field_class *field_class = find_field_class_by_type (type); + gcc_assert (field_class); + field_class->cond = cond; + cond->field_class = field_class; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Create new fc_cond, type: "); + print_generic_expr (dump_file, type); + fprintf (dump_file, "\n"); + } + + return cond; +} + +void +fc_type_info::record_cond (fc_field *fc_f) +{ + if (fc_f->cond) + return; + + fc_cond *cond = create_cond (TREE_TYPE (fc_f->field)); + fc_f->cond = cond; + cond->fields.safe_push (fc_f); +} + +fc_path_info::~fc_path_info () +{ + if (cloned_func) + delete cloned_func; +} + +/* Search and store basic_blocks that: + 1) can reach STMT (when DIRECTION == PRED); + 2) can be reached from STMT (when DIRECTION == SUCC). + Return false if field compression cannot be performed. */ + +bool +fc_path_info::collect_blocks (gimple *stmt, direction dir) +{ + basic_block start_bb = gimple_bb (stmt); + if (!start_bb) + return false; + + /* The start block should not be in a loop. */ + if (start_bb->loop_father != NULL + && loop_outer (start_bb->loop_father) != NULL) + return false; + + bool prev = dir == direction::PRED; + basic_block stop_bb = prev ? ENTRY_BLOCK_PTR_FOR_FN (cfun) + : EXIT_BLOCK_PTR_FOR_FN (cfun); + auto *store_list = prev ? &pre_bbs : &reach_bbs; + + auto_bitmap visited; + auto_vec worklist; + worklist.safe_push (start_bb); + bool exit_p = false; + + while (!worklist.is_empty ()) + { + basic_block bb = worklist.pop (); + if (!bitmap_set_bit (visited, bb->index)) + continue; + + if (bb != stop_bb) + store_list->safe_push (bb); + else + exit_p = true; + + if (prev) + for (auto *e : bb->preds) + worklist.safe_push (e->src); + else + for (auto *e : bb->succs) + worklist.safe_push (e->dest); + } + + if (!exit_p) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Found %d blocks in func ", store_list->length ()); + print_generic_expr (dump_file, cfun->decl); + fprintf (dump_file, " %s:\n", + prev ? "before start-point" : "to be cloned"); + for (auto *bb : store_list) + fprintf (dump_file, "%d ", bb->index); + fprintf (dump_file, "\n\n"); + } + + return true; +} + +} // namespace struct_reorg + + +namespace struct_relayout { + +/* Complete Structure Relayout Optimization. It reorganizes all structure members, and puts same member together. struct s { long a; @@ -1680,7 +2281,11 @@ struct const_map struct ipa_struct_reorg { private: + /* The current srfield set in rewrite_expr. For dfc. */ + srfield *cur_srfd; + auto_vec_del global_consts; + hash_set visited_vars; public: // Constructors @@ -1712,10 +2317,13 @@ public: void detect_cycles (void); bool walk_field_for_cycles (srtype *); void prune_escaped_types (void); + void prune_function (srfunction *); + void prune_globals (); void propagate_escape (void); void propagate_escape_via_original (void); void propagate_escape_via_empty_with_no_original (void); void propagate_escape_via_ext_func_types (void); + void propagate_escape_via_no_record_var (void); void analyze_types (void); void clear_visited (void); bool create_new_types (void); @@ -1724,11 +2332,12 @@ public: void create_new_functions (void); void create_new_args (cgraph_node *new_node); unsigned rewrite_functions (void); + void rewrite_block (basic_block); srdecl *record_var (tree decl, escape_type escapes = does_not_escape, int arg = -1); void record_safe_func_with_void_ptr_parm (void); - srfunction *record_function (cgraph_node *node); + srfunction *record_function (cgraph_node *node, srfunction *sfn = NULL); srfunction *find_function (cgraph_node *node); void record_field_type (tree field, srtype *base_srtype); void record_struct_field_types (tree base_type, srtype *base_srtype); @@ -1855,8 +2464,8 @@ public: const auto_vec &); srfield *read_field_in_fc_class_p (gimple *, fc_field_class *); srfield *write_field_in_fc_class_p (gimple *, fc_field_class *); - fc_field *fc_fields_contains (auto_vec &, tree); bool fc_pair_stmts_rhs_equal_p (const auto_vec &); + bool unique_init_const_p (const fc_shadow_info &); bool fc_operand_equal_p (tree, tree); bool fc_global_const_p (tree, HOST_WIDE_INT &); bool fc_peephole_const_p (tree, HOST_WIDE_INT &); @@ -1872,10 +2481,92 @@ public: bool find_hot_access (fc_type_info *, auto_vec &); void cleanup_shadow_write (fc_type_info *); void rewrite_shadow_read (fc_type_info *); - void insert_shadow_stmt (gimple *, unsigned, fc_field *, tree); + void modify_shadow_read (gimple *, unsigned, fc_field *, tree); bool compress_fields_static (fc_type_info *info); - void compress_to_bitfields (fc_type_info *info); + bool compress_to_bitfield_static (fc_type_info *info); + bool compress_to_bitfield_dynamic (fc_type_info *info); auto_vec collect_all_predecessor (gimple *); + bool types_fc_equal_p (tree, tree); + bool types_fc_compatible_p (tree, tree); + bool find_dynamic_fc_fields (fc_type_info *); + bool find_fields_in_input_stmt (fc_type_info *); + bool find_input_stmt (gimple *, gimple *&, gimple *&); + tree find_file_handler (gimple *); + bool find_fopen_fclose (fc_type_info *); + bool check_dynamic_shadow_fields (fc_type_info *); + bool find_fc_paths (fc_type_info *); + bool find_fc_data (fc_type_info *); + bool find_fc_arrays (fc_type_info *); + bool find_fc_array (fc_type_info *, tree, varpool_node *); + bool duplicative_array_p (fc_type_info *, tree); + bool is_stmt_before_fclose (fc_type_info *, gimple *, symtab_node *); + bool reorg_ptr_p (tree); + bool get_allocate_size_iterate (tree, gimple *, tree &, tree * = NULL); + bool get_allocate_size_assign (tree, gassign *, tree &, tree *); + bool get_allocate_size_call (tree, gcall *, tree &, tree *); + bool get_allocate_size_reorg_ptr (gimple *, tree &); + tree get_allocate_size (tree, tree, tree, gimple *); + bool find_fc_refs (fc_type_info *); + bool find_fc_refs_iterate (fc_type_info *, fc_array *, tree, bool); + bool find_fc_refs_ssa_name (fc_type_info *, fc_array *, tree, bool); + bool find_fc_refs_mem_ref (fc_type_info *, fc_array *, tree); + bool find_fc_refs_component_ref (fc_type_info *, fc_array *, tree); + bool fc_type_pointer_p (fc_type_info *, tree); + bool add_fc_ref (fc_type_info *, fc_array *, tree, tree); + check_ref_result check_duplicative_ref (fc_type_info *, fc_array *, tree, + tree, tree &, tree &); + gimple *find_def_stmt_before_fclose (fc_type_info *, tree); + tree get_ptr_decl (tree); + bool check_fc_array_uses (fc_type_info *); + void calc_fc_ref_count (fc_type_info *); + bool compress_fields_dynamic (fc_type_info *); + bool calc_dynamic_boundary (fc_type_info *); + bool fc_cond_field_p (tree, const fc_cond *); + bool fc_input_ssa_p (tree, const fc_cond *); + bool fc_field_load_p (tree, const fc_cond *); + void update_high_bound (fc_cond *, HOST_WIDE_INT); + bool check_closure (fc_type_info *); + bool check_closure (fc_type_info *, fc_cond *); + bool write_field_class_only_p (fc_type_info *, fc_field_class *, tree); + void collect_closure_read_change (fc_type_info *, fc_field_class *); + unsigned execute_dynamic_field_compression (); + unsigned dynamic_fc_rewrite (); + bool create_dynamic_fc_newtypes (); + void create_dynamic_fc_variant (fc_type_info *); + void create_global_var_dfc_path (fc_type_info *); + void create_dynamic_fc_convert_fn (fc_type_info *); + tree create_convert_fn (fc_cond *, unsigned, bool); + edge create_normal_part (fc_cond *); + void create_conversion_part (fc_cond *, edge, bool); + void clone_dynamic_fc_path (fc_type_info *); + void clone_partial_func (fc_type_info *, srfunction *); + void clone_whole_func (srfunction *); + void rewrite_dynamic_shadow_fields (fc_type_info *); + void rewrite_dynamic_fc_path (); + void record_dfc_path_info (fc_type_info *); + void collect_closure_info_dynamic (fc_type_info *); + void collect_closure_info_partial (srfunction *, fc_closure *); + void collect_closure_info_whole (srfunction *, fc_closure *); + void rewrite_partial_func (srfunction *); + void rewrite_whole_func (srfunction *); + void clean_func_after_rewrite (srfunction *); + void dynamic_fc_rewrite_assign (gimple *, tree, tree &, tree &); + void add_dynamic_checking (fc_type_info *); + void insert_code_calc_dfc_path (fc_type_info *); + void insert_code_calc_max_min_val (fc_type_info *); + tree insert_code_calc_cond (fc_type_info *, gimple_stmt_iterator *); + void insert_code_check_init_const (fc_type_info *, gimple_stmt_iterator *, + tree &); + void insert_code_compress_data (fc_type_info *, edge); + void insert_code_compress_variant (fc_type_info *, basic_block, + const auto_vec &, + const auto_vec &); + void insert_code_compress_array (fc_type_info *, edge &, + const auto_vec &, + const auto_vec &); + void insert_code_modify_refs (fc_type_info *, edge); + void create_compress_object_fn (fc_type_info *); + edge insert_code_modify_single_ref (edge, tree, fc_array *, tree, tree); }; struct ipa_struct_relayout @@ -2247,6 +2938,8 @@ ipa_struct_relayout::rewrite_address (tree xhs, gimple_stmt_iterator *gsi) /* Emit gimple _X4 = gptr[I]. */ tree gptr_field_ssa = create_ssa (gptr[field_num], gsi); tree new_address = make_ssa_name (TREE_TYPE (gptr[field_num])); + tree new_address_type = TREE_TYPE (new_address); + tree new_type = TREE_TYPE (new_address_type); gassign *new_stmt = gimple_build_assign (new_address, POINTER_PLUS_EXPR, gptr_field_ssa, step3); gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); @@ -2256,9 +2949,15 @@ ipa_struct_relayout::rewrite_address (tree xhs, gimple_stmt_iterator *gsi) should be transformed to MEM[gptr + sizeof (member)] = 0B */ - HOST_WIDE_INT size - = tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_address)))); - tree new_size = rewrite_offset (pointer_offset, size); + tree new_size = NULL_TREE; + if (integer_zerop (pointer_offset)) + new_size = build_int_cst (TREE_TYPE (new_address), 0); + else + { + HOST_WIDE_INT size = tree_to_shwi (TYPE_SIZE_UNIT (new_type)); + new_size = rewrite_offset (pointer_offset, size); + } + if (new_size) TREE_OPERAND (mem_ref, 1) = new_size; @@ -2530,7 +3229,7 @@ ipa_struct_reorg::dump_newtypes (FILE *f) srtype *type = NULL; FOR_EACH_VEC_ELT (types, i, type) { - if (type->has_escaped ()) + if (!type->has_new_type ()) continue; fprintf (f, "======= the %dth newtype: ======\n", i); fprintf (f, "type : "); @@ -3133,7 +3832,7 @@ check_each_call (cgraph_node *node, cgraph_edge *caller) return false; } - if (!check_node_def (ptr_layers)) + if (current_fc_level != fc_level::DYNAMIC && !check_node_def (ptr_layers)) return false; return true; } @@ -3657,6 +4356,14 @@ ipa_struct_reorg::find_vars (gimple *stmt) find_var (gimple_assign_rhs1 (stmt), stmt); find_var (gimple_assign_rhs2 (stmt), stmt); } + else if (gimple_assign_rhs_code (stmt) == EQ_EXPR + && types_compatible_p ( + TYPE_MAIN_VARIANT (TREE_TYPE (gimple_assign_rhs1 (stmt))), + TYPE_MAIN_VARIANT (TREE_TYPE (gimple_assign_rhs2 (stmt))))) + { + find_var (gimple_assign_rhs1 (stmt), stmt); + find_var (gimple_assign_rhs2 (stmt), stmt); + } else { /* Because we won't handle these stmts in rewrite phase, @@ -4065,61 +4772,10 @@ ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) tree ipa_struct_reorg::allocate_size (srtype *type, srdecl *decl, gimple *stmt) { - if (!stmt - || gimple_code (stmt) != GIMPLE_CALL - || !handled_allocation_stmt (stmt)) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "\nNot a allocate statment:\n"); - print_gimple_stmt (dump_file, stmt, 0); - fprintf (dump_file, "\n"); - } - return NULL; - } - if (type->has_escaped ()) return NULL; - tree struct_size = TYPE_SIZE_UNIT (type->type); - - /* Specify the correct size to relax multi-layer pointer. */ - if (TREE_CODE (decl->decl) == SSA_NAME && isptrptr (decl->orig_type)) - struct_size = TYPE_SIZE_UNIT (decl->orig_type); - - tree size = gimple_call_arg (stmt, 0); - - if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) - || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC)) - size = gimple_call_arg (stmt, 1); - else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) - { - tree arg1; - arg1 = gimple_call_arg (stmt, 1); - /* Check that second argument is a constant equal to - the size of structure. */ - if (operand_equal_p (arg1, struct_size, 0)) - return size; - /* ??? Check that first argument is a constant - equal to the size of structure. */ - /* If the allocated number is equal to the value of struct_size, - the value of arg1 is changed to the allocated number. */ - if (operand_equal_p (size, struct_size, 0)) - return arg1; - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "\ncalloc the correct size:\n"); - print_gimple_stmt (dump_file, stmt, 0); - fprintf (dump_file, "\n"); - } - return NULL; - } - - tree num; - if (!is_result_of_mult (size, &num, struct_size)) - return NULL; - - return num; + return get_allocate_size (type->type, decl->decl, decl->orig_type, stmt); } void @@ -4358,35 +5014,21 @@ ipa_struct_reorg::wholeaccess (tree expr, tree base, if (TREE_CODE (expr) == ADDR_EXPR && TREE_OPERAND (expr, 0) == base) return true; - if (!accesstype) - return false; - - if (!types_compatible_p (TREE_TYPE (expr), TREE_TYPE (accesstype))) - return false; - - if (!handled_type (TREE_TYPE (expr))) + if (!accesstype || !handled_type (TREE_TYPE (expr)) || !t || !t->type) return false; - if (!t || !t->type) - return false; - - tree type = TYPE_MAIN_VARIANT (t->type); + /* T *_1; _2 = MEM[(T *)_1]. */ if (TREE_CODE (expr) == MEM_REF - && POINTER_TYPE_P (TREE_TYPE (expr)) - && POINTER_TYPE_P (accesstype) - && POINTER_TYPE_P (TREE_TYPE (accesstype)) + && integer_zerop (TREE_OPERAND (expr, 1)) && POINTER_TYPE_P (TREE_TYPE (base)) - && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (base))) == type - && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (expr))) == type - && TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (accesstype))) == type) - return false; - - srtype *other_type = find_type (inner_type (TREE_TYPE (expr))); + && types_compatible_p (TREE_TYPE (TREE_TYPE (base)), t->type)) + return POINTER_TYPE_P (accesstype) + && types_compatible_p (TREE_TYPE (accesstype), t->type); - if (t == other_type) - return true; + if (!types_compatible_p (TREE_TYPE (expr), TREE_TYPE (accesstype))) + return false; - return false; + return t == find_type (inner_type (TREE_TYPE (expr))); } bool @@ -4422,18 +5064,6 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, base = TREE_OPERAND (base, 0); } - if (offset != 0 && accesstype) - { - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, "Non zero offset (%d) with MEM.\n", (int)offset); - print_generic_expr (dump_file, expr); - fprintf (dump_file, "\n"); - print_generic_expr (dump_file, base); - fprintf (dump_file, "\n"); - } - } - srdecl *d = find_decl (base); srtype *t; @@ -4545,7 +5175,14 @@ ipa_struct_reorg::get_type_field (tree expr, tree &base, bool &indirect, return true; } - srfield *f = t->find_field (offset); + srfield *f = NULL; + if (TREE_CODE (expr) == COMPONENT_REF + && DECL_BIT_FIELD (TREE_OPERAND (expr, 1))) + /* Static field compression may create bitfield. In this case, + byte position is not reliable. */ + f = t->find_field_by_decl (TREE_OPERAND (expr, 1)); + else + f = t->find_field (offset); if (!f) { if (dump_file && (dump_flags & TDF_DETAILS)) @@ -4706,6 +5343,17 @@ ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt) argtype = argtype ? TREE_CHAIN (argtype) : NULL_TREE; } + + /* Types escapes via a argument at empty or inlined function. */ + cgraph_node *callee = node->get_edge (stmt)->callee; + if (!gimple_call_builtin_p (stmt, BUILT_IN_FREE) + && gimple_call_num_args (stmt) + && callee && (!callee->has_gimple_body_p () || callee->inlined_to)) + { + for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) + mark_type_as_escape (TREE_TYPE (gimple_call_arg (stmt, i)), + escape_var_arg_function); + } } void @@ -4717,8 +5365,8 @@ ipa_struct_reorg::record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt) srfield *field; bool realpart, imagpart, address; bool escape_from_base = false; - if (!get_type_field (expr, base, indirect, type, field, - realpart, imagpart, address, escape_from_base)) + if (!get_type_field (expr, base, indirect, type, field, realpart, + imagpart, address, escape_from_base, false, true)) return; if (current_layout_opt_level > NONE) @@ -4727,7 +5375,6 @@ ipa_struct_reorg::record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt) type->mark_escape (escape_non_optimize, stmt); } - /* Record it. */ type->add_access (new sraccess (expr, stmt, node, find_function (node), type, base, field)); @@ -4739,8 +5386,15 @@ srfunction * ipa_struct_reorg::find_function (cgraph_node *node) { for (unsigned i = 0; i < functions.length (); i++) - if (functions[i]->node == node) - return functions[i]; + { + if (functions[i]->node == node) + return functions[i]; + + srfunction *cloned_func = functions[i]->fc_path.cloned_func; + if (current_fc_level == fc_level::DYNAMIC + && cloned_func && cloned_func->node == node) + return cloned_func; + } return NULL; } @@ -4797,6 +5451,13 @@ ipa_struct_reorg::check_type_and_push (tree newdecl, srdecl *decl, } /* At this point there should only be unkown void* ssa names. */ gcc_assert (TREE_CODE (newdecl) == SSA_NAME); + tree inner = SSA_NAME_VAR (newdecl); + if (current_layout_opt_level >= STRUCT_REORDER_FIELDS && + inner && find_decl (inner) == NULL) + { + type->mark_escape (escape_no_record_var, stmt); + return; + } if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nrecording unkown decl: "); @@ -4905,7 +5566,7 @@ ipa_struct_reorg::check_definition_assign (srdecl *decl, : TYPE_SIZE_UNIT (type->type))) type->mark_escape (escape_non_multiply_size, stmt); - if (TREE_CODE (rhs) == SSA_NAME) + if (TREE_CODE (rhs) == SSA_NAME || TREE_CODE (rhs) == ADDR_EXPR) check_type_and_push (rhs, decl, worklist, stmt); return; } @@ -5364,12 +6025,11 @@ ipa_struct_reorg::check_uses (srdecl *decl, vec &worklist) /* Record function corresponding to NODE. */ srfunction * -ipa_struct_reorg::record_function (cgraph_node *node) +ipa_struct_reorg::record_function (cgraph_node *node, srfunction *sfn) { function *fn; tree parm, var; unsigned int i; - srfunction *sfn = NULL; escape_type escapes = does_not_escape; if (dump_file && (dump_flags & TDF_DETAILS)) @@ -5389,8 +6049,11 @@ ipa_struct_reorg::record_function (cgraph_node *node) if (!fn) return sfn; - sfn = new srfunction (node); - functions.safe_push (sfn); + if (!sfn) + { + sfn = new srfunction (node); + functions.safe_push (sfn); + } current_function = sfn; @@ -5843,6 +6506,41 @@ ipa_struct_reorg::propagate_escape_via_ext_func_types (void) } } +/* Escape propagation is performed on ssa_name decl that no record var in + decls. */ + +void +ipa_struct_reorg::propagate_escape_via_no_record_var (void) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n propagate_escape_via_no_record_var: \n\n"); + + for (unsigned i = 0; i < functions.length (); i++) + { + if (functions[i]->node) + set_cfun (DECL_STRUCT_FUNCTION (functions[i]->node->decl)); + + for (unsigned j = 0; j < functions[i]->decls.length (); j++) + { + srdecl *decl = functions[i]->decls[j]; + srtype *type = decl->type; + + if (TREE_CODE (decl->decl) == SSA_NAME) + { + tree inner = SSA_NAME_VAR (decl->decl); + + if (inner && functions[i]->find_decl (inner) == NULL) + type->mark_escape (escape_no_record_var, NULL); + } + } + + set_cfun (NULL); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n end propagate_escape_via_no_record_var \n\n"); +} + /* Prune the escaped types and their decls from what was recorded. */ void @@ -5855,12 +6553,14 @@ ipa_struct_reorg::prune_escaped_types (void) /* If contains or is contained by the escape type, mark them as escaping. */ propagate_escape (); + propagate_escape_via_no_record_var (); } if (current_layout_opt_level >= STRUCT_REORDER_FIELDS) { propagate_escape_via_original (); propagate_escape_via_empty_with_no_original (); propagate_escape_via_ext_func_types (); + propagate_escape_via_no_record_var (); } if (dump_file && (dump_flags & TDF_DETAILS)) @@ -5880,38 +6580,7 @@ ipa_struct_reorg::prune_escaped_types (void) for (unsigned i = 0; i < functions.length ();) { srfunction *function = functions[i]; - - /* Prune function arguments of types that escape. */ - for (unsigned j = 0; j < function->args.length ();) - { - if (function->args[j]->type->has_escaped ()) - function->args.ordered_remove (j); - else - j++; - } - - /* Prune global variables that the function uses of types - that escape. */ - for (unsigned j = 0; j < function->globals.length ();) - { - if (function->globals[j]->type->has_escaped ()) - function->globals.ordered_remove (j); - else - j++; - } - - /* Prune variables that the function uses of types that escape. */ - for (unsigned j = 0; j < function->decls.length ();) - { - srdecl *decl = function->decls[j]; - if (decl->type->has_escaped ()) - { - function->decls.ordered_remove (j); - delete decl; - } - else - j++; - } + prune_function (function); /* Prune functions which don't refer to any variables any more. */ if (function->args.is_empty () @@ -5926,19 +6595,7 @@ ipa_struct_reorg::prune_escaped_types (void) i++; } - /* Prune globals of types that escape, all references to those decls - will have been removed in the first loop. */ - for (unsigned j = 0; j < globals.decls.length ();) - { - srdecl *decl = globals.decls[j]; - if (decl->type->has_escaped ()) - { - globals.decls.ordered_remove (j); - delete decl; - } - else - j++; - } + prune_globals (); /* Prune types that escape, all references to those types will have been removed in the above loops. */ @@ -5971,6 +6628,62 @@ ipa_struct_reorg::prune_escaped_types (void) } } +/* Prune the decls in function SRFN. */ + +void +ipa_struct_reorg::prune_function (srfunction *srfn) +{ + /* Prune function arguments of types that escape. */ + for (unsigned i = 0; i < srfn->args.length ();) + { + if (srfn->args[i]->type->has_escaped ()) + srfn->args.ordered_remove (i); + else + i++; + } + + /* Prune global variables that the function uses of types that escape. */ + for (unsigned i = 0; i < srfn->globals.length ();) + { + if (srfn->globals[i]->type->has_escaped ()) + srfn->globals.ordered_remove (i); + else + i++; + } + + /* Prune variables that the function uses of types that escape. */ + for (unsigned i = 0; i < srfn->decls.length ();) + { + srdecl *decl = srfn->decls[i]; + if (decl->type->has_escaped ()) + { + srfn->decls.ordered_remove (i); + delete decl; + } + else + i++; + } +} + +/* Prune globals of types that escape, all references to those decls + will have been removed in the first loop. */ + +void +ipa_struct_reorg::prune_globals () +{ + for (unsigned i = 0; i < globals.decls.length ();) + { + srdecl *decl = globals.decls[i]; + if (decl->type->has_escaped ()) + { + globals.decls.ordered_remove (i); + delete decl; + } + else + i++; + } +} + /* Analyze all of the types. */ void @@ -6187,7 +6900,6 @@ ipa_struct_reorg::create_new_functions (void) bool anyargchanges = false; cgraph_node *new_node; cgraph_node *node = f->node; - int newargs = 0; if (f->old) continue; @@ -6199,10 +6911,7 @@ ipa_struct_reorg::create_new_functions (void) srdecl *d = f->args[j]; srtype *t = d->type; if (t->has_new_type ()) - { - newargs += t->newtype[1] != NULL; - anyargchanges = true; - } + anyargchanges = true; } if (!anyargchanges) continue; @@ -6334,6 +7043,7 @@ ipa_struct_reorg::rewrite_expr (tree expr, } return true; } + cur_srfd = f; tree newdecl = newbase[f->clusternum]; for (unsigned i = 0; i < max_split && f->newfield[i]; i++) @@ -6904,8 +7614,6 @@ ipa_struct_reorg::decompress_candidate_without_check (gimple_stmt_iterator *gsi, tree &new_lhs, tree &new_rhs) { - imm_use_iterator imm_iter; - use_operand_p use_p; bool processed = false; if (!gsi_one_before_end_p (*gsi)) @@ -7640,10 +8348,13 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) if (!rewrite_lhs_rhs (lhs, rhs1, newlhs, newrhs)) return false; - tree size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))); - tree num; + tree struct_type = TREE_TYPE (TREE_TYPE (lhs)); + tree size = TYPE_SIZE_UNIT (struct_type); + tree num = NULL_TREE; /* Check if rhs2 is a multiplication of the size of the type. */ - if (!is_result_of_mult (rhs2, &num, size) + if ((current_fc_level != fc_level::DYNAMIC + || !POINTER_TYPE_P (struct_type)) + && !is_result_of_mult (rhs2, &num, size) && !(current_layout_opt_level & SEMI_RELAYOUT)) internal_error ( "The rhs of pointer is not a multiplicate and it slips through"); @@ -7781,6 +8492,7 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) } tree newlhs[max_split]; tree newrhs[max_split]; + cur_srfd = NULL; if (!rewrite_lhs_rhs (lhs, rhs, newlhs, newrhs)) { if (dump_file && (dump_flags & TDF_DETAILS)) @@ -7798,6 +8510,9 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) if (current_layout_opt_level >= POINTER_COMPRESSION_SAFE) try_rewrite_with_pointer_compression (stmt, gsi, lhs, rhs, newlhs[i], newrhs[i]); + if (current_fc_level == fc_level::DYNAMIC + && cur_srfd && cur_srfd->dfc_type_change_p ()) + dynamic_fc_rewrite_assign (stmt, rhs, newlhs[i], newrhs[i]); remove = true; if (fields_copied) continue; @@ -7807,7 +8522,10 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) tree conv_rhs = build_convert_gimple (lhs_expr, rhs_expr, gsi); if (conv_rhs) rhs_expr = conv_rhs; - + if (rhs_expr && get_gimple_rhs_class (TREE_CODE (rhs_expr)) + == GIMPLE_INVALID_RHS) + rhs_expr = gimplify_build1 (gsi, NOP_EXPR, TREE_TYPE (rhs_expr), + rhs_expr); gimple *newstmt = gimple_build_assign (lhs_expr, rhs_expr); if (dump_file && (dump_flags & TDF_DETAILS)) { @@ -7819,17 +8537,44 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) return remove; } - return remove; -} + if (gimple_assign_cast_p (stmt)) + { + tree rhs = gimple_assign_rhs1 (stmt); + tree newrhs[max_split]; + if (!rewrite_expr (rhs, newrhs)) + return false; -tree -ipa_struct_reorg::get_real_allocated_ptr (tree ptr, gimple_stmt_iterator *gsi) -{ - tree ptr_to_int = fold_convert (long_unsigned_type_node, ptr); - tree align = build_int_cst (long_unsigned_type_node, relayout_part_size); - tree real_addr = gimplify_build2 (gsi, MINUS_EXPR, long_unsigned_type_node, - ptr_to_int, align); - tree res = gimplify_build1 (gsi, NOP_EXPR, + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nrewriting cast statement:\n"); + print_gimple_stmt (dump_file, stmt, 0); + } + + tree lhs = gimple_assign_lhs (stmt); + tree conv_rhs = fold_convert (TREE_TYPE (lhs), newrhs[0]); + gimple *newstmt = gimple_build_assign (lhs, conv_rhs); + gsi_insert_before (gsi, newstmt, GSI_SAME_STMT); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "replaced with:\n"); + print_gimple_stmt (dump_file, newstmt, 0); + fprintf (dump_file, "\n"); + } + return true; + } + + return remove; +} + +tree +ipa_struct_reorg::get_real_allocated_ptr (tree ptr, gimple_stmt_iterator *gsi) +{ + tree ptr_to_int = fold_convert (long_unsigned_type_node, ptr); + tree align = build_int_cst (long_unsigned_type_node, relayout_part_size); + tree real_addr = gimplify_build2 (gsi, MINUS_EXPR, long_unsigned_type_node, + ptr_to_int, align); + tree res = gimplify_build1 (gsi, NOP_EXPR, build_pointer_type (long_unsigned_type_node), real_addr); return res; @@ -7942,6 +8687,9 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) if (!decl || !decl->type) return false; srtype *type = decl->type; + if (type->has_escaped () || !type->has_new_type ()) + return false; + tree num = allocate_size (type, decl, stmt); gcc_assert (num); memset (newrhs1, 0, sizeof (newrhs1)); @@ -8059,9 +8807,9 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) { if (t && t->semi_relayout) newexpr[0] = get_real_allocated_ptr (newexpr[0], gsi); - gimple_call_set_arg (stmt, 0, newexpr[0]); - update_stmt (stmt); - return false; + gimple_call_set_arg (stmt, 0, newexpr[0]); + update_stmt (stmt); + return false; } for (unsigned i = 0; i < max_split && newexpr[i]; i++) @@ -8087,6 +8835,7 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) /* Add a safe func mechanism. */ if (current_layout_opt_level >= STRUCT_REORDER_FIELDS + && current_fc_level != fc_level::DYNAMIC && f && f->is_safe_func) { tree expr = gimple_call_arg (stmt, 0); @@ -8105,9 +8854,35 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) /* Did not find the function or had not cloned it return saying don't change the function call. */ - if (!f || !f->newf) + if (!f) return false; + if (current_fc_level == fc_level::DYNAMIC) + { + if (f->partial_clone_p ()) + return false; + f = f->fc_path.cloned_func; + } + else + { + if (!f->newf) + return false; + /* Move over to the new function. */ + f = f->newf; + } + + if (current_fc_level == fc_level::DYNAMIC && f->is_safe_func) + { + tree expr = gimple_call_arg (stmt, 0); + tree newexpr[max_split] = {NULL_TREE}; + if (rewrite_expr (expr, newexpr) && newexpr[1] == NULL_TREE) + gimple_call_set_arg (stmt, 0, newexpr[0]); + + gimple_call_set_fndecl (stmt, f->node->decl); + update_stmt (stmt); + return false; + } + if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Changing arguments for function call :\n"); @@ -8115,9 +8890,6 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) fprintf (dump_file, "\n"); } - /* Move over to the new function. */ - f = f->newf; - tree chain = gimple_call_chain (stmt); unsigned nargs = gimple_call_num_args (stmt); auto_vec vargs (nargs); @@ -8401,9 +9173,8 @@ ipa_struct_reorg::rewrite_functions (void) if (flag_ipa_struct_sfc_shadow) { - for (unsigned i = 0; i < fc_infos.length (); i++) + for (auto *info : fc_infos) { - fc_type_info *info = fc_infos[i]; if (!info || !info->static_fc_p) continue; cleanup_shadow_write (info); @@ -8425,6 +9196,11 @@ ipa_struct_reorg::rewrite_functions (void) if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "\nNo rewrite:\n"); + if (current_function_decl == NULL) + { + fprintf (dump_file, "\ncurrent_function_decl == NULL\n"); + continue; + } if (current_function_decl) dump_function_to_file (current_function_decl, dump_file, dump_flags | TDF_VOPS); @@ -8511,39 +9287,7 @@ ipa_struct_reorg::rewrite_functions (void) i, f->node->name ()); } FOR_EACH_BB_FN (bb, cfun) - { - for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);) - { - if (rewrite_phi (si.phi ())) - si = gsi_start_phis (bb); - else - gsi_next (&si); - } - - for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) - { - gimple *stmt = gsi_stmt (si); - if (rewrite_stmt (stmt, &si)) - gsi_remove (&si, true); - else - gsi_next (&si); - } - } - - /* Debug statements need to happen after all other statements - have changed. */ - FOR_EACH_BB_FN (bb, cfun) - { - for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) - { - gimple *stmt = gsi_stmt (si); - if (gimple_code (stmt) == GIMPLE_DEBUG - && rewrite_debug (stmt, &si)) - gsi_remove (&si, true); - else - gsi_next (&si); - } - } + rewrite_block (bb); /* Release the old SSA_NAMES for old arguments. */ if (f->old) @@ -8599,6 +9343,39 @@ ipa_struct_reorg::rewrite_functions (void) return retval | TODO_verify_all; } +void +ipa_struct_reorg::rewrite_block (basic_block bb) +{ + for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si);) + { + if (rewrite_phi (si.phi ())) + si = gsi_start_phis (bb); + else + gsi_next (&si); + } + + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (rewrite_stmt (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } + + /* Debug statements need to happen after all other statements + have changed. */ + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);) + { + gimple *stmt = gsi_stmt (si); + if (gimple_code (stmt) == GIMPLE_DEBUG + && rewrite_debug (stmt, &si)) + gsi_remove (&si, true); + else + gsi_next (&si); + } +} + unsigned int ipa_struct_reorg::execute_struct_relayout (void) { @@ -8711,9 +9488,11 @@ ipa_struct_reorg::check_and_prune_struct_for_pointer_compression (void) if (!type->has_legal_alloc_num) { if (current_layout_opt_level & POINTER_COMPRESSION_UNSAFE) + { if (dump_file) fprintf (dump_file, " has unknown alloc size, but" " in unsafe mode, so"); + } else { if (dump_file) @@ -8857,7 +9636,7 @@ ipa_struct_reorg::check_and_prune_struct_for_field_compression (void) if (!find_field_compression_candidate (type)) continue; - gcc_assert (type->fc_info->static_fc_p); + gcc_assert (type->fc_info->static_fc_p ^ type->fc_info->dynamic_fc_p); if (dump_file) { fprintf (dump_file, "[field compress] Found candidate: "); @@ -8886,13 +9665,19 @@ ipa_struct_reorg::find_field_compression_candidate (srtype *type) /* Classify fields by field type firstly. */ classify_fields (info); - if (flag_ipa_struct_sfc) + if (current_fc_level == fc_level::STATIC) { FC_DUMP_MSG ("Looking for static fc fields\n"); info->static_fc_p = find_static_fc_fields (info); } - if (!info->static_fc_p) + if (current_fc_level == fc_level::DYNAMIC) + { + FC_DUMP_MSG ("Looking for dynamic fc fields\n"); + info->dynamic_fc_p = find_dynamic_fc_fields (info); + } + + if (!info->static_fc_p && !info->dynamic_fc_p) { FC_DUMP_MSG ("Fail finding field compression candidate\n"); return false; @@ -8901,6 +9686,8 @@ ipa_struct_reorg::find_field_compression_candidate (srtype *type) if (!compress_fields (info)) { FC_DUMP_MSG ("Fail compressing fields\n"); + info->static_fc_p = false; + info->dynamic_fc_p = false; return false; } @@ -8950,7 +9737,7 @@ ipa_struct_reorg::find_static_fc_fields (fc_type_info *info) continue; /* We have marked these fields as shadow, so skip them. */ - if (fc_fields_contains (info->static_fc_fields, srf->fielddecl)) + if (find_fc_field (info->static_fc_fields, srf->fielddecl)) continue; found_static_compress |= static_compress_p (info, srf->fielddecl); @@ -8979,15 +9766,20 @@ ipa_struct_reorg::find_static_fc_fields (fc_type_info *info) bool ipa_struct_reorg::compress_fields (fc_type_info *info) { - if (info->static_fc_p && !compress_fields_static (info)) - info->static_fc_p = false; - - if (!info->static_fc_p) - return false; + gcc_assert (info->static_fc_p ^ info->dynamic_fc_p); - compress_to_bitfields (info); - - return true; + if (info->static_fc_p) + { + return compress_fields_static (info) + && compress_to_bitfield_static (info); + } + else + { + return compress_fields_dynamic (info) + && compress_to_bitfield_dynamic (info) + && calc_dynamic_boundary (info) + && check_closure (info); + } } /* Check if the type has any field that can be shadowed. */ @@ -9039,6 +9831,7 @@ ipa_struct_reorg::find_shadow_fields (fc_type_info *info, /* Unpair assignment checking. */ auto &srfields = field_class->srfields; unsigned original_index = 0; + tree init_const = NULL_TREE; if (shadow_info.unpair_stmt) { if (dump_file && (dump_flags & TDF_DETAILS)) @@ -9055,6 +9848,19 @@ ipa_struct_reorg::find_shadow_fields (fc_type_info *info, return false; } + if (current_fc_level == fc_level::DYNAMIC) + { + if (!shadow_info.unpair_stmt) + return false; + /* We have proved that the unpair_stmt is single assign. */ + init_const = gimple_assign_rhs1 (shadow_info.unpair_stmt); + if (TREE_CODE (init_const) != INTEGER_CST) + return false; + + if (!unique_init_const_p (shadow_info)) + return false; + } + /* Add a new static fc_field. */ srfield *original_srf = srfields[original_index]; @@ -9066,7 +9872,10 @@ ipa_struct_reorg::find_shadow_fields (fc_type_info *info, continue; fc_field *fc_f = new fc_field (shadow_srf->fielddecl, 1, original_srf); - info->static_fc_fields.safe_push (fc_f); + auto &fc_fields = current_fc_level == fc_level::STATIC + ? info->static_fc_fields : info->dynamic_shadow_fields; + fc_fields.safe_push (fc_f); + fc_f->init_const = init_const; /* Not NULL only in dynamic. */ /* Record all shadow stmts to fc_field. */ unsigned j; @@ -9227,15 +10036,30 @@ ipa_struct_reorg::write_field_in_fc_class_p (gimple *stmt, return field; } -fc_field * -ipa_struct_reorg::fc_fields_contains (auto_vec &fc_fields, - tree field) +/* Check if the init_const is a unique constant, which is different from all + constant rhs of pair statements. */ + +bool +ipa_struct_reorg::unique_init_const_p (const fc_shadow_info &shadow_info) { - for (auto *fc_f : fc_fields) - if (fc_f->field == field) - return fc_f; + tree init_const = gimple_assign_rhs1 (shadow_info.unpair_stmt); + HOST_WIDE_INT value = tree_to_shwi (init_const); + for (auto *stmts : shadow_info.pair_stmts_groups) + { + /* We have prove all rhs in a group are equal, checking one of them + is enough. */ + tree rhs = gimple_assign_rhs1 ((*stmts)[0]); + if (TREE_CODE (rhs) != INTEGER_CST) + continue; - return NULL; + if (tree_to_shwi (rhs) == value) + { + FC_DUMP_MSG ("Init const is not unique.\n"); + return false; + } + } + + return true; } /* Check if the right operands of all assignments are equal. */ @@ -9301,12 +10125,6 @@ ipa_struct_reorg::fc_operand_equal_p (tree var1, tree var2) bool ipa_struct_reorg::fc_global_const_p (tree var, HOST_WIDE_INT &value) { - srtype *type; - srfield *field; - tree base; - if (!get_base_type (var, base, type, field) || type->has_escaped ()) - return false; - const_map *cm = find_global_const (var); if (cm) { @@ -9314,33 +10132,44 @@ ipa_struct_reorg::fc_global_const_p (tree var, HOST_WIDE_INT &value) return true; } + if (visited_vars.contains (var)) + return false; + visited_vars.add (var); + bool is_const = false; HOST_WIDE_INT const_value = 0; - for (auto *access : type->accesses) + for (auto *srfn : functions) { - SET_CFUN (access->function); - - gimple *stmt = access->stmt; - if (!gimple_assign_single_p (stmt) - || !operand_equal_p (gimple_assign_lhs (stmt), var)) - continue; + SET_CFUN (srfn); + basic_block bb = NULL; + FOR_EACH_BB_FN (bb, cfun) + { + for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); + gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + if (!gimple_assign_single_p (stmt) + || !operand_equal_p (gimple_assign_lhs (stmt), var)) + continue; - if (!fc_peephole_const_p (gimple_assign_rhs1 (stmt), value)) - return false; + if (!fc_peephole_const_p (gimple_assign_rhs1 (stmt), value)) + return false; - /* Make sure the value is never changed. */ - if (is_const) - { - if (value != const_value) - return false; - continue; - } + /* Make sure the value is never changed. */ + if (is_const) + { + if (value != const_value) + return false; + continue; + } - is_const = true; - const_value = value; + is_const = true; + const_value = value; - /* Record a global constant here. */ - global_consts.safe_push (new const_map (var, value)); + /* Record a global constant here. */ + global_consts.safe_push (new const_map (var, value)); + } + } } return is_const; @@ -9723,7 +10552,7 @@ ipa_struct_reorg::struct_copy_p (gimple *stmt, tree type) { if (!gimple_assign_single_p (stmt) || TREE_TYPE (gimple_assign_lhs (stmt)) != type - || !types_compatible_p (TREE_TYPE (gimple_assign_rhs1 (stmt)), type)) + || !types_fc_compatible_p (TREE_TYPE (gimple_assign_rhs1 (stmt)), type)) return false; if (dump_file && (dump_flags & TDF_DETAILS)) @@ -9753,8 +10582,7 @@ ipa_struct_reorg::find_hot_access (fc_type_info *info, SET_CFUN (access->function); basic_block bb = access->stmt->bb; - if (!bb->loop_father->num - || !access->write_type_p (type->type)) + if (!bb->loop_father->num || !access->write_p ()) continue; /* Case (1). */ @@ -9766,7 +10594,7 @@ ipa_struct_reorg::find_hot_access (fc_type_info *info, continue; tree fielddecl = access->field->fielddecl; - if (!fielddecl || !fc_fields_contains (fc_fields, fielddecl)) + if (!fielddecl || !find_fc_field (fc_fields, fielddecl)) continue; auto &set = write_map.get_or_insert (bb); @@ -9797,8 +10625,8 @@ ipa_struct_reorg::cleanup_shadow_write (fc_type_info *info) { SET_CFUN (fc_f->shadow_stmts_func[i]); gcc_assert (gimple_assign_single_p (stmt)); - gimple_assign_set_rhs1 ( - stmt, build_int_cst (TREE_TYPE (fc_f->field), 1)); + tree newrhs = build_int_cst (TREE_TYPE (fc_f->field), 1); + gimple_assign_set_rhs1 (stmt, newrhs); update_stmt (stmt); } } @@ -9820,7 +10648,7 @@ ipa_struct_reorg::rewrite_shadow_read (fc_type_info *info) continue; SET_CFUN (access->function); - insert_shadow_stmt (access->stmt, access->index, + modify_shadow_read (access->stmt, access->index, fc_f, access->base); } } @@ -9829,14 +10657,16 @@ ipa_struct_reorg::rewrite_shadow_read (fc_type_info *info) /* Insert the followings for shadow data read before STMT. The IDX operand is the shadow data. - * For static: (shadow_field == true) ? original_field : 0 */ + * For static: (shadow_field == true) ? original_field : 0 + * For dynamic: (original_field != init_const) ? original_field : 0 + */ void -ipa_struct_reorg::insert_shadow_stmt (gimple *stmt, unsigned idx, - fc_field *fc_field, tree base) +ipa_struct_reorg::modify_shadow_read (gimple *stmt, unsigned idx, + fc_field *field, tree base) { tree shadow = gimple_op (stmt, idx); - tree original = build_field_ref (base, fc_field->original->fielddecl); + tree original = build_field_ref (base, field->original->fielddecl); /* Insert new stmt immediately before stmt. */ gimple_stmt_iterator gsi = gsi_for_stmt (stmt); @@ -9847,15 +10677,26 @@ ipa_struct_reorg::insert_shadow_stmt (gimple *stmt, unsigned idx, gsi_insert_before (&gsi, original_stmt, GSI_SAME_STMT); update_stmt (original_stmt); - /* shadow_ssa = shadow */ - tree shadow_ssa = make_temp_ssa_name (TREE_TYPE (shadow), NULL, ""); - gimple *shadow_stmt = gimple_build_assign (shadow_ssa, shadow); - gsi_insert_before (&gsi, shadow_stmt, GSI_SAME_STMT); - update_stmt (shadow_stmt); + tree cond = NULL_TREE; + if (current_fc_level == fc_level::DYNAMIC) + { + field->original->get_closure ()->add_read_change (original_stmt); + /* new_shadow_ssa = (original_ssa != init_const ? original_ssa : 0) */ + cond = fold_build2 (NE_EXPR, boolean_type_node, original_ssa, + field->init_const); + } + else + { + /* shadow_ssa = shadow */ + tree shadow_ssa = make_temp_ssa_name (TREE_TYPE (shadow), NULL, ""); + gimple *shadow_stmt = gimple_build_assign (shadow_ssa, shadow); + gsi_insert_before (&gsi, shadow_stmt, GSI_SAME_STMT); + update_stmt (shadow_stmt); - /* new_shadow_ssa = (shadow_ssa == true ? original_ssa : 0) */ - tree cond = fold_build2 (EQ_EXPR, boolean_type_node, shadow_ssa, - build_int_cst (TREE_TYPE (shadow), 1)); + /* new_shadow_ssa = (shadow_ssa == true ? original_ssa : 0) */ + cond = fold_build2 (EQ_EXPR, boolean_type_node, shadow_ssa, + build_int_cst (TREE_TYPE (shadow), 1)); + } tree new_shadow = build_cond_expr (cond, original_ssa, build_int_cst (TREE_TYPE (shadow), 0)); @@ -9908,34 +10749,97 @@ ipa_struct_reorg::compress_fields_static (fc_type_info *info) /* Compress fields to bitfield, for which bits will be the width. */ -void -ipa_struct_reorg::compress_to_bitfields (fc_type_info *info) +bool +ipa_struct_reorg::compress_to_bitfield_static (fc_type_info *info) { - /* For static compression. Calculate bitsize for static field. */ - if (flag_ipa_struct_sfc_bitfield && info->static_fc_p) + if (!flag_ipa_struct_sfc_bitfield) + return true; + + for (auto *fc_f : info->static_fc_fields) { - for (auto *fc_f : info->static_fc_fields) + HOST_WIDE_INT max_value = fc_f->max_value; + gcc_assert (max_value > 0 && max_value <= UINT_MAX); + + /* Calculate bitsize. */ + fc_f->bits = 0; + while (max_value) { - HOST_WIDE_INT max_value = fc_f->max_value; - gcc_assert (max_value > 0 && max_value <= UINT_MAX); + fc_f->bits++; + max_value >>= 1; + } - /* Calculate bitsize. */ - fc_f->bits = 0; - while (max_value) - { - fc_f->bits++; - max_value >>= 1; - } + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Static bitfield: "); + print_generic_expr (dump_file, fc_f->field); + fprintf (dump_file, ":%d", fc_f->bits); + fprintf (dump_file, "\n"); + } + } - if (dump_file && (dump_flags & TDF_DETAILS)) - { - FC_DUMP_MSG ("Bitfield: "); - print_generic_expr (dump_file, fc_f->field); - fprintf (dump_file, ":%d", fc_f->bits); - fprintf (dump_file, "\n"); - } + return true; +} + +/* Compress fields to bitfield for dynamic field compression. */ + +bool +ipa_struct_reorg::compress_to_bitfield_dynamic (fc_type_info *info) +{ + if (!flag_ipa_struct_dfc_bitfield) + return true; + + calc_fc_ref_count (info); + + /* Collect existing bitfields. */ + unsigned total_static_bits = 0; + for (auto *srf : info->type->fields) + { + tree field = srf->fielddecl; + if (DECL_BIT_FIELD (field)) + total_static_bits += tree_to_uhwi (DECL_SIZE (field)); + } + + unsigned max_ref_cnt = 0; + fc_field *max_f = NULL; + fc_cond *max_cond = NULL; + for (auto *cond : info->fc_conds) + { + /* Heuristically, only try bit field for big data size. */ + if (TYPE_MAIN_VARIANT (cond->old_type) != long_integer_type_node) + continue; + + /* Find the hottest field. */ + for (auto *fc_f : cond->fields) + { + if (fc_f->ref_cnt <= max_ref_cnt) + continue; + + max_ref_cnt = fc_f->ref_cnt; + max_f = fc_f; + max_cond = cond; } } + + /* Choose the hottest candidate to try bitfield. */ + unsigned new_type_bits = TYPE_PRECISION (max_f->new_type); + if (new_type_bits <= total_static_bits) + return false; + + /* The fc condition covering this field is marked as bitfield, + although not all of the fields for this condition are marked as + bitfield. */ + max_f->bits = new_type_bits - total_static_bits; + max_cond->bits = max_f->bits; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Dynamic bitfield: "); + print_generic_expr (dump_file, max_f->field); + fprintf (dump_file, ":%d", max_f->bits); + fprintf (dump_file, "\n"); + } + + return true; } /* Collect all blocks that can reach stmt. */ @@ -9969,51 +10873,2850 @@ ipa_struct_reorg::collect_all_predecessor (gimple *stmt) return blocks; } -/* Init pointer size from parameter param_pointer_compression_size. */ - -static void -init_pointer_size_for_pointer_compression (void) +bool +ipa_struct_reorg::types_fc_equal_p (tree type1, tree type2) { - switch (param_pointer_compression_size) + if (type1 == type2) + return true; + if (TREE_CODE (type1) != TREE_CODE (type2)) + return false; + + const char *tname1; + const char *tname2; + size_t len1; + size_t len2; + const char *p; + + switch (TREE_CODE (type1)) { - case 8: - // FALLTHRU - case 16: - // FALLTHRU - case 32: compressed_size = param_pointer_compression_size; break; + case POINTER_TYPE: + return types_fc_equal_p (inner_type (type1), inner_type (type2)); + + case RECORD_TYPE: + tname1 = get_type_name (type1); + tname2 = get_type_name (type2); + if (!tname1 || !tname2) + return false; + + len1 = strlen (tname1); + len2 = strlen (tname2); + if (len1 > len2) + { + std::swap (len1, len2); + std::swap (tname1, tname2); + } + + p = strstr (tname2, tname1); + if (!p) + return false; + p += len1; + + /* As suffixes with '.' are generated by compiler, should be safe to + skip the rest of p. */ + return STRING_STARTS_WITH (p, ".reorg"); + default: - error ("Invalid pointer compression size, using the following param: " - "\"--param compressed-pointer-size=[8,16,32]\""); + return false; } } -unsigned int -ipa_struct_reorg::execute (unsigned int opt) +bool +ipa_struct_reorg::types_fc_compatible_p (tree type1, tree type2) { - unsigned int ret = 0; + return types_compatible_p (type1, type2) || types_fc_equal_p (type1, type2); +} - if (dump_file) - fprintf (dump_file, "\n\n====== ipa_struct_reorg level %d ======\n\n", - opt); +/* Scan all of fields to check whether each can be dynamically + compressed or not. */ - if (opt != COMPLETE_STRUCT_RELAYOUT) - { - current_layout_opt_level = opt; - /* If there is a top-level inline-asm, - the pass immediately returns. */ - if (symtab->first_asm_symbol ()) - return 0; - record_accesses (); - prune_escaped_types (); - if (current_layout_opt_level == STRUCT_SPLIT) - analyze_types (); +bool +ipa_struct_reorg::find_dynamic_fc_fields (fc_type_info *info) +{ + if (flag_ipa_struct_dfc_shadow) + find_shadow_fields (info); + + if (!find_fields_in_input_stmt (info)) + { + FC_DUMP_MSG ("Fail finding fields in input stmt\n"); + return false; + } + + if (!find_fopen_fclose (info)) + { + FC_DUMP_MSG ("Fail finding fopen/fclose stmt\n"); + return false; + } + + /* Avoid compressing fields without hot access. */ + if (!find_hot_access (info, info->dynamic_fc_fields)) + { + FC_DUMP_MSG ("Fail finding hot access for dynamic\n"); + return false; + } + + if (!check_dynamic_shadow_fields (info)) + { + FC_DUMP_MSG ("Fail checking dynamic shadow fields\n"); + return false; + } + + if (!find_fc_paths (info)) + { + FC_DUMP_MSG ("Fail finding fc paths\n"); + return false; + } + + if (!find_fc_data (info)) + { + FC_DUMP_MSG ("Fail finding fc data\n"); + return false; + } + + return true; +} + +/* Find the stmt that read data from a file, the fields that can be affected + by the input data will be treated as the dynamic field compression + candidate fields. */ + +bool +ipa_struct_reorg::find_fields_in_input_stmt (fc_type_info *info) +{ + basic_block input_bb = NULL; + + for (auto *access : info->type->accesses) + { + if (!access->write_field_p ()) + continue; + + srfield *field = access->field; + if (find_fc_field (info->dynamic_shadow_fields, field->fielddecl) + || find_fc_field (info->dynamic_fc_fields, field->fielddecl)) + continue; + + /* Skip dead field. */ + if (field->dead_field_p ()) + continue; + + if (TREE_CODE (field->fieldtype) != INTEGER_TYPE) + continue; + + SET_CFUN (access->function); + /* Guarantee this struct field is from a file. */ + gimple *input_stmt = NULL; + gimple *var_stmt = NULL; + if (!find_input_stmt (access->stmt, input_stmt, var_stmt) + || gimple_bb (input_stmt)->loop_father->num == 0) + continue; + + tree var = gimple_assign_rhs1 (var_stmt); + + if (!info->input_stmt) + { + info->input_stmt = input_stmt; + info->input_var = access->base; + info->input_file_handler = find_file_handler (input_stmt); + if (!info->input_file_handler) + return false; + } + + /* Support only one input stmt now. */ + if (info->input_stmt != input_stmt + || info->input_var != access->base) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Found a dynamic compression field: "); + print_generic_expr (dump_file, field->fielddecl); + fprintf (dump_file, ", input var: "); + print_generic_expr (dump_file, var); + fprintf (dump_file, "\n"); + } + + tree in_ssa = gimple_assign_rhs1 (access->stmt); + fc_field *fc_f = new fc_field (field->fielddecl, var, in_ssa); + info->dynamic_fc_fields.safe_push (fc_f); + + /* All fc fields should define their ssas in the same block. */ + if (!input_bb) + input_bb = gimple_bb (SSA_NAME_DEF_STMT (in_ssa)); + else if (input_bb != gimple_bb (SSA_NAME_DEF_STMT (in_ssa))) + return false; + + info->start_srfn = access->function; + } + + if (info->dynamic_fc_fields.is_empty ()) + return false; + + /* Sort all fields in the order of input ssa position. This is required + to simplify the min_val and max_val calculation. */ + SET_CFUN (info->start_srfn); + renumber_gimple_stmt_uids_in_blocks (&input_bb, 1); + info->dynamic_fc_fields.qsort (input_order_cmp); + + return true; +} + +/* Find the input stmt for the rhs of the given stmt. + Now we only support sscanf. */ + +bool +ipa_struct_reorg::find_input_stmt (gimple *stmt, gimple *&input_stmt, + gimple *&var_stmt) +{ + /* Check pattern fc_type->field = _ssa_name. */ + if (!gimple_assign_single_p (stmt) + || TREE_CODE (gimple_assign_rhs1 (stmt)) != SSA_NAME) + return false; + + stmt = strip_copy_stmts (SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt))); + /* Check pattern _ssa_name = var. */ + if (!gimple_assign_single_p (stmt) + || !VAR_P (gimple_assign_rhs1 (stmt))) + return false; + + var_stmt = stmt; + tree var = gimple_assign_rhs1 (stmt); + + /* Search backward to find a sscanf stmt. */ + while (gimple_bb (stmt)) + { + tree vuse = gimple_vuse (stmt); + if (!vuse) + break; + + stmt = SSA_NAME_DEF_STMT (vuse); + if (!gimple_call_builtin_p (stmt, BUILT_IN_SSCANF)) + continue; + + /* Search '&var' from the 3th arguments. */ + for (unsigned i = 2; i < gimple_call_num_args (stmt); i++) + { + tree arg = gimple_call_arg (stmt, i); + if (TREE_CODE (arg) != ADDR_EXPR + || TREE_OPERAND (arg, 0) != var) + continue; + + input_stmt = stmt; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Found input stmt: "); + print_gimple_stmt (dump_file, stmt, 0); + } + return true; + } + + /* The sscanf stmt doesn't contain 'var', so the check failed. */ + break; + } + + return false; +} + +/* Find the file handler, which holds the file from which the given stmt + read data. + Now only support sscanf. */ + +tree +ipa_struct_reorg::find_file_handler (gimple *input_stmt) +{ + if (!gimple_call_builtin_p (input_stmt, BUILT_IN_SSCANF)) + return NULL_TREE; + + /* Find fgets stmt. */ + gimple *stmt = SSA_NAME_DEF_STMT (gimple_vuse (input_stmt)); + if (gimple_code (stmt) != GIMPLE_CALL) + return NULL_TREE; + + tree callee = gimple_call_fn (stmt); + if (callee && TREE_CODE (callee) == OBJ_TYPE_REF) + return NULL_TREE; + + callee = gimple_call_fndecl (stmt); + const char *fn_name = get_func_name (callee); + if (!fn_name || strcmp (fn_name, "fgets") != 0) + return NULL_TREE; + + /* Check fget is using the string for sscanf. */ + tree fget_arg0 = gimple_call_arg (stmt, 0); + tree sscanf_arg0 = gimple_call_arg (input_stmt, 0); + if (TREE_OPERAND (fget_arg0, 0) != TREE_OPERAND (sscanf_arg0, 0)) + return NULL_TREE; + + return gimple_call_arg (stmt, 2); +} + +/* Find fclose in start function. */ + +bool +ipa_struct_reorg::find_fopen_fclose (fc_type_info *info) +{ + SET_CFUN (info->start_srfn); + + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + { + for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) + { + gimple *stmt = gsi_stmt (si); + if (!is_gimple_call (stmt)) + continue; + + tree decl = gimple_call_fndecl (stmt); + const char *callee = get_func_name (decl); + if (!callee || strcmp (callee, "fclose") != 0) + continue; + + /* The fclose must use the same file handler as the fget, + which reads data for sscanf. */ + tree fh = gimple_call_arg (stmt, 0); + if (fh != info->input_file_handler) + continue; + + info->fclose_stmt = stmt; + renumber_gimple_stmt_uids_in_blocks (&bb, 1); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nFound fclose in function %s:\n", + get_func_name (info->start_srfn->node->decl)); + print_gimple_stmt (dump_file, stmt, 0); + } + + return true; + } + } + + return false; +} + +/* Check whether the original of a dynamic shadow field is one of + dynamic_fc_fields. */ + +bool +ipa_struct_reorg::check_dynamic_shadow_fields (fc_type_info *info) +{ + for (auto *shadow_field : info->dynamic_shadow_fields) + { + srfield *original = shadow_field->original; + fc_field *input_field = find_fc_field (info->dynamic_fc_fields, + original->fielddecl); + if (!input_field) + return false; + + shadow_field->input_field = input_field; + shadow_field->input_ssa = input_field->input_ssa; + } + + return true; +} + +bool +ipa_struct_reorg::find_fc_paths (fc_type_info *info) +{ + /* Start point function. */ + srfunction *srfn = info->start_srfn; + gimple *start_stmt = info->fclose_stmt; + + while (srfn) + { + /* Already seen. */ + if (srfn->fc_path.start_stmt) + return srfn->fc_path.start_stmt == start_stmt; + + SET_CFUN (srfn); + + srfn->fc_path.start_stmt = start_stmt; + if (!srfn->fc_path.collect_blocks (start_stmt, fc_path_info::PRED) + || !srfn->fc_path.collect_blocks (start_stmt, fc_path_info::SUCC)) + return false; + + /* Start at the entry function. */ + if (srfn->entry_function_p ()) + return true; + + /* The current function should only be called once. */ + cgraph_edge *edge = srfn->node->callers; + if (!edge || edge->next_caller || !edge->call_stmt) + return false; + + srfn = find_function (edge->caller); + start_stmt = edge->call_stmt; + } + + return false; +} + +bool +ipa_struct_reorg::find_fc_data (fc_type_info *info) +{ + if (!find_fc_arrays (info)) + { + FC_DUMP_MSG ("Fail finding fc arrays\n"); + return false; + } + + if (!check_fc_array_uses (info)) + { + FC_DUMP_MSG ("Fail checking fc array uses\n"); + return false; + } + + if (!find_fc_refs (info)) + { + FC_DUMP_MSG ("Fail finding fc refs\n"); + return false; + } + + return true; +} + +/* Find all arrays to be cached: + 1. Defined by malloc/calloc (before start-point) + 2. Will be used after fclose (e.g. global variables) */ + +bool +ipa_struct_reorg::find_fc_arrays (fc_type_info *info) +{ + varpool_node *vnode; + tree type = info->type->type; + + /* 1) Process all global vars, search for arrays of cached objects. */ + FOR_EACH_VARIABLE (vnode) + { + tree node = vnode->decl; + tree node_type = TREE_TYPE (node); + /* Global object is not supported. */ + if (types_fc_compatible_p (node_type, type)) + return false; + + switch (TREE_CODE (node_type)) + { + /* POINTER->RECORD(fc_type) */ + case POINTER_TYPE: + if (types_fc_compatible_p (TREE_TYPE (node_type), type) + && !find_fc_array (info, node, vnode)) + return false; + break; + /* RECORD->POINTER->RECORD(fc_type) */ + case RECORD_TYPE: + for (tree t = TYPE_FIELDS (node_type); t; t = DECL_CHAIN (t)) + { + if (TREE_CODE (t) != FIELD_DECL) + continue; + + tree field_type = TREE_TYPE (t); + if (TREE_CODE (field_type) == RECORD_TYPE) + { + FC_DUMP_MSG ("RECORD->RECORD->... not supported\n"); + return false; + } + + if (POINTER_TYPE_P (field_type) + && types_fc_compatible_p (TREE_TYPE (field_type), type)) + { + tree field_node = build3 (COMPONENT_REF, field_type, node, + t, NULL_TREE); + if (!find_fc_array (info, field_node, vnode)) + return false; + } + /* More safe: trace-back following VDEF/VUSE. */ + } + break; + case ARRAY_TYPE: + case UNION_TYPE: + case QUAL_UNION_TYPE: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("node_type not handled: "); + print_generic_expr (dump_file, node_type); + fprintf (dump_file, "\n"); + } + return false; + default: + break; + } + } + + return !info->fc_arrays.is_empty (); +} + +/* Check if node is a array to be cached, if so, record it. + vnode contains referrring to node. */ + +bool +ipa_struct_reorg::find_fc_array (fc_type_info *info, tree node, + varpool_node *vnode) +{ + tree size_expr = NULL_TREE; + tree ssa_def = NULL_TREE; + + ipa_ref *ref = NULL; + for (unsigned i = 0; vnode->iterate_referring (i, ref); i++) + { + /* Filter for writes to NODE. */ + if (ref->use != IPA_REF_STORE) + continue; + /* Ignore assignments after start-point. */ + if (!is_stmt_before_fclose (info, ref->stmt, ref->referring)) + continue; + tree lhs = gimple_get_lhs (ref->stmt); + if (!operand_equal_p (lhs, node, COMPARE_DECL_FLAGS)) + continue; + + tree new_size_expr = NULL_TREE; + if (!get_allocate_size_iterate (info->type->type, ref->stmt, + new_size_expr, &ssa_def)) + return false; + + if (new_size_expr) + { + if (size_expr) + { + FC_DUMP_MSG ("fc_array allocated twice before start-point\n"); + return false; + } + size_expr = new_size_expr; + + /* Allocation must happen at start function. */ + if (ref->referring != info->start_srfn->node) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Add array: "); + print_generic_expr (dump_file, node); + fprintf (dump_file, ", size: "); + print_generic_expr (dump_file, size_expr); + fprintf (dump_file, ", ssa_def: "); + print_generic_expr (dump_file, ssa_def); + fprintf (dump_file, "\n"); + } + } + } + + if (size_expr) + { + if (duplicative_array_p (info, ssa_def)) + return false; + + fc_array *array = new fc_array (node, size_expr, ssa_def, vnode); + info->fc_arrays.safe_push (array); + } + + return true; +} + +/* Give the SSA_DEF of a array, check if it's duplicative. */ + +bool +ipa_struct_reorg::duplicative_array_p (fc_type_info *info, tree ssa_def) +{ + for (auto *array : info->fc_arrays) + { + if (array->ssa_def != ssa_def) + continue; + + FC_DUMP_MSG ("Array assigned to multiple variable\n"); + return true; + } + + return false; +} + +bool +ipa_struct_reorg::is_stmt_before_fclose (fc_type_info *info, gimple *stmt, + symtab_node *node) +{ + gcc_assert (info->fclose_stmt); + srfunction *f = find_function (as_a (node)); + gcc_assert (f); + + if (gimple_bb (stmt) == gimple_bb (info->fclose_stmt)) + return gimple_uid (stmt) < gimple_uid (info->fclose_stmt); + + /* If array allocations are outside start-point's function, we may need to + create global vars to record the sizes. */ + return f->fc_path.pre_bbs.contains (gimple_bb (stmt)); +} + +/* Check if the VAR is a global pointer created by reorg. */ + +bool +ipa_struct_reorg::reorg_ptr_p (tree var) +{ + if (TREE_CODE (var) != VAR_DECL) + return false; + + const char *decl_name = IDENTIFIER_POINTER (DECL_NAME (var)); + if (!decl_name) + return false; + + const char *reorg_name = strstr (decl_name, ".reorg"); + if (!reorg_name) + return false; + + return strstr (reorg_name, "_gptr"); +} + +/* Return number of objects of TYPE following define chain from STMT. + If the number is not certain, set ERROR so we can abort field compression. + If SSA_DEF is not NULL, the ssa_name of allocated ptr will be assigned to it. + */ + +bool +ipa_struct_reorg::get_allocate_size_iterate (tree type, gimple *stmt, + tree &size, tree *ssa_def) +{ + if (!stmt) + return false; + + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + return get_allocate_size_assign (type, as_a (stmt), + size, ssa_def); + case GIMPLE_CALL: + return get_allocate_size_call (type, as_a (stmt), + size, ssa_def); + default: + return false; + } +} + +bool +ipa_struct_reorg::get_allocate_size_assign (tree type, gassign *stmt, + tree &size, tree *ssa_def) +{ + tree rhs = gimple_assign_rhs1 (stmt); + if ((!gimple_assign_single_p (stmt) && !gimple_assign_cast_p (stmt)) + || TREE_CODE (rhs) != SSA_NAME) + return true; + + gimple *def_stmt = SSA_NAME_DEF_STMT (rhs); + /* Handle the global arrays split by struct_reorg. */ + if (reorg_ptr_p (gimple_assign_lhs (stmt))) + return get_allocate_size_reorg_ptr (def_stmt, size); + + return get_allocate_size_iterate (type, def_stmt, size, ssa_def); +} + +bool +ipa_struct_reorg::get_allocate_size_call (tree type, gcall *stmt, + tree &size, tree *ssa_def) +{ + tree lhs = gimple_call_lhs (stmt); + gcc_assert (TREE_CODE (lhs) == SSA_NAME); + if (ssa_def) + *ssa_def = lhs; + + size = get_allocate_size (type, lhs, NULL_TREE, stmt); + + return size != NULL_TREE; +} + +/* Handle the global arrays split by struct_reorg: + 1) The new array ptrs are marked with suffix "_gptr". + 2) The array ptr are calculated with form: + _gptr0 = calloc (NUM, size_all); + _gptr1 = _gptr0 + NUM * sizeof (TREE_TYPE (_gptr0)); + _gptr2 = _gptr1 + NUM * sizeof (TREE_TYPE (_gptr1)); + ... + */ + +bool +ipa_struct_reorg::get_allocate_size_reorg_ptr (gimple *plus_stmt, tree &size) +{ + /* Check the POINTER_PLUS_EXPR. */ + if (!is_gimple_assign (plus_stmt) + || gimple_assign_rhs_code (plus_stmt) != POINTER_PLUS_EXPR) + return false; + + tree rhs1 = gimple_assign_rhs1 (plus_stmt); + tree rhs2 = gimple_assign_rhs2 (plus_stmt); + tree prev_type = TREE_TYPE (rhs1); + + /* Check the MULT_EXPR. */ + gcc_assert (TREE_CODE (rhs2) == SSA_NAME); + gimple *mul_stmt = SSA_NAME_DEF_STMT (rhs2); + if (!is_gimple_assign (mul_stmt) + || gimple_assign_rhs_code (mul_stmt) != MULT_EXPR) + return false; + + tree num = gimple_assign_rhs1 (mul_stmt); + tree mul_by = gimple_assign_rhs2 (mul_stmt); + if (TREE_CODE (mul_by) == SSA_NAME) + std::swap (num, mul_by); + + if (TREE_CODE (num) != SSA_NAME || TREE_CODE (mul_by) != INTEGER_CST + || !operand_equal_p (mul_by, TYPE_SIZE_UNIT (prev_type))) + return false; + + /* We can trace to original calloc/malloc to make this safer. */ + + size = num; + + return true; +} + +/* Returns the allocated size / T size for STMT. That is the number of + elements in the array allocated. */ + +tree +ipa_struct_reorg::get_allocate_size (tree type, tree decl, tree orig_type, + gimple *stmt) +{ + if (!stmt + || gimple_code (stmt) != GIMPLE_CALL + || !handled_allocation_stmt (stmt)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nNot an allocate statement:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return NULL_TREE; + } + + tree struct_size = TYPE_SIZE_UNIT (type); + + /* Specify the correct size to relax multi-layer pointer. */ + if (TREE_CODE (decl) == SSA_NAME && orig_type && isptrptr (orig_type)) + struct_size = TYPE_SIZE_UNIT (orig_type); + + tree size = gimple_call_arg (stmt, 0); + + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) + || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC)) + size = gimple_call_arg (stmt, 1); + else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) + { + tree arg1; + arg1 = gimple_call_arg (stmt, 1); + /* Check that second argument is a constant equal to + the size of structure. */ + if (operand_equal_p (arg1, struct_size, 0)) + return size; + /* ??? Check that first argument is a constant + equal to the size of structure. */ + /* If the allocated number is equal to the value of struct_size, + the value of arg1 is changed to the allocated number. */ + if (operand_equal_p (size, struct_size, 0)) + return arg1; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\ncalloc the correct size:\n"); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, "\n"); + } + return NULL_TREE; + } + + tree num; + if (!is_result_of_mult (size, &num, struct_size)) + return NULL_TREE; + + return num; +} + +/* Find all fc_refs (variables/arrays to be modified according to some + fc_array): + - Will be used after fclose (e.g. global variables). + - Before fclose, value or array content is assigned with references to some + recognized fc_array. (If there are multiple fc_array variables referenced + by one fc_ref, quit field compression. Because we don't know how to + modify it then.) */ + +bool +ipa_struct_reorg::find_fc_refs (fc_type_info *info) +{ + /* For each fc_array, follow the use chains and search for fc_refs. */ + for (auto *array : info->fc_arrays) + { + gcc_assert (array->ssa_def); + SET_CFUN (info->start_srfn); + if (!find_fc_refs_iterate (info, array, array->ssa_def, true)) + return false; + + ipa_ref *ref = NULL; + for (unsigned i = 0; array->vnode->iterate_referring (i, ref); i++) + { + /* Filter for memory loads. */ + if (ref->use != IPA_REF_LOAD) + continue; + /* Ignore assignments after start-point. */ + if (!is_stmt_before_fclose (info, ref->stmt, ref->referring)) + continue; + if (!gimple_assign_single_p (ref->stmt)) + return false; + tree rhs = gimple_assign_rhs1 (ref->stmt); + if (!operand_equal_p (rhs, array->var, COMPARE_DECL_FLAGS)) + continue; + + SET_CFUN (find_function (as_a (ref->referring))); + tree lhs = gimple_assign_lhs (ref->stmt); + if (!find_fc_refs_iterate (info, array, lhs, true)) + return false; + } + } + + return true; +} + +/* Given a fc_array ARRAY and a variable VAR referring to ARRAY, + find fc_refs iteratively follow the use chain of VAR. */ + +bool +ipa_struct_reorg::find_fc_refs_iterate (fc_type_info *info, fc_array *array, + tree var, bool loop_back) +{ + switch (TREE_CODE (var)) + { + /* 1) For SSA_NAME, iterate through use chain. */ + case SSA_NAME: + return find_fc_refs_ssa_name (info, array, var, loop_back); + + /* 2) For VAR_DECL, submit a fc_ref. */ + case VAR_DECL: + return add_fc_ref (info, array, var, NULL_TREE); + + /* 3) For MEM_REF, find fc_ref following base's def chain. */ + case MEM_REF: + return find_fc_refs_mem_ref (info, array, var); + + case COMPONENT_REF: + return find_fc_refs_component_ref (info, array, var); + + default: + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Unknown use kind, code: %s, var: ", + get_tree_code_name (TREE_CODE (var))); + print_generic_expr (dump_file, var); + fprintf (dump_file, "\n"); + } + return false; + } +} + +/* Find all fc_refs of a SSA_NAME var through its use chain. */ + +bool +ipa_struct_reorg::find_fc_refs_ssa_name (fc_type_info *info, fc_array *array, + tree var, bool loop_back) +{ + use_operand_p use_p; + imm_use_iterator iter; + FOR_EACH_IMM_USE_FAST (use_p, iter, var) + { + gimple *stmt = USE_STMT (use_p); + cgraph_node *cnode = current_function->node; + if (!is_stmt_before_fclose (info, stmt, cnode)) + { + gimple *def_stmt = SSA_NAME_DEF_STMT (var); + if (!is_stmt_before_fclose (info, def_stmt, cnode)) + continue; + + /* If a local ptr of compressed type is defined before start-point + and used after start-point, quit field compression. (Otherwise we + need to clone and version the ptr's define statement.) */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Local usage not handled: "); + print_gimple_stmt (dump_file, stmt, 0); + fprintf (dump_file, " Defined at: "); + print_gimple_stmt (dump_file, def_stmt, 0); + } + + return false; + } + + tree lhs = gimple_get_lhs (stmt); + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + /* Rule out: expr(var) = X. */ + if (walk_tree (&lhs, check_for_ssa, var, NULL) + || !fc_type_pointer_p (info, lhs)) + break; + if (!find_fc_refs_iterate (info, array, lhs, loop_back)) + return false; + break; + case GIMPLE_PHI: + { + /* Check if VAR is from back_edge. */ + bool loop_var = false; + gphi *phi = as_a (stmt); + for (unsigned i = 0; i < gimple_phi_num_args (phi); i++) + { + if (gimple_phi_arg_def (phi, i) != var) + continue; + edge e = gimple_phi_arg_edge (phi, i); + if (e->flags & EDGE_DFS_BACK) + { + loop_var = true; + break; + } + } + + if (!loop_var) + { + if (!find_fc_refs_iterate (info, array, lhs, loop_back)) + return false; + } + else if (loop_back) + { + if (!find_fc_refs_iterate (info, array, lhs, false)) + return false; + } + break; + } + case GIMPLE_DEBUG: + case GIMPLE_COND: + case GIMPLE_SWITCH: + case GIMPLE_NOP: + break; + default: + /* Cannot be sure how fc_array is used, like GIMPLE_CALL? */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("fc_array usage not handled: "); + print_gimple_stmt (dump_file, stmt, 0); + } + return false; + } + } + return true; +} + +/* Find all fc_refs of a MEM_REF var through its base's def chain. */ + +bool +ipa_struct_reorg::find_fc_refs_mem_ref (fc_type_info *info, fc_array *array, + tree var) +{ + if (!integer_zerop (TREE_OPERAND (var, 1))) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("MEM_REF offset not handled: "); + print_generic_expr (dump_file, var); + fprintf (dump_file, "\n"); + } + return false; + } + + if (!fc_type_pointer_p (info, var)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Type not compatible: "); + print_generic_expr (dump_file, TREE_TYPE (var)); + fprintf (dump_file, "\n"); + } + return false; + } + + tree base = TREE_OPERAND (var, 0); + tree ref = get_ptr_decl (base); + if (!ref) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Failed to get array decl from: "); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\n"); + } + return false; + } + + return add_fc_ref (info, array, ref, NULL_TREE); +} + +/* Find fc_refs of a COMPONENT_REF var. */ + +bool +ipa_struct_reorg::find_fc_refs_component_ref (fc_type_info *info, + fc_array *array, tree var) +{ + tree base = TREE_OPERAND (var, 0); + + if (TREE_CODE (base) == VAR_DECL) + return add_fc_ref (info, array, var, NULL_TREE); + else if (TREE_CODE (base) == MEM_REF) + base = TREE_OPERAND (base, 0); + + tree ref = get_ptr_decl (base); + if (!ref) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Failed to get array decl from: "); + print_generic_expr (dump_file, base); + fprintf (dump_file, "\n"); + } + return false; + } + + tree field = TREE_OPERAND (var, 1); + return add_fc_ref (info, array, ref, field); +} + +/* Return the top level fc_type pointer tree node. */ + +bool +ipa_struct_reorg::fc_type_pointer_p (fc_type_info *info, tree t) +{ + tree type = TREE_TYPE (t); + + return POINTER_TYPE_P (type) + && types_fc_compatible_p (TREE_TYPE (type), info->type->type); +} + +/* Add VAR as a fc_ref entry into INFO. + 1) VAR is a single pointer: fc_ref::size = NULL, fc_ref::field = NULL + 2) VAR is an array of pointers: fc_ref::size is the size of array, + fc_ref::field = NULL + 3) VAR is an array of records(e.g. struct {fc_type *p;}): + fc_ref::size is the size of array, fc_ref::field is p + */ + +bool +ipa_struct_reorg::add_fc_ref (fc_type_info *info, fc_array *array, tree var, + tree field) +{ + /* The way we're searching for fc_refs, fc_array vars will also meet the + requirements. Rule out them. */ + for (auto *d : info->fc_arrays) + if (operand_equal_p (var, d->var, COMPARE_DECL_FLAGS)) + return true; + + tree type = NULL_TREE; + tree size_expr = NULL_TREE; + + /* Rule out duplicants. */ + switch (check_duplicative_ref (info, array, var, field, type, size_expr)) + { + case check_ref_result::NEW: break; + case check_ref_result::DUPLICATIVE: return true; + case check_ref_result::ERROR: return false; + } + + if (!type) + { + type = TREE_TYPE (var); + /* Use the "real" type for void*. */ + if (VOID_POINTER_P (type)) + { + srdecl *decl = find_decl (var); + if (!decl || !decl->orig_type || !POINTER_TYPE_P (decl->orig_type)) + return false; + type = decl->orig_type; + } + } + + /* If REF is an array, get the size it is allocated with. */ + if ((!size_expr) && POINTER_TYPE_P (type) + && !types_fc_compatible_p (TREE_TYPE (type), info->type->type)) + { + gimple *stmt = NULL; + if (TREE_CODE (var) == SSA_NAME) + stmt = SSA_NAME_DEF_STMT (var); + else + stmt = find_def_stmt_before_fclose (info, var); + + if (!get_allocate_size_iterate (TREE_TYPE (type), stmt, size_expr)) + return false; + } + + fc_ref *ref = new fc_ref (var, type, array, size_expr, field); + info->fc_refs.safe_push (ref); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Add fc_ref: "); + ref->dump (dump_file); + } + + return true; +} + +/* Check if we have found another fc_ref with the same var. */ + +check_ref_result +ipa_struct_reorg::check_duplicative_ref (fc_type_info *info, fc_array *array, + tree var, tree field, + tree &type, tree &size_expr) +{ + for (auto *ref : info->fc_refs) + { + if (!operand_equal_p (var, ref->var, COMPARE_DECL_FLAGS)) + continue; + + /* The var refers to multiple fc_array. */ + if (ref->source != array) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Variable "); + print_generic_expr (dump_file, var); + fprintf (dump_file, " referring to multiple arrays: "); + print_generic_expr (dump_file, ref->source->var); + fprintf (dump_file, " and "); + print_generic_expr (dump_file, array->var); + fprintf (dump_file, "\n"); + } + return check_ref_result::ERROR; + } + + if (ref->field) + { + gcc_assert (field); + /* Different fields in an array of structures. */ + if (!operand_equal_p (field, ref->field, COMPARE_DECL_FLAGS)) + { + type = ref->orig_type ? ref->orig_type : TREE_TYPE (var); + size_expr = ref->size; + continue; + } + } + + return check_ref_result::DUPLICATIVE; + } + + return check_ref_result::NEW; +} + +/* Find the single defination stmt before start-point for a var. */ + +gimple * +ipa_struct_reorg::find_def_stmt_before_fclose (fc_type_info *info, tree var) +{ + tree base = TREE_CODE (var) == COMPONENT_REF ? TREE_OPERAND (var, 0) : var; + if (TREE_CODE (base) != VAR_DECL) + return NULL; + + varpool_node *vnode = varpool_node::get (base); + /* Local array is not handled yet. */ + if (!vnode) + return NULL; + + gimple *def_stmt = NULL; + ipa_ref *ref = NULL; + for (unsigned i = 0; vnode->iterate_referring (i, ref); i++) + { + if (ref->use != IPA_REF_STORE) + continue; + + gimple *stmt = ref->stmt; + tree lhs = gimple_get_lhs (stmt); + if (!operand_equal_p (lhs, var, COMPARE_DECL_FLAGS) + || !is_stmt_before_fclose (info, stmt, ref->referring)) + continue; + + if (gimple_assign_single_p (stmt) + && integer_zerop (gimple_assign_rhs1 (stmt))) + continue; + + if (def_stmt) + { + FC_DUMP_MSG ("Multiple definations before start-point?\n"); + return NULL; + } + + def_stmt = stmt; + } + + return def_stmt; +} + +/* VAR is an ssa_name defined by some array + offset. + 1) For global variables, returns declaration of the array. + 2) For arrays locally allocated with recogized functions, returns the + ssa_name it is assigned with. + 3) Return NULL_TREE if cannot decide. */ + +tree +ipa_struct_reorg::get_ptr_decl (tree var) +{ + if (TREE_CODE (var) != SSA_NAME) + return NULL_TREE; + + gimple *stmt = SSA_NAME_DEF_STMT (var); + tree var_type = TREE_TYPE (var); + + if (gimple_code (stmt) == GIMPLE_ASSIGN) + { + gassign *assign = as_a (stmt); + switch (gimple_assign_rhs_class (assign)) + { + case GIMPLE_BINARY_RHS: + { + if (gimple_assign_rhs_code (assign) != POINTER_PLUS_EXPR) + return NULL_TREE; + tree lhs = gimple_assign_rhs1 (assign); + if (types_fc_compatible_p (TREE_TYPE (lhs), var_type) + || VOID_POINTER_P (TREE_TYPE (lhs))) + return get_ptr_decl (lhs); + return NULL_TREE; + } + + case GIMPLE_UNARY_RHS: + case GIMPLE_SINGLE_RHS: + { + tree rhs = gimple_assign_rhs1 (stmt); + if (TREE_CODE (rhs) == SSA_NAME) + return get_ptr_decl (rhs); + else if (TREE_CODE (rhs) == VAR_DECL) + return rhs; + else if (TREE_CODE (rhs) == COMPONENT_REF) + { + tree base = TREE_OPERAND (rhs, 0); + return DECL_P (base) ? rhs : NULL_TREE; + } + else + return NULL_TREE; + } + default: + return NULL_TREE; + } + } + else if (gimple_code (stmt) == GIMPLE_CALL) + return handled_allocation_stmt (stmt) ? gimple_get_lhs (stmt) : NULL_TREE; + else + return NULL_TREE; + + /* TODO: GIMPLE_PHI can be supported (not affecting correctness). */ +} + +/* Search info->input_var backward using def/use chain until finding one of + the arrays we have found in find_fc_arrays. */ + +bool +ipa_struct_reorg::check_fc_array_uses (fc_type_info *info) +{ + hash_set visited; + auto_vec worklist; + + visited.add (info->input_var); + worklist.safe_push (info->input_var); + + while (!worklist.is_empty ()) + { + tree t = worklist.pop (); + tree_code code = TREE_CODE (t); + if (code != SSA_NAME && code != VAR_DECL) + continue; + + for (auto *array : info->fc_arrays) + if (t == array->ssa_def || t == array->var) + return true; + + /* If we reach a global variable, it must match a fc_array. */ + if (code == VAR_DECL) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (t); + if (gimple_code (stmt) == GIMPLE_PHI) + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); ++i) + { + tree arg = gimple_phi_arg_def (stmt, i); + if (!visited.add (arg)) + worklist.safe_push (arg); + } + } + else if (gimple_assign_single_p (stmt) + || gimple_assign_rhs_code_p (stmt, POINTER_PLUS_EXPR)) + { + tree rhs = gimple_assign_rhs1 (stmt); + if (!visited.add (rhs)) + worklist.safe_push (rhs); + } + } + + return false; +} + +/* Calculate the reference count of all fc fields. */ + +void +ipa_struct_reorg::calc_fc_ref_count (fc_type_info *info) +{ + for (auto *access : info->type->accesses) + { + if (!access->field) + continue; + + fc_field *fc_f = find_fc_field (info->dynamic_fc_fields, + access->field->fielddecl); + if (fc_f) + fc_f->ref_cnt++; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Reference count:\n"); + for (auto *fc_f : info->dynamic_fc_fields) + { + print_generic_expr (dump_file, fc_f->field); + fprintf (dump_file, " : %d\n", fc_f->ref_cnt); + } + } +} + +/* For dynamic fields, we heuristically change data type. */ + +bool +ipa_struct_reorg::compress_fields_dynamic (fc_type_info *info) +{ + const std::map precision_map{ + {64, 16}, {32, 16}, {16, 8} + }; + + for (auto *fc_f : info->dynamic_fc_fields) + { + tree old_type = TREE_TYPE (fc_f->field); + bool is_unsigned = TYPE_UNSIGNED (old_type); + gcc_assert (TREE_CODE (old_type) == INTEGER_TYPE); + + auto iter = precision_map.find (TYPE_PRECISION (old_type)); + if (iter == precision_map.cend ()) + return false; + + fc_f->new_type = get_integer_type_node (iter->second, is_unsigned); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Change the type of "); + print_generic_expr (dump_file, fc_f->field); + fprintf (dump_file, " from (prec=%d) to ", TYPE_PRECISION (old_type)); + print_generic_expr (dump_file, fc_f->new_type); + fprintf (dump_file, "(prec=%d)\n", TYPE_PRECISION (fc_f->new_type)); + } + + info->record_cond (fc_f); + fc_f->cond->new_type = fc_f->new_type; + } + + return true; +} + +/* Tune the upper boundary for dynamic fields. The data field may be + assigned a constant that is larger than the maximum value. For this + case, we can reserve a special value for it, and then we can + compress/decompress it by creating a map between this reserved value + and the real big value. In the meantime, we will have to reduce the + upper boundary for this specific field. */ + +bool +ipa_struct_reorg::calc_dynamic_boundary (fc_type_info *info) +{ + /* Initialize the low_bound and high_bound. */ + for (auto *cond : info->fc_conds) + { + tree ssa_type = TREE_TYPE (cond->fields[0]->input_ssa); + + /* Low bound is always zero. */ + cond->low_bound = fold_convert (ssa_type, integer_zero_node); + + /* High bound is the max value of the type. */ + unsigned bits = cond->bits ? cond->bits + : TYPE_PRECISION (cond->new_type); + unsigned max_value = wi::max_value (bits, UNSIGNED).to_uhwi (); + cond->high_bound = build_int_cst (ssa_type, max_value); + + auto_vec special_values; + /* Calculate upper bound. */ + for (auto *access : info->type->accesses) + { + gimple *stmt = access->stmt; + if (!gimple_assign_single_p (stmt)) + continue; + + /* Skip if it is not an assignment to fc field. */ + if (!fc_cond_field_p (gimple_assign_lhs (stmt), cond)) + continue; + + /* Skip if it is loaded from a fc field. */ + tree rhs = gimple_assign_rhs1 (stmt); + if (fc_field_load_p (rhs, cond)) + continue; + + /* Skip if it is from input_ssa. */ + if (fc_input_ssa_p (rhs, cond)) + continue; + + /* Make sure the assignment is a constant. If possible, we + try to find all possible costants by peephole. */ + HOST_WIDE_INT value; + if (fc_peephole_const_p (rhs, value)) + { + special_values.safe_push (value); + cond->field_class->closure.write_special_rhs.put (rhs, value); + continue; + } + } + + /* Execute multiple rounds cause we didn't sort the special_values. */ + while (true) + { + unsigned size = cond->special_values.length (); + for (auto value : special_values) + update_high_bound (cond, value); + if (size == cond->special_values.length ()) + break; + } + } + + return true; +} + +/* Return true if the VAR is a mem reference of fc_field in the fc_cond. */ + +bool +ipa_struct_reorg::fc_cond_field_p (tree var, const fc_cond *cond) +{ + if (TREE_CODE (var) != COMPONENT_REF) + return false; + + /* Find the stmt assigning to the fc field. */ + tree field = TREE_OPERAND (var, 1); + return find_fc_field (cond->fields, field); +} + +/* Return true if var is one of cond's input_ssa. */ + +bool +ipa_struct_reorg::fc_input_ssa_p (tree var, const fc_cond *cond) +{ + if (TREE_CODE (var) != SSA_NAME) + return false; + + for (auto *fc_f : cond->fields) + if (fc_f->input_ssa == var) + return true; + + return false; +} + +/* Return true the VAR is loaded from another fc field. */ + +bool +ipa_struct_reorg::fc_field_load_p (tree var, const fc_cond *cond) +{ + if (TREE_CODE (var) != SSA_NAME) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (var); + return gimple_assign_load_p (stmt) + && fc_cond_field_p (gimple_assign_rhs1 (stmt), cond); +} + +/* Reduce the high_bound of COND by 1, if the value is larger + than the high_bound. */ + +void +ipa_struct_reorg::update_high_bound (fc_cond *cond, HOST_WIDE_INT value) +{ + HOST_WIDE_INT high_bound = tree_to_uhwi (cond->high_bound); + if (value >= 0 && value <= high_bound) + return; + + if (cond->special_values.contains (value)) + return; + + high_bound--; + cond->high_bound = build_int_cst (TREE_TYPE (cond->high_bound), high_bound); + cond->special_values.safe_push (value); + if (dump_file && (dump_flags & TDF_DETAILS)) + FC_DUMP_MSG ("Found special value %ld, and reduce high_bound to 0x%lx\n", + value, high_bound); +} + +/* Check all data in fc_cond refer to a closure. */ +bool +ipa_struct_reorg::check_closure (fc_type_info *info) +{ + for (auto *cond : info->fc_conds) + { + if (!check_closure (info, cond)) + { + FC_DUMP_MSG ("Fail checking closure\n"); + return false; + } + } + + return true; +} + +/* All write stmts could be + (1) unchange, i.e. optimize away compress/decompress + (2) change, i.e. use unoptimized compress + + For case (2), we may have the scenario like below, + + B = A->field; + ... = B; + C->field = B; + + We still can prove C->field is from A->field, so they are + in a closure, but we must decompress A->field and compress + C->field, because B may be used outside the clsoure, for + which we don't care about. */ + +bool +ipa_struct_reorg::check_closure (fc_type_info *info, fc_cond *cond) +{ + fc_field_class *field_class = cond->field_class; + + for (auto *access : info->type->accesses) + { + if (!access->write_field_p () + || access->field->field_class != field_class + || !fc_cond_field_p (access->expr, cond)) + continue; + + SET_CFUN (access->function); + + gimple *stmt = access->stmt; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Check closure: "); + print_gimple_stmt (dump_file, stmt, 0); + } + + /* Skip if we have already analyzed this stmt. */ + if (field_class->closure.write_unchange_p (stmt) + || field_class->closure.write_change_p (stmt)) + continue; + + tree rhs = gimple_assign_rhs1 (stmt); + HOST_WIDE_INT *value = field_class->closure.write_special_rhs.get (rhs); + if (fc_input_ssa_p (rhs, cond) + || (value && cond->special_values.contains (*value))) + { + /* Case (2) */ + field_class->closure.add_write_change (stmt); + FC_DUMP_MSG ("Need to change.\n"); + continue; + } + if (value && !cond->special_values.contains (*value)) + { + /* Case (2) */ + field_class->closure.add_write_unchange (stmt); + FC_DUMP_MSG ("No need to change.\n"); + continue; + } + + if (!gimple_assign_single_p (stmt) + || TREE_CODE (rhs) != SSA_NAME) + return false; + + gimple *def_stmt = SSA_NAME_DEF_STMT (rhs); + if (gimple_assign_single_p (def_stmt)) + { + /* Check if RHS is from the the same fc class. */ + srtype *type = NULL; + srfield *field = NULL; + tree base = NULL_TREE; + if (get_base_type (gimple_assign_rhs1 (def_stmt), base, type, field) + && field->field_class == field_class) + { + if (write_field_class_only_p (info, field_class, rhs)) + { + /* Case (1). */ + field_class->closure.add_write_unchange (stmt); + field_class->closure.add_read_unchange (def_stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("No need to change: "); + print_gimple_stmt (dump_file, def_stmt, 0); + } + } + else + { + /* Case (2). */ + field_class->closure.add_write_change (stmt); + FC_DUMP_MSG ("Need to change. \n"); + } + + continue; + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Check closure fail: "); + print_gimple_stmt (dump_file, stmt, 0); + } + + return false; + } + + collect_closure_read_change (info, field_class); + + return true; +} + +/* Return true if all stmts using ssa_def are to write a fc field. */ + +bool +ipa_struct_reorg::write_field_class_only_p (fc_type_info *info, + fc_field_class *field_class, + tree ssa_def) +{ + imm_use_iterator imm_iter; + gimple *stmt; + FOR_EACH_IMM_USE_STMT (stmt, imm_iter, ssa_def) + { + if (gimple_code (stmt) == GIMPLE_DEBUG) + continue; + + /* We don't know if it is PHI. */ + if (!is_gimple_assign (stmt)) + return false; + + srtype *type = NULL; + srfield *field = NULL; + tree base = NULL_TREE; + if (!get_base_type (gimple_assign_lhs (stmt), base, type, field) + || type != info->type + || !field_class->srfields.contains (field)) + return false; + } + + return true; +} + +/* Collect read_change. */ + +void +ipa_struct_reorg::collect_closure_read_change (fc_type_info *info, + fc_field_class *field_class) +{ + for (auto *access : info->type->accesses) + { + if (!access->read_field_p () + || access->field->field_class != field_class) + continue; + + /* Skip statement that has been marked as unchanged. */ + if (field_class->closure.read_unchange_p (access->stmt)) + continue; + + field_class->closure.add_read_change (access->stmt); + } +} + +unsigned +ipa_struct_reorg::execute_dynamic_field_compression () +{ + if (current_fc_level != fc_level::DYNAMIC) + return 0; + + current_layout_opt_level = STRUCT_REORDER_FIELDS; + replace_type_map.empty (); + record_accesses (); + prune_escaped_types (); + check_and_prune_struct_for_field_compression (); + + return dynamic_fc_rewrite (); +} + +unsigned +ipa_struct_reorg::dynamic_fc_rewrite () +{ + if (!create_dynamic_fc_newtypes ()) + { + FC_DUMP_MSG ("Failed to create newtypes for dfc\n"); + return 0; + } + + for (auto *info : fc_infos) + { + if (!info->dynamic_fc_p) + continue; + create_dynamic_fc_convert_fn (info); + clone_dynamic_fc_path (info); + record_dfc_path_info (info); + if (flag_ipa_struct_dfc_shadow) + rewrite_dynamic_shadow_fields (info); + rewrite_dynamic_fc_path (); + add_dynamic_checking (info); + } + + return TODO_verify_all; +} + +bool +ipa_struct_reorg::create_dynamic_fc_newtypes () +{ + bool created = false; + for (auto *info : fc_infos) + { + if (!info->dynamic_fc_p) + continue; + + create_dynamic_fc_variant (info); + if (info->type->create_new_type ()) + created = true; + else + info->dynamic_fc_p = false; + } + + if (!created) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "=========== all created newtypes ===========\n\n"); + dump_newtypes (dump_file); + } + + return true; +} + +/* Create a new fc_variant for the given fc_type in terms of fc_conds. */ + +void +ipa_struct_reorg::create_dynamic_fc_variant (fc_type_info *info) +{ + create_global_var_dfc_path (info); + info->variant = new fc_variant (); +} + +/* Create a global variable to identify the current dynamic path. */ + +void +ipa_struct_reorg::create_global_var_dfc_path (fc_type_info *info) +{ + tree name = get_identifier ("dfc.path"); + tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, name, + boolean_type_node); + + TREE_PUBLIC (var) = 1; + TREE_STATIC (var) = 1; + DECL_IGNORED_P (var) = 1; + DECL_ARTIFICIAL (var) = 1; + DECL_INITIAL (var) = boolean_false_node; + SET_DECL_ASSEMBLER_NAME (var, name); + + varpool_node::finalize_decl (var); + info->dfc_path = var; +} + +/* Insert compress/decompress functions. */ + +void +ipa_struct_reorg::create_dynamic_fc_convert_fn (fc_type_info *info) +{ + for (unsigned i = 0; i < info->fc_conds.length (); i++) + { + fc_cond *cond = info->fc_conds[i]; + cond->compress_fn = create_convert_fn (cond, i, false); + cond->decompress_fn = create_convert_fn (cond, i, true); + } +} + +/* Create function to further compress fields with special_values. + - DECOMP == 0: create function to compress the field. + - DECOMP != 0: create function to decompress the field. + IDX is an unique number for function name. + Return declaration of created function. */ + +tree +ipa_struct_reorg::create_convert_fn (fc_cond *fcond, unsigned idx, + bool decompress) +{ + if (fcond->special_values.is_empty ()) + return NULL_TREE; + + push_cfun (NULL); + + /* Init declarations. */ + char fn_name[64]; + const char *name = decompress ? "dfc.decompress." : "dfc.compress."; + sprintf (fn_name, "%s%d", name, idx); + + tree arg_type = decompress ? fcond->new_type : fcond->old_type; + tree return_type = decompress ? fcond->old_type : fcond->new_type; + tree fn_decl = create_new_fn_decl (fn_name, 1, &arg_type, return_type); + + basic_block return_bb = init_lowered_empty_function ( + fn_decl, true, profile_count::uninitialized ()); + calculate_dominance_info (CDI_DOMINATORS); + + split_edge (single_pred_edge (return_bb)); + tree result = make_ssa_name (return_type); + create_phi_node (result, return_bb); + + /* Create compress/decompress function body. */ + edge exit_e = create_normal_part (fcond); + create_conversion_part (fcond, exit_e, decompress); + + /* Return stmt. */ + update_stmt (gsi_start_phis (return_bb).phi ()); + gimple *return_stmt = gimple_build_return (result); + gimple_stmt_iterator gsi = gsi_last_bb (return_bb); + gsi_insert_after (&gsi, return_stmt, GSI_NEW_STMT); + + free_dominance_info (CDI_DOMINATORS); + update_ssa (TODO_update_ssa); + + cgraph_node::create (fn_decl); + cgraph_node::add_new_function (fn_decl, true); + cgraph_edge::rebuild_edges (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Create %s field function:\n", + decompress ? "decompress" : "compress"); + dump_function_to_file (cfun->decl, dump_file, dump_flags); + } + + pop_cfun (); + + return fn_decl; +} + +/* Insert code for values in the bound: + if (arg <= high_bound && arg >= low_bound) + return arg; + + Return the exit_edge of the if region, whose dest is the return block. + */ + +edge +ipa_struct_reorg::create_normal_part (fc_cond *fcond) +{ + edge true_e = NULL; + edge false_e = NULL; + tree arg = DECL_ARGUMENTS (cfun->decl); + + /* Create 'arg <= high_bound'. */ + basic_block bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); + tree tmp_arg = fold_convert (TREE_TYPE (fcond->high_bound), arg); + tree cond = build2 (LE_EXPR, boolean_type_node, tmp_arg, fcond->high_bound); + edge exit_e = create_empty_if_region_on_edge (single_succ_edge (bb), cond); + extract_true_false_edges_from_block (single_succ (bb), &true_e, &false_e); + + /* Create 'arg >= low_bound'. */ + bb = true_e->dest; + tmp_arg = fold_convert (TREE_TYPE (fcond->low_bound), arg); + cond = build2 (GE_EXPR, boolean_type_node, tmp_arg, fcond->low_bound); + create_empty_if_region_on_edge (single_succ_edge (bb), cond); + extract_true_false_edges_from_block (single_succ (bb), &true_e, &false_e); + + /* Return the original value on true_edge. */ + bb = true_e->dest; + tree return_type = TREE_TYPE (TREE_TYPE (cfun->decl)); + tree val = make_ssa_name (return_type); + gimple_stmt_iterator gsi = gsi_last_bb (bb); + APPEND_GASSIGN_1 (gsi, val, NOP_EXPR, arg); + + basic_block return_bb = single_pred (EXIT_BLOCK_PTR_FOR_FN (cfun)); + redirect_edge_succ (single_succ_edge (bb), return_bb); + gphi *phi = gsi_start_phis (return_bb).phi (); + add_phi_arg (phi, val, single_succ_edge (bb), UNKNOWN_LOCATION); + + return exit_e; +} + +/* Insert conversion code to compress/decompress special values. */ + +void +ipa_struct_reorg::create_conversion_part (fc_cond *fcond, edge e, bool decomp) +{ + edge exit_e = e; + basic_block return_bb = single_pred (EXIT_BLOCK_PTR_FOR_FN (cfun)); + HOST_WIDE_INT reserved_value = tree_to_uhwi (fcond->high_bound) + 1; + for (unsigned i = 0; i < fcond->special_values.length (); i++) + { + basic_block bb = exit_e->src; + tree special_cst = build_int_cst (signed_type_for (fcond->old_type), + fcond->special_values[i]); + tree compressed_cst = build_int_cst (fcond->new_type, reserved_value); + + if (i == fcond->special_values.length () - 1) + { + /* Omit condition check for the last special value. */ + redirect_edge_and_branch (single_succ_edge (bb), return_bb); + gphi *phi = gsi_start_phis (return_bb).phi (); + add_phi_arg (phi, decomp ? special_cst : compressed_cst, + single_succ_edge (bb), UNKNOWN_LOCATION); + } + else + { + tree arg = DECL_ARGUMENTS (cfun->decl); + tree cond = build2 (EQ_EXPR, boolean_type_node, arg, + decomp ? compressed_cst : special_cst); + exit_e = create_empty_if_region_on_edge (exit_e, cond); + edge true_e = NULL; + edge false_e = NULL; + extract_true_false_edges_from_block (single_succ (bb), &true_e, + &false_e); + redirect_edge_and_branch (single_succ_edge (true_e->dest), + return_bb); + gphi *phi = gsi_start_phis (return_bb).phi (); + add_phi_arg (phi, decomp ? special_cst : compressed_cst, + single_succ_edge (true_e->dest), UNKNOWN_LOCATION); + reserved_value++; + } + } +} + +void +ipa_struct_reorg::clone_dynamic_fc_path (fc_type_info *info) +{ + SET_DUMP_FILE (NULL, TDF_NONE); + for (auto *srfn : functions) + { + SET_CFUN (srfn); + + if (srfn->partial_clone_p ()) + clone_partial_func (info, srfn); + else + clone_whole_func (srfn); + } +} + +/* start_bb: if (dfc.path) + / \ + false true + / \ + origin-bbs clone-bbs + */ +void +ipa_struct_reorg::clone_partial_func (fc_type_info *info, srfunction *srfn) +{ + calculate_dominance_info (CDI_DOMINATORS); + + fc_path_info &path = srfn->fc_path; + auto_vec &reach_bbs = path.reach_bbs; + gimple *start_stmt = path.start_stmt; + basic_block fclose_bb = reach_bbs[0]; + + gcc_assert (fclose_bb == gimple_bb (start_stmt)); + edge e = split_block (fclose_bb, start_stmt); + reach_bbs[0] = e->dest; + + unsigned n = reach_bbs.length (); + basic_block *origin_bbs = new basic_block[n]; + for (unsigned i = 0; i < reach_bbs.length (); i++) + origin_bbs[i] = reach_bbs[i]; + + /* 1. Clone blocks reachable from start point. */ + initialize_original_copy_tables (); + basic_block *cloned_bbs = new basic_block[n]; + copy_bbs (origin_bbs, n, cloned_bbs, NULL, 0, NULL, fclose_bb->loop_father, + fclose_bb, true); + delete[] origin_bbs; + + /* Add phis for edges from copied bbs. */ + add_phi_args_after_copy (cloned_bbs, n, NULL); + free_original_copy_tables (); + + path.cloned_bbs.reserve (n); + for (unsigned i = 0; i < n; i++) + path.cloned_bbs.safe_push (cloned_bbs[i]); + delete[] cloned_bbs; + + /* 2. Add if-else on dfc.path. */ + basic_block checking_bb = split_edge (e); + gimple_stmt_iterator gsi = gsi_last_bb (checking_bb); + tree dfc_path_ssa = make_ssa_name (info->dfc_path); + gassign *assign = gimple_build_assign (dfc_path_ssa, info->dfc_path); + gsi_insert_after (&gsi, assign, GSI_NEW_STMT); + gcond *dfc_path_cond = gimple_build_cond_from_tree (dfc_path_ssa, + NULL_TREE, NULL_TREE); + gsi_insert_after (&gsi, dfc_path_cond, GSI_NEW_STMT); + + e = single_succ_edge (checking_bb); + e->flags = (e->flags & ~EDGE_FALLTHRU) | EDGE_FALSE_VALUE; + + make_edge (checking_bb, path.cloned_bbs[0], EDGE_TRUE_VALUE); + + /* Necessary for visiting call stmts. */ + cgraph_edge::rebuild_edges (); + free_dominance_info (CDI_DOMINATORS); + + if (loops_state_satisfies_p (LOOPS_NEED_FIXUP)) + { + calculate_dominance_info (CDI_DOMINATORS); + fix_loop_structure (NULL); + } + + update_ssa (TODO_update_ssa); +} + +void +ipa_struct_reorg::clone_whole_func (srfunction *srfn) +{ + cgraph_node *new_node; + cgraph_node *node = srfn->node; + + statistics_counter_event (NULL, "Create new function", 1); + new_node = node->create_version_clone_with_body (vNULL, NULL, NULL, NULL, + NULL, "dfc"); + new_node->can_change_signature = node->can_change_signature; + new_node->make_local (); + + srfunction *new_srfn = new srfunction (new_node); + if (srfn->is_safe_func) + { + safe_functions.add (new_srfn->node); + new_srfn->is_safe_func = true; + } + + srfn->fc_path.cloned_func = new_srfn; +} + +/* Rewrite dynamic shadow fields in cloned path. */ + +void +ipa_struct_reorg::rewrite_dynamic_shadow_fields (fc_type_info *info) +{ + if (info->dynamic_shadow_fields.is_empty ()) + return; + + for (auto *access : info->type->accesses) + { + srfield *srf = access->field; + if (!srf || !srf->fc_f || !srf->fc_f->original) + continue; + + /* Skip statements in original path. */ + srfunction *srfn = access->function; + gimple *stmt = access->stmt; + if (srfn->fc_path.cloned_func + || (srfn->partial_clone_p () + && !srfn->fc_path.cloned_bbs.contains (gimple_bb (stmt)))) + continue; + + SET_CFUN (srfn); + + if (access->write_p ()) + { + /* Remove stmt by replacing lhs by a dummy ssa. */ + tree lhs = gimple_assign_lhs (stmt); + tree dummy_ssa = make_ssa_name (TREE_TYPE (lhs)); + gimple_assign_set_lhs (stmt, dummy_ssa); + update_stmt (stmt); + } + else if (access->read_p ()) + modify_shadow_read (stmt, access->index, srf->fc_f, access->base); + else + gcc_unreachable (); + } +} + +/* Rewrite functions either partially or wholely. */ + +void +ipa_struct_reorg::rewrite_dynamic_fc_path () +{ + for (auto *srfn : functions) + { + if (srfn->partial_clone_p ()) + { + SET_CFUN (srfn); + + /* 2.1 rewrite the original function for each path. */ + rewrite_partial_func (srfn); + clean_func_after_rewrite (srfn); + } + else + { + /* 2.2 rewrite the cloned function for each path. */ + srfunction *cloned_func = srfn->fc_path.cloned_func; + SET_CFUN (cloned_func); + + rewrite_whole_func (cloned_func); + clean_func_after_rewrite (cloned_func); + } + } +} + +void +ipa_struct_reorg::record_dfc_path_info (fc_type_info *info) +{ + /* 1. record accesse info for cloned stmts. */ + for (auto *srfn : functions) + { + SET_DUMP_FILE (NULL, TDF_NONE); + if (srfn->partial_clone_p ()) + { + record_function (srfn->node, srfn); + } + else + { + srfunction *cloned_srfn = srfn->fc_path.cloned_func; + record_function (cloned_srfn->node, cloned_srfn); + prune_function (cloned_srfn); + } + } + + prune_globals (); + gcc_assert (!info->type->has_escaped ()); + + /* 2. collect closure info for cloned paths. */ + collect_closure_info_dynamic (info); +} + +/* Collect the closure info for all dynamic-fc cloned paths. */ + +void +ipa_struct_reorg::collect_closure_info_dynamic (fc_type_info *info) +{ + for (auto *cond : info->fc_conds) + { + fc_field_class *field_class = cond->field_class; + for (auto *srfn : functions) + { + if (srfn->partial_clone_p ()) + collect_closure_info_partial (srfn, &field_class->closure); + else + collect_closure_info_whole (srfn, &field_class->closure); + } + } +} + +/* Collect closure info for partially cloned function SRFN in dynamic fc. */ + +void +ipa_struct_reorg::collect_closure_info_partial (srfunction *srfn, + fc_closure *cinfo) +{ + closure_helper helper (cinfo); + + for (auto *bb : srfn->fc_path.reach_bbs) + helper.record_origin_closure (bb); + + helper.reset_uid (); + + for (unsigned i = 0; i < srfn->fc_path.reach_bbs.length (); i++) + helper.add_cloned_closure (srfn->fc_path.cloned_bbs[i]); +} + +/* Collect closure info for wholely cloned function SRFN in dfc. */ + +void +ipa_struct_reorg::collect_closure_info_whole (srfunction *srfn, + fc_closure *cinfo) +{ + closure_helper helper (cinfo); + + basic_block bb = NULL; + FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (srfn->node->decl)) + helper.record_origin_closure (bb); + + helper.reset_uid (); + + srfunction *cloned_srfn = srfn->fc_path.cloned_func; + FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (cloned_srfn->node->decl)) + helper.add_cloned_closure (bb); +} + +void +ipa_struct_reorg::rewrite_partial_func (srfunction *srfn) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Before rewrite: %s\n", srfn->node->name ()); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + FC_DUMP_MSG ("Start to rewrite: %s\n", srfn->node->name ()); + fprintf (dump_file, "\n\n"); + } + + srfn->create_new_decls (); + + /* Rewrite each related stmts in the current path. */ + for (unsigned i = 0; i < srfn->fc_path.reach_bbs.length (); i++) + rewrite_block (srfn->fc_path.cloned_bbs[i]); +} + +void +ipa_struct_reorg::rewrite_whole_func (srfunction *srfn) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Before rewrite: %s\n", srfn->node->name ()); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + FC_DUMP_MSG ("Start to rewrite: %s\n", srfn->node->name ()); + } + + create_new_args (srfn->node); + srfn->create_new_decls (); + + basic_block bb = NULL; + FOR_EACH_BB_FN (bb, cfun) + rewrite_block (bb); +} + +void +ipa_struct_reorg::clean_func_after_rewrite (srfunction *srfn) +{ + if (!srfn->partial_clone_p ()) + for (auto *srd : srfn->args) + release_srdecl_ssa_name (srd); + + for (auto *srd : srfn->decls) + release_srdecl_ssa_name (srd); + + { + SET_DUMP_FILE (NULL, TDF_NONE); + update_ssa (TODO_update_ssa_only_virtuals); + + unsigned i; + tree ssa_name; + FOR_EACH_SSA_NAME (i, ssa_name, cfun) + { + if (SSA_NAME_IN_FREE_LIST (ssa_name)) + continue; + + gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); + + if (!stmt || (!SSA_NAME_IS_DEFAULT_DEF (ssa_name) + && !gimple_bb (stmt))) + release_ssa_name (ssa_name); + } + + if (flag_tree_pta) + compute_may_aliases (); + + remove_unused_locals (); + cgraph_edge::rebuild_edges (); + free_dominance_info (CDI_DOMINATORS); + } + + if (dump_file) + { + FC_DUMP_MSG ("After rewrite: %s\n", srfn->node->name ()); + dump_function_to_file (current_function_decl, dump_file, + dump_flags | TDF_VOPS); + fprintf (dump_file, "\n\n"); + } +} + +void +ipa_struct_reorg::dynamic_fc_rewrite_assign (gimple *stmt, tree rhs, + tree &newlhs, tree &newrhs) +{ + fc_closure *closure = cur_srfd->get_closure (); + if (closure->write_change_p (stmt)) + { + /* For a write stmt _0->fld = rhs, should only rewrite lhs. */ + gcc_assert (newrhs == NULL_TREE); + tree compress_fn = cur_srfd->fc_f->cond->compress_fn; + if (compress_fn) + newrhs = closure->convert_rhs (rhs, compress_fn); + } + else if (closure->read_change_p (stmt)) + { + /* For a read stmt lhs = _0->fld, should only rewrite rhs. */ + gcc_assert (newlhs == NULL_TREE); + tree decompress_fn = cur_srfd->fc_f->cond->decompress_fn; + if (decompress_fn) + newrhs = closure->convert_rhs (newrhs, decompress_fn); + } + else if (!closure->unchange_p (stmt)) + gcc_unreachable (); +} + +/* Add code for dynamic checking and data compressing. */ + +void +ipa_struct_reorg::add_dynamic_checking (fc_type_info *info) +{ + basic_block bb = gimple_bb (info->fclose_stmt); + gcc_assert (single_succ_p (bb)); + + SET_CFUN (info->start_srfn); + + insert_code_calc_dfc_path (info); + insert_code_compress_data (info, single_succ_edge (bb)); + + SET_DUMP_FILE (NULL, TDF_NONE); + cgraph_edge::rebuild_edges (); + update_ssa (TODO_update_ssa_only_virtuals); +} + +/* Insert dynamic checking code to calculate info->dfc_path. */ + +void +ipa_struct_reorg::insert_code_calc_dfc_path (fc_type_info *info) +{ + insert_code_calc_max_min_val (info); + gimple_stmt_iterator gsi = gsi_for_stmt (info->fclose_stmt); + tree dfc_path = insert_code_calc_cond (info, &gsi); + insert_code_check_init_const (info, &gsi, dfc_path); + + /* Store dfc_path to global var. */ + gimple *dfc_path_stmt = gimple_build_assign (info->dfc_path, dfc_path); + gsi_insert_after (&gsi, dfc_path_stmt, GSI_NEW_STMT); + + basic_block bb = gimple_bb (info->fclose_stmt); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\n"); + FC_DUMP_MSG ("Insert code to calculate dfc.path\n"); + dump_bb (dump_file, bb, 0, TDF_DETAILS); + } +} + +/* Insert code to calculate min and max after input_ssa inside the loop. */ + +void +ipa_struct_reorg::insert_code_calc_max_min_val (fc_type_info *info) +{ + basic_block bb = gimple_bb (info->input_stmt); + class loop *loop = bb->loop_father; + edge latch_edge = loop_latch_edge (loop); + for (unsigned i = 0; i < info->fc_conds.length (); i++) + { + fc_cond *cond = info->fc_conds[i]; + /* Use the old type for min and max value, as they will be used to + compare with the input ssa, which is with old type. */ + tree ssa_type = TREE_TYPE (cond->fields[0]->input_ssa); + char *min_name = append_suffix ("dfc.min_cond.", i); + char *max_name = append_suffix ("dfc.max_cond.", i); + cond->min_val = make_temp_ssa_name (ssa_type, NULL, min_name); + cond->max_val = make_temp_ssa_name (ssa_type, NULL, max_name); + + /* Insert phi for min and max in loop header. */ + gphi *min_phi = create_phi_node (cond->min_val, loop->header); + gphi *max_phi = create_phi_node (cond->max_val, loop->header); + + /* For the input_ssa of each fc fields, we calculate min and max. + Assume all of the fc_fields have been sorted in terms of the + position of input_ssa. We should always access an input_ssa in + forward direction. This way, all fields' input will be used to + update min_val and max_val in order. */ + tree min_val = cond->min_val; + tree max_val = cond->max_val; + hash_set input_ssa; + for (auto *fc_f : cond->fields) + { + /* We handle the same input_ssa only once. */ + if (input_ssa.contains (fc_f->input_ssa)) + continue; + + input_ssa.add (fc_f->input_ssa); + gcc_assert (TREE_TYPE (fc_f->input_ssa) == ssa_type); + + /* Insert new stmt immediately after input_ssa. */ + gimple *def_stmt = SSA_NAME_DEF_STMT (fc_f->input_ssa); + gimple_stmt_iterator input_gsi = gsi_for_stmt (def_stmt); + bb = gimple_bb (def_stmt); + + /* min = (input < min) ? input : min_phi */ + tree min_cmp = fold_build2 (LT_EXPR, boolean_type_node, + fc_f->input_ssa, min_val); + tree input_min_rhs = build_cond_expr (min_cmp, fc_f->input_ssa, + min_val); + min_val = make_temp_ssa_name (ssa_type, NULL, min_name); + gimple *min_stmt = gimple_build_assign (min_val, input_min_rhs); + gsi_insert_after (&input_gsi, min_stmt, GSI_NEW_STMT); + + /* max = (input < max) ? input : max_phi */ + tree max_cmp = fold_build2 (GT_EXPR, boolean_type_node, + fc_f->input_ssa, max_val); + tree input_max_rhs = build_cond_expr (max_cmp, fc_f->input_ssa, + max_val); + max_val = make_temp_ssa_name (ssa_type, NULL, max_name); + gimple *max_stmt = gimple_build_assign (max_val, input_max_rhs); + gsi_insert_after (&input_gsi, max_stmt, GSI_NEW_STMT); + } + free (min_name); + free (max_name); + + /* Add input_min_rhs and input_max_rhs phis. */ + add_phi_arg (min_phi, min_val, latch_edge, UNKNOWN_LOCATION); + add_phi_arg (max_phi, max_val, latch_edge, UNKNOWN_LOCATION); + edge entry_edge = NULL; + edge_iterator ei; + FOR_EACH_EDGE (entry_edge, ei, loop->header->preds) + { + if (entry_edge == latch_edge) + continue; + add_phi_arg (min_phi, build_zero_cst (ssa_type), entry_edge, + UNKNOWN_LOCATION); + add_phi_arg (max_phi, build_zero_cst (ssa_type), entry_edge, + UNKNOWN_LOCATION); + } + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Insert min/max calculation\n"); + dump_bb (dump_file, loop->header, 0, TDF_DETAILS); + dump_bb (dump_file, bb, 0, TDF_DETAILS); + } +} + +/* Insert code to calculate fc_cond after fclose. */ + +tree +ipa_struct_reorg::insert_code_calc_cond (fc_type_info *info, + gimple_stmt_iterator *gsi) +{ + tree dfc_path = boolean_true_node; + for (auto *cond : info->fc_conds) + { + /* min >= low_bound */ + tree cmp_min = fold_build2 (GE_EXPR, boolean_type_node, + cond->min_val, cond->low_bound); + + /* max <= high_bound */ + tree cmp_max = fold_build2 (LE_EXPR, boolean_type_node, + cond->max_val, cond->high_bound); + + /* ret = ((min >= low_bound) && (max <= high_bound)) */ + tree cmp_ret = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, + cmp_min, cmp_max); + + /* dfc.path.tmp = dfc.path.tmp && ret */ + tree tmp = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, dfc_path, + cmp_ret); + dfc_path = force_gimple_operand_gsi (gsi, tmp, true, NULL, false, + GSI_CONTINUE_LINKING); + } + + return dfc_path; +} + +/* Insert code to check init_const for dynamic shadow fields. */ + +void +ipa_struct_reorg::insert_code_check_init_const (fc_type_info *info, + gimple_stmt_iterator *gsi, + tree &dfc_path) +{ + basic_block bb = gimple_bb (info->input_stmt); + class loop *loop = bb->loop_father; + edge latch_edge = loop_latch_edge (loop); + + for (auto *fc_f : info->dynamic_shadow_fields) + { + gcc_assert (fc_f->init_const); + + /* Skip a init_const that is in special_values, because the boundary + check for fc_cond should have cover that. */ + tree init_const = fc_f->init_const; + if (fc_f->input_field->cond->special_values.contains ( + tree_to_uhwi (init_const))) + continue; + + tree shadow_valid = make_temp_ssa_name (boolean_type_node, NULL, + "dfc.shadow_valid"); + gphi *shadow_valid_phi = create_phi_node (shadow_valid, loop->header); + + auto input_gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (fc_f->input_ssa)); + /* input != init_const */ + tree ne_ret = fold_build2 (NE_EXPR, boolean_type_node, fc_f->input_ssa, + init_const); + tree ne_tmp = force_gimple_operand_gsi (&input_gsi, ne_ret, true, NULL, + false, GSI_CONTINUE_LINKING); + + /* shadow_valid = shadow_valid && (input != init_const) */ + tree and_ret = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, + shadow_valid, ne_tmp); + tree and_tmp = force_gimple_operand_gsi (&input_gsi, and_ret, true, + NULL, false, + GSI_CONTINUE_LINKING); + + /* Insert phi for shadow_valid in loop header. */ + add_phi_arg (shadow_valid_phi, and_tmp, latch_edge, UNKNOWN_LOCATION); + edge entry_edge = NULL; + edge_iterator ei; + FOR_EACH_EDGE (entry_edge, ei, loop->header->preds) + { + if (entry_edge == latch_edge) + continue; + add_phi_arg (shadow_valid_phi, boolean_true_node, entry_edge, + UNKNOWN_LOCATION); + } + + /* dfc.path.tmp = dfc.path.tmp && shadow_valid */ + tree tmp = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, dfc_path, + shadow_valid); + dfc_path = force_gimple_operand_gsi (gsi, tmp, true, NULL, false, + GSI_CONTINUE_LINKING); + } +} + +/* Split edge E and insert code to compress data. */ + +void +ipa_struct_reorg::insert_code_compress_data (fc_type_info *info, edge e) +{ + if (!dom_info_available_p (CDI_DOMINATORS)) + calculate_dominance_info (CDI_DOMINATORS); + if (!loops_state_satisfies_p (LOOPS_HAVE_PREHEADERS)) + loop_optimizer_init (LOOPS_HAVE_PREHEADERS); + record_loop_exits (); + + auto_vec array_names; + auto_vec size_names; + basic_block bb = e->src; + gimple_stmt_iterator gsi = gsi_last_bb (bb); + + /* Generate SSA_NAMEs for fc_array, if they are global addresses. */ + for (auto *array : info->fc_arrays) + { + array_names.safe_push (generate_ssa_name (array->var, &gsi)); + size_names.safe_push (generate_ssa_name (array->size, &gsi)); + } + + insert_code_compress_variant (info, bb, array_names, size_names); +} + +/* Insert code tot compress hot data for a fc_variant. */ + +void +ipa_struct_reorg::insert_code_compress_variant ( + fc_type_info *info, basic_block bb, const auto_vec &array_names, + const auto_vec &size_names) +{ + edge true_e = NULL; + edge false_e = NULL; + edge entry_e = single_succ_edge (bb); + create_empty_if_region_on_edge (entry_e, info->dfc_path); + extract_true_false_edges_from_block (entry_e->dest, &true_e, &false_e); + + /* Create function decl and node for compressed single object. */ + create_compress_object_fn (info); + + edge current_e = true_e; + insert_code_compress_array (info, current_e, array_names, size_names); + insert_code_modify_refs (info, current_e); +} + +/* For each variable(array) to compress, insert loop like: + : + _1 = ARRAY_BASE; + _2 = ARRAY_SIZE; + goto ; + + : + # i_1 = PHI <0, i_2(THEN)> + if (i_1 < _2) + goto ; + else + goto ; + + : + dfc.compress_obj (_1, i_1); + i_2 = i_1 + 1; + + : + */ + +void +ipa_struct_reorg::insert_code_compress_array ( + fc_type_info *info, edge &e, const auto_vec &data_names, + const auto_vec &size_names) +{ + loop_p outer_loop = gimple_bb (info->fclose_stmt)->loop_father; + for (size_t i = 0; i < size_names.length (); ++i) + { + tree iv_before, iv_after; + tree size_type = TREE_TYPE (size_names[i]); + tree iv = create_tmp_reg (size_type, "dfc.compress_idx"); + loop_p loop + = create_empty_loop_on_edge (e, build_zero_cst (size_type), + build_int_cst (size_type, 1), + size_names[i], iv, &iv_before, + &iv_after, outer_loop); + + /* Build call statement to compress a single object. */ + basic_block latch_bb = loop->latch; + auto gsi = gsi_last_bb (latch_bb); + tree fndecl = info->variant->compress_object_fn; + gcall *call = gimple_build_call (fndecl, 2, data_names[i], iv_before); + gsi_insert_after (&gsi, call, GSI_NEW_STMT); + cgraph_node *node = cgraph_node::get (current_function_decl); + cgraph_node *new_node = cgraph_node::get (fndecl); + node->create_edge (new_node, call, latch_bb->count); + + e = single_exit (loop); + } +} + +/* Insert code to modify all fc_refs. */ + +void +ipa_struct_reorg::insert_code_modify_refs (fc_type_info *info, edge current_e) +{ + fc_variant *variant = info->variant; + loop_p outer_loop = gimple_bb (info->fclose_stmt)->loop_father; + for (auto *dr : info->fc_refs) + { + if (!dr->size) + { + /* 1) fc_ref is a single ptr. */ + current_e = insert_code_modify_single_ref ( + current_e, dr->var, dr->source, + TYPE_SIZE_UNIT (info->type->type), + TYPE_SIZE_UNIT (variant->new_type)); + continue; + } + + /* 2) fc_ref is an array, create a loop. */ + tree iv_before, iv_after; + tree ptr_type = dr->orig_type ? dr->orig_type : TREE_TYPE (dr->var); + tree size_type = TREE_TYPE (dr->size); + loop_p loop = create_empty_loop_on_edge ( + current_e, build_zero_cst (size_type), + build_int_cst (size_type, 1), dr->size, + create_tmp_reg (size_type, NULL), &iv_before, + &iv_after, outer_loop); + /* Fetch array element. */ + auto gsi = gsi_last_bb (loop->latch); + tree var1 = make_ssa_name (ptr_type); + gsi_insert_after (&gsi, gimple_build_assign (var1, dr->var), + GSI_NEW_STMT); + tree var_mul = make_ssa_name (long_unsigned_type_node); + APPEND_GASSIGN_2 (gsi, var_mul, MULT_EXPR, iv_before, + TYPE_SIZE_UNIT (TREE_TYPE (ptr_type))); + tree var_plus = make_ssa_name (ptr_type); + APPEND_GASSIGN_2 (gsi, var_plus, POINTER_PLUS_EXPR, var1, var_mul); + tree ref_expr = build2 (MEM_REF, TREE_TYPE (ptr_type), var_plus, + build_int_cst (ptr_type, 0)); + if (dr->field) + ref_expr = build3 (COMPONENT_REF, TREE_TYPE (dr->field), ref_expr, + dr->field, NULL_TREE); + /* Modify the ref's value. */ + insert_code_modify_single_ref (single_succ_edge (loop->latch), + ref_expr, dr->source, + TYPE_SIZE_UNIT (info->type->type), + TYPE_SIZE_UNIT (variant->new_type)); + current_e = single_exit (loop); + } +} + +/* Create function to compress a single object. Return function decl. */ + +void +ipa_struct_reorg::create_compress_object_fn (fc_type_info *info) +{ + /* Function declairation. */ + tree orig_struct_type = info->type->type; + tree orig_struct_size = TYPE_SIZE_UNIT (orig_struct_type); + tree orig_ptr_type = build_pointer_type (orig_struct_type); + tree size_type = TREE_TYPE (orig_struct_size); + tree arg_types[2] = {orig_ptr_type, size_type}; + char fn_name[32]; + sprintf (fn_name, "%s", "dfc.compress_obj"); + tree fndecl = create_new_fn_decl (fn_name, 2, arg_types, void_type_node); + + /* Function arguments. */ + tree struct_array = DECL_ARGUMENTS (fndecl); + tree idx = TREE_CHAIN (struct_array); + + /* Push NULL cfun. */ + push_cfun (NULL); + basic_block bb = init_lowered_empty_function ( + fndecl, true, profile_count::uninitialized ()); + + /* Function body. */ + /* Use a temporary struct to avoid overlapping. */ + tree tmp_obj = create_tmp_var (orig_struct_type, "tmp"); + /* tmp = start[i]; + => + idx_1 = (long unsigned int) idx; + _2 = idx_1 * sizeof (orig_struct); + _3 = start + _2; + tmp = *_3; + */ + gimple_stmt_iterator gsi = gsi_last_bb (bb); + tree offset = make_ssa_name (long_unsigned_type_node); + APPEND_GASSIGN_2 (gsi, offset, MULT_EXPR, idx, orig_struct_size); + tree address = make_ssa_name (orig_ptr_type); + APPEND_GASSIGN_2 (gsi, address, POINTER_PLUS_EXPR, struct_array, offset); + tree rhs = build2 (MEM_REF, orig_struct_type, address, + build_int_cst (orig_ptr_type, 0)); + APPEND_GASSIGN_1 (gsi, tmp_obj, MEM_REF, rhs); + + /* Init: new_struct* ptr = start + idx_1 * sizeof (new_struct) */ + fc_variant *variant = info->variant; + tree new_type = variant->new_type; + tree new_ptr_type = build_pointer_type (new_type); + tree new_ptr = create_tmp_var (new_ptr_type, "ptr"); + offset = make_ssa_name (long_unsigned_type_node); + APPEND_GASSIGN_2 (gsi, offset, MULT_EXPR, idx, TYPE_SIZE_UNIT (new_type)); + APPEND_GASSIGN_2 (gsi, new_ptr, POINTER_PLUS_EXPR, struct_array, offset); + tree ref = build2 (MEM_REF, new_type, new_ptr, + build_int_cst (new_ptr_type, 0)); + + /* Compress and assign the fields. */ + for (auto *field : info->type->fields) + { + /* Skip shadow fields. */ + if (field->fc_f && field->fc_f->original) + continue; + + tree old_field = field->fielddecl; + tree old_field_type = field->fieldtype; + tree new_field = field->newfield[0] ? field->newfield[0] : old_field; + tree new_field_type = TREE_TYPE (new_field); + + tree var = make_ssa_name (old_field_type); + tree rhs = build3 (COMPONENT_REF, old_field_type, tmp_obj, old_field, + NULL_TREE); + APPEND_GASSIGN_1 (gsi, var, COMPONENT_REF, rhs); + if (new_field_type != old_field_type) + { + fc_cond *cond = field->fc_f->cond; + if (cond && cond->compress_fn) + { + /* Need compressing. */ + /* As we may have bitfield, so cond->new_type and new_type + can be different. */ + tree compressed_var = make_ssa_name (cond->new_type); + gcall *stmt = gimple_build_call (cond->compress_fn, 1, var); + gimple_call_set_lhs (stmt, compressed_var); + gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); + var = compressed_var; + } + tree converted_var = make_ssa_name (new_field_type); + APPEND_GASSIGN_1 (gsi, converted_var, NOP_EXPR, var); + var = converted_var; + } + tree lhs = build3 (COMPONENT_REF, new_field_type, ref, new_field, + NULL_TREE); + APPEND_GASSIGN_1 (gsi, lhs, MEM_REF, var); + } + + /* Clobber and return. */ + tree clobber = build_clobber (orig_struct_type); + gimple *stmt = gimple_build_assign (tmp_obj, clobber); + gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); + stmt = gimple_build_return (NULL); + gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); + + { + SET_DUMP_FILE (NULL, TDF_NONE); + update_ssa (TODO_update_ssa); + } + + cgraph_node::create (fndecl); + cgraph_node::add_new_function (fndecl, true); + cgraph_edge::rebuild_edges (); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + FC_DUMP_MSG ("Create compress object function:\n"); + dump_function_to_file (cfun->decl, dump_file, dump_flags); + } + pop_cfun (); + + info->variant->compress_object_fn = fndecl; +} + +/* Split edge E and insert codes to modify a single fc_ref expression. + Return the exit edge of created codes. */ + +edge +ipa_struct_reorg::insert_code_modify_single_ref (edge e, tree ref, + fc_array *array, + tree orig_size, + tree new_size) +{ + /* For each fc_ref, create code like: + if (REF) + REF = (long) ARRAY + ((long) REF - (long) ARRAY) + / sizeof(old_type) * sizeof(new_type); + */ + + /* 1) Create ssa_name for fc_ref. */ + tree ref_ssa_name = create_tmp_reg (TREE_TYPE (ref)); + gimple *stmt = gimple_build_assign (ref_ssa_name, unshare_expr (ref)); + basic_block bb = split_edge (e); + gimple_stmt_iterator gsi = gsi_last_bb (bb); + gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); + + /* 2) Create the if-else structure. */ + tree cmp = build2 (EQ_EXPR, boolean_type_node, ref_ssa_name, + null_pointer_node); + edge exit_e = create_empty_if_region_on_edge (single_succ_edge (bb), cmp); + edge true_e = NULL; + edge false_e = NULL; + extract_true_false_edges_from_block (single_succ (bb), &true_e, &false_e); + gsi = gsi_last_bb (false_e->dest); + + /* 3) Create conversion codes. */ + tree ssa_def = array->ssa_def; + tree sub_var = make_ssa_name (ptrdiff_type_node); + APPEND_GASSIGN_2 (gsi, sub_var, POINTER_DIFF_EXPR, ref_ssa_name, ssa_def); + tree div_var = make_ssa_name (ptrdiff_type_node); + APPEND_GASSIGN_2 (gsi, div_var, TRUNC_DIV_EXPR, sub_var, + fold_convert (ptrdiff_type_node, orig_size)); + tree mul_var = make_ssa_name (ptrdiff_type_node); + APPEND_GASSIGN_2 (gsi, mul_var, MULT_EXPR, div_var, + fold_convert (ptrdiff_type_node, new_size)); + tree mul_var2 = make_ssa_name (size_type_node); + APPEND_GASSIGN_1 (gsi, mul_var2, NOP_EXPR, mul_var); + tree add_var = make_ssa_name (TREE_TYPE (ssa_def)); + APPEND_GASSIGN_2 (gsi, add_var, POINTER_PLUS_EXPR, ssa_def, mul_var2); + + /* 4) Store. */ + gsi_insert_after (&gsi, gimple_build_assign (unshare_expr (ref), add_var), + GSI_NEW_STMT); + return exit_e; +} + +/* Init pointer size from parameter param_pointer_compression_size. */ + +static void +init_pointer_size_for_pointer_compression (void) +{ + switch (param_pointer_compression_size) + { + case 8: + // FALLTHRU + case 16: + // FALLTHRU + case 32: compressed_size = param_pointer_compression_size; break; + default: + error ("Invalid pointer compression size, using the following param: " + "\"--param compressed-pointer-size=[8,16,32]\""); + } +} + +unsigned int +ipa_struct_reorg::execute (unsigned int opt) +{ + unsigned int ret = 0; + + if (dump_file) + fprintf (dump_file, "\n\n====== ipa_struct_reorg level %d ======\n\n", + opt); + + if (opt != COMPLETE_STRUCT_RELAYOUT) + { + current_layout_opt_level = opt; + /* If there is a top-level inline-asm, + the pass immediately returns. */ + if (symtab->first_asm_symbol ()) + return 0; + record_accesses (); + prune_escaped_types (); + if (current_layout_opt_level == STRUCT_SPLIT) + analyze_types (); if (opt >= POINTER_COMPRESSION_SAFE) check_and_prune_struct_for_pointer_compression (); if (opt >= SEMI_RELAYOUT) check_and_prune_struct_for_semi_relayout (); /* Avoid doing static field compression in STRUCT_SPLIT. */ - if (opt >= STRUCT_REORDER_FIELDS && flag_ipa_struct_sfc) + if (opt >= STRUCT_REORDER_FIELDS + && current_fc_level == fc_level::STATIC) check_and_prune_struct_for_field_compression (); ret = rewrite_functions (); } @@ -10089,6 +13792,10 @@ public: relayout_part_size = 1 << semi_relayout_level; } + current_fc_level = fc_level::NONE; + if (flag_ipa_struct_sfc) + current_fc_level = fc_level::STATIC; + /* Preserved for backward compatibility, reorder fields needs run before struct split and complete struct relayout. */ if (flag_ipa_reorder_fields && level < STRUCT_REORDER_FIELDS) @@ -10097,16 +13804,29 @@ public: if (level >= STRUCT_REORDER_FIELDS) ret = ipa_struct_reorg ().execute (level); + /* Reset current_fc_level before struct_split and csr. */ + current_fc_level = fc_level::NONE; + if (ret & TODO_remove_functions) symtab->remove_unreachable_nodes (dump_file); if (level >= COMPLETE_STRUCT_RELAYOUT) { - /* Preserved for backward compatibility. */ - ret_reorg = ipa_struct_reorg ().execute (STRUCT_SPLIT); + /* Preserved for backward compatibility. + Rewrite between STRUCT_REORDER_FIELDS and STRUCT_SPLIT has unfixed + problem, so avoid using them together. */ + if (!ret) + ret_reorg = ipa_struct_reorg ().execute (STRUCT_SPLIT); if (!ret_reorg) ret_reorg = ipa_struct_reorg ().execute (COMPLETE_STRUCT_RELAYOUT); } + + if (ret && flag_ipa_struct_dfc) + { + current_fc_level = fc_level::DYNAMIC; + ret = ipa_struct_reorg ().execute_dynamic_field_compression (); + } + return ret | ret_reorg; } @@ -10135,3 +13855,4 @@ make_pass_ipa_struct_reorg (gcc::context *ctxt) { return new pass_ipa_struct_reorg (ctxt); } + diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h index 2ab6444d645f551e192bb212859d79e307b3a729..c7c6b74339c50f5bc341fb6bb4d23f7ef4788b24 100644 --- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h +++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h @@ -59,14 +59,56 @@ const char *escape_type_string[escape_max_escape - 1] = #include "escapes.def" }; +enum class check_ref_result +{ + NEW, + DUPLICATIVE, + ERROR, +}; + struct srfield; struct srtype; struct sraccess; struct srdecl; struct srfunction; +class fc_closure; class fc_type_info; class fc_field; class fc_field_class; +class fc_cond; + +class fc_path_info +{ +public: + enum direction + { + PRED, + SUCC + }; + +public: + /* The start stmt to clone blocks. If it is NULL, the whole function is + cloned (i.e. versioning). */ + gimple *start_stmt = NULL; + + /* Blocks reachable from the start_stmt. */ + auto_vec reach_bbs; + + /* Blocks that can reach the start_stmt. */ + auto_vec pre_bbs; + + /* Cloned basic blocks of reach_bbs. */ + auto_vec cloned_bbs; + + /* Cloned whole function versions. */ + srfunction *cloned_func = NULL; + + fc_path_info () + {} + ~fc_path_info (); + + bool collect_blocks (gimple *, direction); +}; struct srfunction { @@ -82,6 +124,8 @@ struct srfunction bool is_safe_func; + fc_path_info fc_path; + // Constructors srfunction (cgraph_node *n); @@ -93,6 +137,9 @@ struct srfunction bool check_args (void); void create_new_decls (void); srdecl *find_decl (tree); + + bool partial_clone_p (); + bool entry_function_p (); }; struct srglobal : private srfunction @@ -155,6 +202,7 @@ public: void add_field_site (srfield *); srfield *find_field (unsigned HOST_WIDE_INT offset); + srfield *find_field_by_decl (tree); bool create_new_type (void); void analyze (void); @@ -164,10 +212,8 @@ public: unsigned calculate_bucket_size (); bool has_recursive_field_type (); void check_fc_fields (); - bool has_escaped (void) - { - return escapes != does_not_escape; - } + bool reorg_name_p (); + bool has_escaped (void); const char *escape_reason (void) { if (!has_escaped ()) @@ -203,7 +249,7 @@ struct srfield tree newfield[max_split]; unsigned field_access; /* FIELD_DECL -> bitflag (use for dfe). */ - fc_field *static_fc_field; + fc_field *fc_f; fc_field_class *field_class; // Constructors @@ -222,6 +268,8 @@ struct srfield tree newfields[max_split], tree newlast[max_split]); bool dead_field_p (); + bool dfc_type_change_p (); + fc_closure *get_closure (); }; struct sraccess @@ -243,9 +291,10 @@ struct sraccess // Methods void dump (FILE *file) const; - bool write_type_p (tree) const; - bool write_field_p (tree) const; - bool read_field_p (tree) const; + bool write_field_p (tree = NULL_TREE) const; + bool read_field_p (tree = NULL_TREE) const; + bool write_p () const; + bool read_p () const; }; struct srdecl @@ -276,17 +325,76 @@ struct srdecl } }; +/* Describe stmt closure to help rewrite. The closure could be either array + pointers for the same memory space, or normal data without calculation. */ + +class fc_closure +{ +public: + /* The stmts for read/write of the fc field. For read/write_change, we need + to add convert function for read and write respectively. */ + hash_set read_unchange_set; + hash_set read_change_set; + hash_set write_unchange_set; + hash_set write_change_set; + + /* Record the known special rhs assigned to this fc field. */ + hash_map write_special_rhs; + + void add_read_change (gimple *); + bool read_change_p (gimple *); + void add_read_unchange (gimple *); + bool read_unchange_p (gimple *); + void add_write_change (gimple *); + bool write_change_p (gimple *); + void add_write_unchange (gimple *); + bool write_unchange_p (gimple *); + bool change_p (gimple *); + bool unchange_p (gimple *); + + /* Call compress/decompress function FN for RHS. */ + tree convert_rhs (tree, tree); +}; + +class closure_helper +{ +private: + /* The unique id for assign stmts used in collecting closure info. */ + int uid; + fc_closure *cinfo; + + auto_bitmap read_change_map; + auto_bitmap write_change_map; + auto_bitmap read_unchange_map; + auto_bitmap write_unchange_map; + +public: + closure_helper (fc_closure *cinfo) + : uid (0), cinfo (cinfo) + {} + + void record_origin_closure (basic_block); + void add_cloned_closure (basic_block); + void reset_uid (); +}; + /* All fields belong to this class should have the same type. */ class fc_field_class { public: /* The same type for all of the fields in the class. */ - tree fieldtype; + tree fieldtype = NULL_TREE; /* The fields with the same type are in the same element of this vector. */ auto_vec srfields; + /* Back reference to corresponding fc_cond. */ + fc_cond *cond = NULL; + + /* Record all info related if the class is an identified closure. */ + fc_closure closure; + fc_field_class (tree fieldtype) : fieldtype (fieldtype) {} @@ -296,23 +404,80 @@ public: int get_field_index (srfield *) const; }; +/* The fc condition for a specified data type. Multiple vars with the same + data type can map to the same fc_cond object. */ + +class fc_cond +{ +public: + /* The old field data type for this condition. */ + tree old_type = NULL_TREE; + + /* The new field data type for this condition. */ + tree new_type = NULL_TREE; + + /* The bit width of the new_type if it is a bit field. */ + unsigned bits = 0; + + /* The type class to which all of fc_fields in this condition belongs. */ + fc_field_class *field_class = NULL; + + /* May have multiple fields mapping to this condition, as they have the + same data type. */ + auto_vec fields; + + /* The condition variable we want to check. */ + tree cond_var = NULL_TREE; + + /* The vars to hold the min and max input. */ + tree min_val = NULL_TREE; + tree max_val = NULL_TREE; + + /* The constant value we need to check at run-time. */ + tree low_bound = NULL_TREE; + tree high_bound = NULL_TREE; + + /* Hold all special constant values for this condition type. */ + auto_vec special_values; + + /* Compress and decompress function decls, if there're special values. */ + tree compress_fn = NULL_TREE; + tree decompress_fn = NULL_TREE; + + fc_cond (tree old_type = NULL_TREE) + : old_type (old_type) + {} + ~fc_cond () + {} +}; + /* The field for field compression. */ class fc_field { public: - tree field; - tree new_type; + tree field = NULL_TREE; + tree new_type = NULL_TREE; /* This field's max value we can know at compile time. If it is 0, it means the max value cannot be determined at compile time. */ - HOST_WIDE_INT max_value; + HOST_WIDE_INT max_value = 0; /* The bit width of the field if it is not zero. */ - unsigned bits; + unsigned bits = 0; + + /* The total number of static reference count. The bigger, the smaller + size for dynamic field compression. */ + unsigned ref_cnt = 0; /* The original field of a shadow field if it is not NULL. */ - srfield *original; + srfield *original = NULL; + + /* A dynamic shadow field must have a input fc_field counter part. */ + fc_field *input_field = NULL; + + /* Init constants of the original srfield. */ + tree init_const = NULL_TREE; /* All assignments that need to be optimized as shadow. */ auto_vec shadow_stmts; @@ -321,10 +486,23 @@ public: stmt belongs to. */ auto_vec shadow_stmts_func; + /* The input var that is read from a file, and assigned to this fc_field. */ + tree input_var = NULL_TREE; + + /* The ssa for the input_var. */ + tree input_ssa = NULL_TREE; + + /* The condition var descriptor for this field. */ + fc_cond *cond = NULL; + /* For static field compression. */ fc_field (tree field, HOST_WIDE_INT max_value, srfield *original) - : field (field), new_type (NULL_TREE), max_value (max_value), - bits (0), original (original) + : field (field), max_value (max_value), original (original) + {} + + /* For dynamic field compression. */ + fc_field (tree field, tree input_var, tree input_ssa) + : field (field), input_var (input_var), input_ssa (input_ssa) {} unsigned get_bits (void) const @@ -333,32 +511,138 @@ public: } }; +/* A hot array that needs to be cached. */ + +class fc_array +{ +public: + /* The variable declaration that holds the data to be cached. */ + tree var = NULL_TREE; + + /* The size expr to help data initialization. */ + tree size = NULL_TREE; + + /* If fc_array is allocated in start-function, record the ssa_name of + allocated ptr, we may need this to create fc_refs. */ + tree ssa_def = NULL_TREE; + + /* varpool_node for a global fc_array variable. We may need this to search + for fc_refs. */ + varpool_node *vnode = NULL; + + fc_array (tree var, tree size, tree ssa_def, varpool_node *vnode) + : var (var), size (size), ssa_def (ssa_def), vnode (vnode) + {} + ~fc_array () + {} +}; + +/* A variable that needs to be modified according to the caching data. */ + +class fc_ref +{ +public: + /* The variable's declaration. */ + tree var = NULL_TREE; + + /* "real" type, for void*. */ + tree orig_type = NULL_TREE; + + /* fc_array referred by this variable. */ + fc_array *source = NULL; + + /* Number of elements, if this variable is an array. */ + tree size = NULL_TREE; + + /* For array of records, this is the field to be modified. */ + tree field = NULL_TREE; + + fc_ref (tree var, tree orig_type, fc_array *source, + tree size, tree field) + : var (var), orig_type (orig_type), source (source), + size (size), field (field) + {} + ~fc_ref () + {} + + void dump (FILE *) const; +}; + +/* Variants for different dynamic checking condition combinations. */ +class fc_variant +{ +public: + /* New structure type. */ + tree new_type = NULL_TREE; + + /* The function to compress a single object. */ + tree compress_object_fn = NULL_TREE; +}; + /* The class to hold field compression type information. A single info object is only for one structure type. */ class fc_type_info { public: - srtype *type; + srtype *type = NULL; /* The flag to control whether the type can do static field compression. */ bool static_fc_p = false; + bool dynamic_fc_p = false; /* Multiple fields of the data struct for static compression. */ auto_delete_vec static_fc_fields; + /* Multiple fields of the data struct for dynamic compression. */ + auto_delete_vec dynamic_fc_fields; + + /* Multiple fields of the data struct for dynamic shadow. */ + auto_delete_vec dynamic_shadow_fields; + + /* The stmt that read data from file. */ + gimple *input_stmt = NULL; + + /* The variable into which the data read from input stmt is assigned. */ + tree input_var = NULL_TREE; + + /* The file handler of data file. */ + tree input_file_handler = NULL_TREE; + + /* The fclose stmt of the data file. */ + gimple *fclose_stmt = NULL; + + /* The function with start point. */ + srfunction *start_srfn = NULL; + + /* All fc_array variables need to be compressed. */ + auto_delete_vec fc_arrays; + + /* All variables to be modified according to compressed data. */ + auto_delete_vec fc_refs; + + /* All indivisual fc conditions. */ + auto_delete_vec fc_conds; + + /* The variant of data type after dfc. Now we only support one variant. */ + fc_variant *variant = NULL; + + /* The flag to indicate which path to run. */ + tree dfc_path = NULL_TREE; + /* The field classes classified by field type. */ auto_delete_vec field_classes; fc_type_info (srtype *type) : type (type) {} - fc_type_info () - : type (NULL) - {} + ~fc_type_info (); fc_field_class *find_field_class_by_type (tree) const; fc_field_class *record_field_class (srfield *); + fc_cond *find_cond (tree) const; + fc_cond *create_cond (tree); + void record_cond (fc_field *); }; /* The structure to hold necessary information for field shadow. */ diff --git a/gcc/ipa-utils.cc b/gcc/ipa-utils.cc index 67dd42f4faf41efca0a503314dc01a92da0fda8c..5d9981c0420e6111cab3e8bb82adbf21cc20638b 100644 --- a/gcc/ipa-utils.cc +++ b/gcc/ipa-utils.cc @@ -35,6 +35,30 @@ along with GCC; see the file COPYING3. If not see #include "tree-vrp.h" #include "ipa-prop.h" #include "ipa-fnsummary.h" +#include "cfgloop.h" + +cfun_saver::cfun_saver (cgraph_node *node) +{ + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + calculate_dominance_info (CDI_DOMINATORS); + calculate_dominance_info (CDI_POST_DOMINATORS); +} + +cfun_saver::cfun_saver (cgraph_node *node, unsigned loop_flags) + : cfun_saver (node) +{ + loop_optimizer_init (loop_flags); + need_finalize_loop_optimizer = true; +} + +cfun_saver::~cfun_saver () +{ + if (need_finalize_loop_optimizer) + loop_optimizer_finalize (); + free_dominance_info (CDI_POST_DOMINATORS); + free_dominance_info (CDI_DOMINATORS); + pop_cfun (); +} /* Debugging function for postorder and inorder code. NOTE is a string that is printed before the nodes are printed. ORDER is an array of @@ -781,3 +805,23 @@ recursive_call_p (tree func, tree dest) return false; return true; } + +/* Return true if NODE has only one non-recursive caller and no non-recursive + callee. */ +bool +leaf_recursive_node_p (cgraph_node *node) +{ + if (node->inlined_to || !node->has_gimple_body_p () || node->indirect_calls) + return false; + + for (cgraph_edge *e = node->callees; e; e = e->next_callee) + if (node != e->callee) + return false; + + unsigned non_recursive_caller_count = 0; + for (cgraph_edge *e = node->callers; e; e = e->next_caller) + if (node != e->caller) + non_recursive_caller_count++; + + return non_recursive_caller_count == 1; +} diff --git a/gcc/ipa-utils.h b/gcc/ipa-utils.h index dc6ba0d52cb46bebcece451335115b66a2c15719..15c63e9055a2113902d00fe8c7def85756b39254 100644 --- a/gcc/ipa-utils.h +++ b/gcc/ipa-utils.h @@ -21,6 +21,9 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_IPA_UTILS_H #define GCC_IPA_UTILS_H +#include "cgraph.h" +#include "function.h" + struct ipa_dfs_info { int dfn_number; int low_link; @@ -33,6 +36,17 @@ struct ipa_dfs_info { PTR aux; }; +/* Use RAII to help save cfun. */ +class cfun_saver +{ +public: + cfun_saver (cgraph_node *node); + cfun_saver (cgraph_node *node, unsigned loop_flags); + ~cfun_saver (); + +private: + bool need_finalize_loop_optimizer = false; +}; /* In ipa-utils.cc */ void ipa_print_order (FILE*, const char *, struct cgraph_node**, int); @@ -46,6 +60,7 @@ tree get_base_var (tree); void ipa_merge_profiles (struct cgraph_node *dst, struct cgraph_node *src, bool preserve_body = false); bool recursive_call_p (tree, tree); +bool leaf_recursive_node_p (cgraph_node *node); /* In ipa-pure-const.cc */ bool finite_function_p (); diff --git a/gcc/optimizer.fdata b/gcc/optimizer.fdata index 09e709df7c4c5e600113ee10e473ef2557154622..adc2f53995fcbe800d6ad16628f68e0c476a22f0 100644 --- a/gcc/optimizer.fdata +++ b/gcc/optimizer.fdata @@ -1 +1 @@ -36363265623439336565393638386239666262313430386562333233346136306663393564326465346632613262633135383163376363623961663035303265323332643236613938346534633837626639313236343833376565646336623561346131343434613933333439626530616333373164653737643064383432366462386336616334336364313031343563656562383436336131613339323036393765383565376235353462643966313363353862306235343964623761663033323766613666343338633462663964346530316365383233306264386333366433663037623164396431623166393365633338633865626230373437613731356564396339653565313130376364653339323433373561333363313235306139353133393562353964383437326630386163373937386236666337636538323332613938393936356637306137336161393933653736663135333434656331306137336566363563333266343765313638323534363562323436643037333036633636323762396265646537323334613134326431313765383864333461643034633936373261323762386663636431323261393434326164323138653135643034396530333030633232626633663462616333346164313965396232656630306434333362303731376230393462633636353761633934363730663363306633323161306362333933353937646131663265323435393861613361646362353435623234613566626161633964663837303135663330616162646564366563306661626462396565613861323335333130323636356561613332313165393564336335383236666363343462623637666338633566636433353033643335323438303163626435376161663866303161326335373832643865626562323665333832323238656562646439303463396237383536326465666461643935333662346237613933313465666237303362613533613833313664303263643065333430386161643933373630636338386230643962396264643538393161316165613735646266646334636433336162303539613932336664326238376436376332353061363165386165363466663033303031656232366234613363356631653334623666313236366366373962356536656434396231303338646265323666386461366430396262353536313433636132653466623061346164303635636162336536383062306637306438626232646636393462353563366437386531316463383239373361643230643566333736663330656538643461313161306163666361663064383962373736636162323565383865336630333461343939336231366437386265323439626332336166376262623837353163376533353066636339313233323761613766333633343432623331373530376432376534623831333339383964633439653966303663303439623739346133626330646333373831393930316439326233646565623761646664356230336233363230383833616266643463626536336133346664656433373630343738343262373863646131373633653939636430616439393731306435313664323166313530636464393664613738643461356437333564343036316262353462313336663335313763323661363564343330623965363866616534323163616337613964356465333333663739313835623363316462346539643539636435303166306664366135313063333630336531336532346134306234626632313565333739356139613430643630613834353666623762363363326431316538313730316132333165383561666434623564623831616263316464353664323731373332656430323435663836393162646335646164333437366436663633353630373762316161316436333461393763343130396235396237333534376639343063316463386432316330663138373338386632643361386565636665653766643836373930326664346163333162613163396664663531626161326134333762666330393261316637333265326138663036363736333734326230373730613665323665333266326265356534376133373330393466336639623431383863383433643563346265613561326134616334626262646637666163376365333962323036323537333737313534313066323364623937303534623665623237306566636366623763623431373666383236383134326365613136653238623231646339353938373030656631646238653961643434653765313834316231346231323563636633356131376538636666613866303638323362643436636638353665346166323633623861616639363862613835306234643961666662656334393366613066653061383333653965343633653837613263636530633863303861313363636361346132613431363962336539613463313366633934633761363761623039396365393263633638656134376162616631363838616238613137323032663864613035313363353335396432373530363233663234343136346339663435333834656630366537353336306137366434646264303466303633663630386363636337383066316631323836353135393134646139373035323637346164303965323536666335393864636364346433383564616435383862366464326130336536313934636139386438373462333162366230623165323533336263313430386430643661386261633061313631613639313232313734383136336464636231323130336231613131373336636238656635353635626666303535663331363332353338313363396334313631333034656133666365353561643830356565633137346638643739636136376432303761633436666465336232356236653164353163346461656165333038653266336161383966633961323462376262386430363536323932373263343731313562356139336265383765336139643837333966623265386131666561366161333138353261663139303338393733346335313934363761393137633266616431663436343236613231663865636236346133616662623761373633663830623231393063616534633032316538626436633731643261313866666339353836373133363939623966636239333637373764323863663964376134323134613133346335326162363334363334386666396666336534623731316461393362623234363636613761643036306364363262623730633535353639623335356663393239396434616263663637656365653039616130666465656162643639663565663234653465386137626238643037666463386662646434666665373032636231623265396534356431303130613862356236313062323031396339626536613030383761623236663432633564653130353935353135313736656235373632373739343662663034343334633035626465356237623135653266313930616430336138383639633339333961623638343936366131366130366564353563396166656361343130346633353434656463353337386630636163303536343164663364383738353266386330376562343962393037306133383363326138393238376435613332353933663235313030326664366164353131373131663834333965316261373539326165383333306531643264386561663433393632623662643266323765643964343963333565653437333831376663333864623131643336303039363361373238633136346130313939316131643632376432623531623761336439353730363063363461613765633838623163656131393765356265356262346663313631633038353363623935356135306362346436336263643865663961323332323737336332303938656636646395a95443848e2641befc27414f10354337870e456d125441a2bade3ba4f5c23d0205be3d8e21c43bb26bb938c5d6d73d0000803f84cc8c3b9a0a953976c9d4ba00b1833b048aa73bbe1c823b3fdf50ba8f81bf3b25a2823bbd893e3ceca38039268b773963ee363bce2e60bbf69dd63775fec639ea30f83a5d29b1b863804abbd351f5b9550fcaba75fa9c3a7a79293bc40ae7b96cf8ddba519fc2ba5b1a0239fcf6e7ba1fa4d3b9177ab1baaf032ebbce30ffbb5ef2d6bb65c4243bbdd420bbcaa134bb734ed1bb0f0e11bb3b58403b9ba1073a975ed1bb27f5823b286a283b51aec9bbd75ccdbbaf36d3bbf7f7403b374ec6bb2869babb6a54a1bb5d83363be7c2b9ba8f5cb33add75123b822e5f3b8c07273b30b64cba8ac8813b48bb0c3b9bac103c88940d3e4fb6a23d0a14a23dfaff75bf6a8d0d3e87ab0b3e4804963d412e093e4d665dbf2249453ee7cbf03da3c5c43d57bcbf3db22e74bf48b6f03db136ed3df22db03d270ce83d304c5bbf80242f3ef986063e51aba33d6a999f3de08774bfcb7f063e93aa043eede49d3d1e10023e5ae05bbff8c73e3e3018073ecd97b03dac9eb03decf674bf3727073e2211053ea2189e3dbaa0023eb4175cbf53ba403e1e09fa3d455bb93d2154b03de7c572bf580efa3db05ef63de3eeae3d8a3cf13d1a475abf35c8343e0ca2fa3dababba3d9928bc3d51d675bfa7a3fa3d3ffff63d4488ae3d1cf2f13d581c5dbfbd88343ec412083ea1cbb73d538fb53daf6f77bf390b083eb531063eafbca83deb95033e17595ebf058d403e190c073ee620b43d33e1ae3d9b8975bfa3fc063e303a053e8ae4a63dae96023e72bb5cbf836f3e3e29bd033eabb3983d1162913d97e470bf32c7033ee1dc013e4cc78c3dcec2fe3da50d58bfd5823b3e29e5fe3dc81aa23d0ab4a03d35a670bf8edafe3de34afb3d6325903dd738f63d09d857bf80e9353e3fd4fc3d94a1be3d8787bd3d1d9472bf7fd1fc3d961df93dafe0b23de500f43d83e759bff470363e3f59053e1b27ac3d3956a53d5a7a75bff85b053e8680033e64c39d3d7a00013e7eb65cbfd6653c3e7f25013e71c2c53de6dcc33d93f973bf5040013e2660fe3da917b83d2e5bf93d1f1c5bbf00803a3e7499bf3b1bb6e5bb96b7aabbda10ca3b0abac23baecbba3bab92cfbbcf25c03b5420bb3bbd8e023c2fff0e3c8ee55dbb8835dbbb64b47e3abae6103ccf580c3c56adb5baf5ce0f3c0c9aefb60d7b373ca97a813b0acc713bebf6a4ba9e00343b5f24843bb59a793be03f143bbab8823b0ddf013bfdf9cd3b58a4a83bc77f483b8601b93a12a2c93aa8dcab3b0bfba33b3c074dbabbb1a93b7df30a3b4ea3f13b01e4e13b12fb063a5a0699392e419d3b12a4e63b06a0dc3b3b07d63969c8e33b9608943b7c75193c51865d3b6c34adbbcab1a8bb350f8f3b83ba633b578a563b4e742fbbf0145f3b1d9f953b8c91b43bc5da943cad5398399afc28bbcd052cbc0be9953cce83933c7c43ebbb10f1943cec2426bc2388ac3c679f0d3b67e9fe3a1a00523b3d17e13be276163bd877043b9144c139cb43133bf830c43bd89d923b0904843ae3ec94bb5e295dbb8df2443cf3a9903a5f026d3a83c23dbb6d978e3aa9a13d3c91653d3b4930123b1180bd3baef8943bcf4eed3bb070193b2845073bc8b0893b9499133bc5cdea3b7f53a33b31adc83bef9a85bca64f9bbc194c8c3a74ffcb3b97e7c43b5ce78dbc2a81cb3b161e9e39b5f2043c9682413c6ab4e6bb2112efbb65b914bbbd69433c56bf3f3cf7f9b1ba25a5423cbfb2f3bab50a603ce46c80bb44854bbb83a110bbeec25a3cab637bbb29d984bb627499ba73107fbb0b524c3ce71f02bb13bab8bb9766553bd73a183b50595b3cf0c0b4bbdec0bcbb2625fd3959fbb5bbfbb6573c46a968bba1d02fba6a41d8bb4c1190bbf169e83b4c2116ba9ee052baaf7ef2b9bf4625ba49f4df3bc221cd3a2c7d883b3143a63b6d370e3c180fca3b8b428c3bf809833b77e2823bd202893b1c2cb53b6815e23ba778e63b2f21053becf17c3be36e553bf41ceb3bcd45e23b25456c3b8bbce73be000293b4e0c1a3c2530883bf60ea03b94a30a3b7d01cf3b86eb8b3baa29843bf577c53b936b8a3b5189b53b32a1d63b6a99d83ba2ce993bc8a1953ba1ead43a3297dc3b012ed53b2a51b53b28e6da3b0129a73af9290d3ccc7b853bd95c03bb3c765ebb33f0d03a49a6893b2f03823b4ce7253a217c883b0caec13aae2cd13b8d1b603bc365ec39cb46103bb6f3633b92e7693bee64553b9c2c093cbc13633bf88f3a3b741ac83b8a8b52ba368b8bbbd6f896bb33c0273cfe4330ba038783baf3e907bc715349ba4e27353c40dee23a811f003ecd62be3da6b3b93dd7fa74bfec25003e5295fc3db575b13dd58af73d7d215cbf81ae373ef9aa023eaa9bbc3df327ba3db44475bfb9ac023ef0d1003e9511a93d0681fc3ddc3d5cbf0e5c3a3eaafb023eb1a7a73d31fea33db31373bf7cf1023e5f1e013ed93c9c3df120fd3d3aac5abf2bff393e82b3f43dd8d1a83dec3fac3d429b70bf62b1f43de8fbf03de121a03d0605ec3de2d657bf07a1313e80c5f33dd1b4ca3dae97cc3d051174bf28daf33d0df6ef3d79b3b73d1b1beb3d9c555bbf2b74323e0466033e2b77a13d4719a43da5e972bf8061033e9a8e013eb7769d3df0ebfd3d1a505abf5aef3a3e50f8003eb9c3b13d102da43dc8be73bfa1f7003e8f37fe3d613a9e3d3917f93d262d5bbf6783383e2148043e5302bc3d4996b23ddecd75bf693b043e6172023e406bae3d03bcff3d09405dbf5d8f3b3eb47c023e2b1dae3d7481ad3dcd4c73bf667a023e809f003e0b81a23df034fc3dddd45abf7c343a3ec804023e98f6b43de6d2b33d184475bff509023e6429003e60cda63da732fb3d4f9b5cbfa9213a3ed051063e21f4b33d04e2af3d71ed74bfa544063ec878043e1511a83d5de6013e643f5cbf26763d3ec00bff3d415bab3d4214ad3d741674bf3df9fe3d4769fb3d4ddf9f3df831f63d85285bbfa025373ece7c063e0baaab3dd33caa3d53a073bf647d063e51a6043e7357a03db20f023ef0cc5abf975e3e3ef8920f3c95d0b6bbbff0e5bb0c9e163b3df1113c66d00c3cfa1821bcbe1c113c559b0f3b47db353cbf550c3ce42154bcffd151bc4591c43afb110e3c6d7b0a3c8afaa8bb3c130d3cc32f8b3a3a1a333c400beeb92f618bbabf0e0c3bc8d7df3bf79ec5b9b20511baaf69b439ed01dab98ebae33b89a6b93aa0a2b33b546f69bb10bfddbb6144a83b0af6b63b412baf3b737531bcbb69b43bb5f2b13b57f7fd3b2a2fa93aeabbea3b8de8bd3bdaf6d03be14abb3a690e963a43d6063b9808b23a54b6ce3b8cb06c3b964caaba8824993aba5c343b2b7dd43b5b479cbadb0fbaba517f0bba43899fba075eba3b3dae8a3a8aa5103c06aea7bb685891bb9b02a73a95b5123c8d930d3ca0f1d0bb5df2103c87389c3adc29403c1a92c4ba353739bba14e29bb463d233ca102b1ba9916dbbaedacedba6ff7bbba10bf1c3c9ff2ab3a75bedc3b04a9963afa578a3ae525cf39a3cbe13b91ecd63b8a76c1393e60df3b831437b8ad4d183ccc69573b9fcd8cbb48054eb9fd6e843b3a8a633b49d24b3b6af01e3b37395c3b2cf3883b7c48c33b441e0f38a23d253b10b9f43b5d47093cedf0ff380178bcb83260fc3ad391333891bd043c6b531d3b5953a13af19bca3a5687eaba202e293c31a3b33a4bcb883a1a479a3a33359f3af6901d3cc92a883b7d2c4c3c8d8047bca2e65fbce1ff1dbb3ebf4e3cc2d9493c7aa532bcf2eb4c3c657f42bb85767d3c4a3b89babf7f0b3b46413b3b37d4dc3b07ee7eba1fcf97bab7c4c7bab0d780ba983ec33b0e7c3e3af1f2cc3b851c9aba1ded8db95267a43b52ffd03b1956c63b5e88db3a38afcc3b9aa79b3be1c2173ce20e343c21b608bb2f4ed33a5b1ec7397dc7353cf890313c28962dbbedd0343cf925b5b907ea5d3cc53cae3a39823b3b097f83b919fa903b6cd2ba3a14349b3a63fdfbbabd00b23a3011953b10ce6e3bca1a2d3b6b8eddbbea49d0bbe05bfb3ba525353b9e5b213bc02fb7bbb869323bd82ff13bc280923b5483c83aeaf0b8b8152f4c3b328d1b3c28b3d43ad7d9b93af17527b9716fc73aa4850a3cd3e7673b3d15293b38b3aeba170603bbb2d2d03b5ae52f3b13bc223bc4bb70390bf82c3b275cc43b3b1ba03b277682ba0a9828bbb53b23b9a121063c8eee60ba373b91ba62a5b5bb461170ba73b8fd3bcb71853a6c28ab3bc2190abb79dd84bb3a43b43b961eae3bf299a63b710736bccbd3aa3b6fdca43b603cfd3bd2f2023ee53eb23d4d2eb23d584874bf59f8023e9410013e5ed6a83d0606fd3de8995bbf9e2f3b3efaf2083eb693ab3d7970b13d6cc875bf40f5083e2607073efc2ca63dab91043ea50a5dbff70d413e7d81f73d7c49bc3d8e99ba3d04fb73bf286cf73d18eff33d4ff3af3d5ed3ee3dd8425bbfa044323eb8cdfd3ddbd4b53d559ab13dd32074bf8eb4fd3dd039fa3deaf5aa3dd532f53df3335bbf2228353e6277053e82c7bc3d2bc1bc3d52e276bfa367053e81a0033ef04ca83d23fe003e7edd5dbf809d3c3e3b29063e8f53bc3db65bba3d36a275bf972d063e6d4e043e3cc9ad3d8fc6013e42d45cbf1e433e3e7000f73d8760b53daa4ab13d941574bf5decf63d8f51f33da35da23de53fee3d241d5bbf3192323e1ddf033ea308b53db4f5b33d86b873bf47e3033e9afc013ee644a93d90dafe3d85e25abfc70d3c3e0027013e0080b33d771bb43d853775bfba23013e1b78fe3dbe92ab3dd865f93dcd255cbfcfc3393ed2d5043e0cbdb03d5539a93d61f674bff1cb043e44e7023ef6e3ac3d0e5f003eb5105cbf9fe83c3ef012063ee9efa03d27f19b3decaa73bff90b063e1c2f043ef3f78d3d9092013e94c25abf50bd3e3e09ef043eb887b43d9f88ae3d285c74bf43f1043ece10033efc30a43d9b8a003eac995bbf87e13c3ed479033e8d19af3d9721b43d92e574bfbc76033e369d013ef78ea73d8119fe3d48515cbf8e803b3eec70c13b7c9cabb92601f8bb8257bd3a5cc7c43b4721be3b2ff78fbbad90c33be0cb973a2bc5fd3b355b2e3c5b4b4c3bbcef153b99a1c93a5fb9303c250a2b3cf7851fbba5422e3ccfbc203b3a7a643c95f36d3b30c732bbefa2b2baacf3ea3bc0e1743b5c9d653b2bae91bb8e03713b6f6ae33be058bb3b473bb83b01e6bb3a92b4cc3a924e72baee08bd3ba44cb33b9fef62390632ba3bb9d1abba549a0b3c079eaa3b68979dbb74c0b8bb288f9f3b3501ad3be7d1a73b5e5ea9bb31a7ab3b24d19e3bab10df3bdc04183bdc51dabbdf1cbabb6344d63b7b4c1e3bb55b0f3bb619a8ba3eab1b3b2969c83bb58e8c3bcf239c3b8ee7c4bb5dd03cbb498ebd3bb6b79f3bc0d2983b2b8532bcde5a9d3b22fbb33b21c2e53b2f62363bb052b237b1e064bb159fa03b22ce3b3b33fe2e3bb1c6d0bb62d9383b51228b3b883ea23bdd8a63bbf5a0423b6fa3fdbabbbf183c3af35bbbc7386cbb549a8bba3bc05fbbf4c0153c96f9b2bafedcd33a6d1c7cbb07c983bbda83063c7530e53aafbbbd3ac0dba3b8da36df3a0f59f83b8187823bb2368c3b42de17bcd81350bc7a35b63bfde98f3b349b873b6d9d16bcd2e98e3b34bdba3b134cd83b61c784baf9c30ebb45b93ebbfce42b3cae786dbac44095ba6c89b53a8faa75bac3f91a3c8e8b9c3aa8c25f3b1f6c42badf4c04bb8a4d7b3b64cc673b44a1573b5c19103ac9e2633bd63f373b9ff7b03b9971253be5cc0cbb3e18bbbb8177e43b276a2c3bf22e1c3bc2f81fbb0354283b837bc93b68bc993bff264f3b143996bc3e6190bcd15a503bd928593badc0463bc0a391bc3654553b400c523bb3d5b93b7625ba3bfb065f39130f21bb9d6b473b18b4be3b6890b43b5c0d51ba4099bb3b0c98263b4ca9043cb795cf3bb55a74396aef19ba180807b9e862d33b4191ca3bd337813b60c6d03be2f00539466e103c22a3ca3b64a9dabb1e99f8bb4c7e823b4c4ccf3b6a71c63b482af7bb66c5cb3b29c57c3bb58f0b3c45f7033b811dac3a898f8d3a7736f43b68fd0c3b2052f43a2b56733baa38063b3aace73b01a88c3b237db23b70f5ffba05c6083bf023623bd2e8b63bb1f9ac3becc802bb5a86b33b562c483bd4140a3cf19cbf3b4bffb93bb8fdc33b16fd813b737cc63b1e78b93bab26c43b16d0c13b630e5f3b0ad5153c978b3abb16cfabb9e21155bb199d553c23f334bb673144bbd8b32fbbd08f36bb689a4b3c15b036ba78a1013edddfab3d351fad3d0ac672bf73a8013eb283ff3d8a3a9f3d2776fa3dca495abfdb61393e7d28013eb0ceac3dac4aad3db64f73bf661c013e6e99fe3d4d31a43def7cf93dca5b5abfc006383e34dd013ebf17c33dcecec13deb5075bff2d5013ea4f5ff3ddd5bb13dbee1fa3d40a25cbf720a3a3e2aa8fa3d34a5a83d5089aa3d6fa672bf7192fa3da5f1f63d4bbd993d11c7f13d83bc59bffb8d343ed1c4093e9933b23dd131b03d703776bfdbbd093e4def073eaf1fa43db867053ed3e65cbf83a5403e6b90f63d8974d63d1968cd3d6dea74bf5297f63d4aebf23daf09c63d72c2ed3d9d025cbfbd2d333e48a5003ec3aaaa3dcfdea93d56b173bf219a003e9f8afd3d361d9f3dda55f83da8e95abf79ed383ee87aff3d9f10b13d4c19ad3d751d76bffe5bff3dd9f0fb3ddb3aa93debd0f63de12f5dbf83f6353e0990023ec7bbb73d28cdb83d0c1875bfbb96023e29b5003e41b8a93dd260fc3db8745cbfb51a3a3eae10023ed7c3ba3d7c22b73d27de74bf3e1e023e0a1a003e617db03dd349fb3d69e05bbfa2a53a3ed69c073e1f70aa3de8a9af3d02ab75bf4297073eefc3053e78b7a33d9623033ed9da5cbff6c03f3e2a29033ef596ae3de4d2ab3df56076bf8e2b033e784b013ed839a73dd075fd3d729a5dbf88423b3e7ad0f93ddb4da53df487a63dd17b71bf69d0f93db918f63dba3f9a3d0d17f13d429a58bf1e60343e3df42f3c91bf773b7f3643bafafd76bbf5f0313ccb122e3c0830e3b9cffe303cba9b76bbbdbf4c3c15b33db9c3e223ba2090713bd6d2fb3b3537afb87fdbacb9d224ceba79d906b9b642ed3b3bee083becb60d3c23d0773aa15b253b76feb33a7cca0f3c77c30a3c0fdff83a614a0d3cb94f4f3a10a43f3c5055bd3be6f990bb09475cbafbb8ac3bba34c13b203db83bb929eaba83d5bf3b2e8fa83b243a0b3cabab3abab8dd95bafe9145bbfcba233c7bea13ba43ed5bbaf9bbb53a126320ba68a5143c21f1be3aede6ebb9f5de19bb988646ba2ddb213cc7d3a0b9a7de20bae05d2eb7d452c5b99ed92c3c57db163be56ad93bf9f600bcedceb5bb99f0113b358edc3b7638d43b3af846bb0b03da3b7aa0ef3a2100153cfd1f1d3bc791f73948e45dbb0a28093c5dcc263bbc06123bd9f828bbd29b213bc69dfd3b79869c3b6d6da03b721596bb4db0d9b8dbbf8e3bbbeba23baffa9a3baeb182ba471ea03b3241873b025cec3bb68ac7bada2a04bba8bf8abb81e3463cb2a7b6bafb27d8bae740d9ba9322bebad8633b3ccc4a1e3a0524e2ba95bf1dbb63c7a63a49001a3c3d8ad0ba54a8f3ba85d7933a21d6d5bad66e163cf33a363aa129c03b7fc0883aacf6513a5f52bd3a059dc33b3400bd3b38e4283bec21c23b0da1163a3821fb3b88bc91b80da9d73b00c0f43b38d4fe3b11b48938f2133db902c4a5bae5a34d37d493f13be6e30e3ba3ab2d3c91d797bc6cd7a8bc5bb40abbdfa72f3c23af2b3c4fba74bca60b2f3c094e1ebb8bd4513cbb3e963b30e90dbb3ea20e3bf17a383bf186993b4b0d923b5e421abb8c62983b90eb2e3be7ade33be511c73b8de1483b968bbb3b23c41e3a443ccc3bed9dc23be91f2f3b838fc93ba809efb841360e3ceeee8a3ac3d726bc841019bc8051353ccd78983ae5fc763a05452fbc3bfc8c3ad8292c3c78d6623b7e7ec7ba16e0913b4369b6bbf217633cdaf7b1ba9e0bdeba3b8673bb12c6bfbacd605e3c4eeab53a29cc023ce09bdcbaa0f4873b6f9097b836bb043c8673003c787f733a7eab033cf7fd3db9fe102c3c8a89333bd9a090bba47bd6bba4a9db3ba1ef3a3b7a6d2b3bc2e707bc6d6d393b3475d23b4702a03b04935bba17742d3b6f313e3bfa03023c71e13ebabf8982ba49f9303bbb5c59bad6a90c3c7a80e63a30def33aa84bde3a88140c3b7001403c8544013ba07cdf3ae24ee5ba549afe3a01773b3ccf97843b153a093e6f41b43d42d2ac3dd6eb75bf193b093e095b073e9d95a43d91cd043e7d175dbfb143413e3997fe3d3ae0b63d455cb63de73775bf0e7bfe3d79d0fa3da5acac3deaadf53d859f5cbf5883373e7049053e9f6fb23d16a3b63d12a974bf8751053ef55b033e6dd4aa3d97e2003e85cf5bbf1aea3d3edb85003e23b9bf3da44cbd3d9b1275bfca88003e2e66fd3d8ad4b43d9771f83d35475cbf3b26383e13990a3ea1b0b23da09db03dce5675bf3b970a3e91a4083eb012a93d7814063e1a705cbf4548443e1aecf43d4febae3d1055ac3db66571bfdddef43d9c3bf13d8a4da73df9ffeb3dfa8d58bfb144323edea40b3e1d5eaa3d265ba33d4a3f75bf58a70b3e8bb4093e92f69d3dd52b073e68995cbf966b443efc83033e18bfa93db02faf3dcdac73bffa7e033e47ae013eeaa4a53dc152fe3d2d275bbfea583a3ef8a7083ee983a13d57369f3d716a74bffb9a083ec2d2063ecff0953d2b38043e69b05bbf6e03403e56d1043e8163c53d5dc4c73d3fb176bf7acb043e8bf3023e8563b53dec55003e60255ebfe36c3d3e2a9f063e5e77b33d85beb13d1c5076bf43a5063e5fc1043e127ca43d8d38023ed7b75dbf414c3e3ed3effa3d10adae3dbfd7aa3d948b72bf6eedfa3d515bf73d2d01a83df64af23d25bd59bf5c83343e3b4e003e6963b83dd96bbb3dee0575bf514e003e98cefc3dc21da93d90b8f73de13b5cbfcaed383e243e4e3a83ceb03bbe4b5e3bea0f103c6406763ac8262d3a7eaf733b65c86c3acefe013c4e01193bbed0e33acd0682bb9f6eb5bb2abbfd3bf0cbeb3a8c34dd3a002d60bb460bee3af31ce53b09d42d3b3476613c02e289bb825438bbe6c7a2bbb52c633c9cab5e3c6affccba3db0613c8eb6a2bb85ab833c8439983b9ceb41ba2c5e063b0f0a883af3879b3be941943bf28a743b2ba2983b2c9ea43a1329de3b6763833ac1920e3b40b28bb91f5b1c3c5583913a8bf55f3a3859a83a32aa843ad2841a3c864b603b0f6c9cba8c324ebb00d692bb5d7ad83bb6dd8cba5d87aeba702097bbdd0c92baaff0be3b920e493a1f84d03b12d116bb0f0243bb3f50283b56b0d43b03c9cb3baee6013a2227d23bcd280f3b38e5103c5ff3b4b9b7348c3a7daedd3af95f1f3cbddd81b96799f7b958656f3a95609db96685143c3dd20b3b6baf093abbf0943acb65cf3a7cfab13b5cd3323a1ca6af3940c164bbb874173aebd1bd3b5f95633b3e584a3a721389bb3d428cbb85f4223c82f66b3a1be61c3a9d46cbbb5eac593a64211a3c47ab533b5a9ccc3b387a2bbcd91e61bc75c3bf3b101ad13b6442c83b64932abc324bce3b23b1ab3b76070b3c40929e396191cbbbcf6197bb7c58983be966cd39cb6e3739a3c269bbb33bb13908cd933beb95263b41bb843b22b876bbff3712bc02b78e3bcb03893b404d803b2ad763bbcf02863b5c0a7c3b2178db3ba495be3af952f43bb0cba43bd11ee13bf5fbcf3a19d1ad3a8a62f33b73fac83ab7cabf3b5e937f3b1eabfc3a2e86ec3a62929a3ad08cb93b46d8053b808ced3a5b9fdabafb01043bfc79b63b04e9653b4f976f3b6fc7b8b90efa173a2deb313c8eca763b1e69673bee6cacba3b01723bc15b1e3c9809bb3b7e0f293b63f98539cc9c42b9a0543f3baf282f3bc766203b457290bb978a2d3b61d1353bf067913b3e7ce8b96f43c5b9008ec1ba21d4343c17d39eb96cb915ba7b3c773b58ddcab9e0e8223cfd6a073bdc4fea3876ecfbbb5cfb83ba34df043c9ee775391ca60ab7131aefbb52376239670ff93b02b5f23a359de63b8fd483bc404546bc9eacb33a91a3ea3bde9fe13b7c7558bc756ce73b57e4b93af6891a3c4a08553a5f1145bbdf0058bb133f103cc90b763af5c62e3a336b8abbefe66c3adc71113c05f0303b8042f43b5640d53a2c2287bba1ffd83aa7def83b89ccee3b8d07c2bb7edef63beedeb73a2765233c6bf9043ec1f6aa3ddd34aa3d8d0375bf00f7043e831c033e2758a03da18f003e132d5cbf99283d3e6cb00c3e5d88aa3d1229a83d5bd875bfd8a90c3e6dc80a3e0e539e3d0f2b083ebb005dbf7302463eba660b3e50d9ab3d3474aa3d533076bfce660b3e628e093e7449a23d0eff063e605b5dbf9af1423e994f043eb85bb63d8679b73d21c875bfc649043e8564023e5ff1a83db0bcff3d3fac5cbfc2cc3c3e46a6f83d1d52c93d0862ca3d34b072bf72a0f83d9dd1f43ddbbabf3dcec6ef3dc7195abf5010353e7a27fb3d5610a33d1599a53d7c6672bf7016fb3dfc81f73ddc5da03d1a6ef23de48c59bffd91343e373a023e2a17b53d19c4af3d3b1875bffe29023eb462003ee8a3a33d1588fb3d3b365cbf7264393e02b6003eb0aebb3d7bc7ba3d244674bf04a9003ed2b2fd3d2fa5af3dc390f83d084f5bbfd20d383e672b093e88eca23db9a1a23db6ad73bf7136093ede35073ecac6983d74a6043ea4d15abf92f2423e5f96043e90ddaf3d9ff6a73d8a3774bfd88b043e30b7023ebe8da33d7420003ed36a5bbf8eec3b3efe54f63dd308b03d15c9ad3d914871bf4338f63dc0a4f23d18b6a53d886aed3d386758bff19d323e3d63043ea233a03d0a899d3d246073bfcd68043eac8b023ea93a933d46faff3dd8595abf85f53b3ea279063eeb26b33da781af3dc4c575bf3881063ea296043e854aa83de41e023e21275dbff1623e3e597ba03992dabfbabe63f53a5c12093c0d8ccf39a2e92139865d853bec73aa3906d3043cc27f343b3853a83ba05a2dbb9027b1bb12d51a3bac42ad3b0b7fa43b3dbebdbb51c4a93b70b1003babe2f93bf094083caa343fbb9061b9babe657dbb46890b3c0cdd053cefc34abc29310a3cc57b9bbb4fe6333ca86fda3bead4aa3b0fa6433beb18183b89fadc3bd7aad63bcb78e13b9df6d93bd935103b654f113c8827953bd69ef9ba207d1ebb3a8cc43b91d1993b186b903bd2cdfbb8bc01963bf8c3b33b33e3f33bbe24653b2dc932bc1e60b4bcacd4c23bb6296b3b611e5d3b44c350bc4649653b3be2c33bfcbfbe3b26dace3bc91340ba649878bb662bf93be23fd13b2c7ec93b8fa897bb4123ce3b67e5e23b2c44173c5687743b48b1a7bb83ceb2bbb811143b15607b3b59386d3b0bc3943a7eab763b66dba53aa702c83b0ed7b73a3643ec395c09463bed1a943b84ebc63a1196a13aa375b9bad1bbbd3a6658953bac348c3bf012e43be4b2bd3ad568303b96bd29bab7dae73bf871df3b014e0c3b9b74e53bb785ecb7d0e1143cd880bc3a3448f2ba9b6686bb5263d93be599cd3adc33aa3ab1b753bb2d63c83a885ec43bc965553ba5270f3c0f55f8ba7a15c9ba9c278e3a23d1113c33240c3c999454bb77b5103c145ffc39c2f33a3c8a519c3b82ab91bbd36689bb3ee0df3b6403a23b726c973b3ecbffbbbd819e3b0d56cf3b71eff83b6780033c2a410db9b4011dbb2018443ab13c053c8278013c6c0640b98d5f043c815b783901e0253cfd18453b086ac7b8ea87dbbadc02753bf0ed4d3b89333d3b9b1d2d3bc3524b3be8ef793b2582b33bf7ae3bbb54aa923be739893a7ddb223caddc36bbc0c040bb08603d3b41703abb52fe1e3c8546a6baf29d063bb64c053aff4e753b5115c73a71e40e3b4707f73aed95823b6cba0a3b2cd4973aa2d88d3b11e6a43a9c06a03a5f55353a9573003cd117b13a0e05953abfd32ab919c3ad3a5e2f003cbd642a3bfffc5fbbd4ee92ba02443bbbaacd563c138d57bbd39f68bb9d730dbbe1ed5bbb1da6483ca1db3cba2fd82a3b0e8815bc484be5bb8db1ba3bd11e353b4bcc1d3b1f965fbbf5382c3b8eddb83b309ebf3b0b2f8b3bb51802bc66b41dbb2c27c23b641f903bb622863b869bcbbb781a8d3bd3d5b13b593aec3bed7a0b3ac74a75bb05c87dba9bd78c3b89362c3a1d05c6391dfb31bbdb41183a152a953b4340293b423dfb3d5364b03de105ac3d8eba73bf3338fb3ded9bf73dd26ca13d8f9ef23d48465bbf9e96343e6d260a3ea836a73d0b71a73dcad174bf8f1e0a3ed432083e77cc983d33af053ea12c5cbff226433e0409043e4b99a53d2a3ca83da9c072bf0209043e0228023eeaeb993d5226ff3d3d615abf96443c3e8ba8023e951caf3d69c4b13dc11675bf6c9d023e3acc003ec554a53d157ffc3d42055cbfa9793a3e34ba023e8e20a73d3d33ac3dc5a272bfefb9023e5ad8003e14419b3db6a8fc3d9f295abf4c743a3eff14033e04ebb13d923ab33d70ec73bfc40c033e8939013e7c6aa53d736afd3def1e5bbf14a0393e4f76023e961da73d1caca63de25073bfaf82023e507e003e480e9a3d8b0dfc3def605abf05e53b3ecc06fd3d046ab53d520cb23d6f9473bf6807fd3da867f93da4a0ab3d2467f43df9cb5abf882f353ec03a063ebafead3d5016b13dbfa074bfd93d063e2765043ec1aaa83d4de1013e06f75bbf84c03d3e3c76ff3d990bb23dd4d3ac3dba9f72bfa66bff3d6db2fb3dedd2a73d3870f63d83025abf7d8f383e42fd003ec415ac3d17bdb33db0a373bf80fe003e3e3afe3d5b8da63d5c25f93d10e65abf4ffa383e60c9fc3de31cad3d47b9ab3d3f0c72bf91aefc3d7c26f93df49fa13dce22f43d393c59bfe3bf343e3cb8003e8bd7b83d10fcb43da52073bf08b3003ed2bffd3dfc8fa93de59df83d504e5abfcdff373e1d2dac3b1199bdbacea6ac3b16798e3a708faf3b091fa83be320283b370aae3b205def3a2e56e43b9070923bbdd02bbbfef605bc9965583b1842953bade78c3b86e50abbc564923bf730633b39a7e73bb04e693b779e49ba768a2ebab3007e3b58cc6f3b7dba623b932a07b9cbf16e3b923f4e3bfa57b33b1310af3b1a1af2ba1a159fbb6c75713b8ac0b43b0b38aa3b7110823b0a6eb23b80c6483bb2cafe3b3ed7c53b5d28cfbadf5e4fbb76ae70bacfa5c93b64bfc23b413a19bb793bc63b16b372ba7d44053c53ce883af3ab8c3aa65536b7aad9973b469d9a3a5dd56f3ae085f6ba89b3903a3a13853b01984b3b6e5ed93bc4d129bc003091bbaa8d453b549ade3b4236d43b72ccfbba48b7db3bff91673baa8d163ccdaa543bd80f773bb4291c3b0ce5f13b0a4d5c3b98304c3ba36f16b9e5cf593b5851fe3baf04b53bf92eb4ba7d54453c9d58113cabdf043c169b9dba8f95c2bae77e623c514ba5babb90f23b5cef5c3adc68353b1211863b779f15ba33e3fb3a0f713c3b0f0d2b3bd4a7c9ba5ca6383bb46efa3a0e61ac3bb6c62e3c24d61cbb4abf1a3be75835396fc1303c6bf22b3c2abe35b969802f3c3c6ecfb76ec0593c49a07e3b3746a2ba42fe4fbbac4ca83b8be0823b5e78753b95932b3b9825813b44e9833bddccc43b5a8e90ba963c2fbc850afcbb145e213c408284baa2399fba7455b5bba9d98dbadef6203cef7e643a1e49e13b5e25893b6192f43b5b0dfb3ac0dde53b2c61dd3bce32383b188ce23b46658a3ad821163cc14791b9caf12a3b09a8faba136f8b3bdbee1fb94ca7ccb99114e839a10a49b9860a853b8064dd3ace28573be0e642bcc0c53fbcdaa89e3baaf1623bfe7f4b3b4ca01bbc154f5c3bc0aeb23bab77ce3b84b0b53bd169aabbe4c528bbba392a3b00feb83b3c3db03b3f1c72bb1c4bb63b4367f53a0a6c053c4f22c0ba08cc44bb7a200abc40d50d3ced19b2ba4222d0ba9c8f9ebbc6f8babaff090a3cd0c6893a344aedb8dfe64ebb1da631ba0ce11e3cab7803381a5996b91e0f80bbc2058bb87a10173c797e223b0e20c5bb980c403be26a453b9fba6c3c95d2c2bb97aac8bbf658c1badcb9c4bbaec46b3c5a6677bb4930bf3a28d3d1baa01c90baddc7e43a638ec63a8f72b13a87ca9dba29d6c53a5607cc3ab1251a3b8796713b0ef70dbc90af0fbc3484e43b24e4783b2611693b44b200bc2da4723b356beb3b448dd33b4a30013e6faab73def8ab83d321d74bf292a013e53aefe3d9675a93d0575f93de52d5bbfb729393ec37ef33dc703bf3deefec23dec3374bf9c7af33d9bccef3d5efcb63df4bdea3db32c5bbf7713313e7f35053e3c47a03d2ddda73d6f5076bf8b32053ed45f033e1100963d28d3003e3b705dbfcbea3c3e57cc033e5720b83d8a03b43d7bbb73bfb9d1033e54de013e2316aa3db7a9fe3d4d315bbf0e023d3e27b3083eb567b23d3e3cac3de33475bf91b6083eb0d2063e6ef9973dc94d043e09745cbf5346403e347d023efdbca93db468a83dd1de73bf9d81023e00a0003e4a3f9d3d4d3ffc3d11285bbf334a3a3ef805063e3399973d4f7c953db45b73bf4bfa053ed927043e4f538c3d1d98013ea63c5abf28223d3e7ef4063e919ea33d80c6a53d7dee73bf74fa063e5911053e62109a3dfc84023ec00c5bbffb1b3f3e2f16043ef8bbbc3dbc02b63d064375bfd01a043ed836023e837fae3d6d50ff3d64525cbfd3773c3eaa5c033e6466b13d40d7b13d3aa374bf3461033e1590013e5a40a43d6801fe3da8f85bbf002e3a3e8f4d003ed7d7ac3d05ffaa3d950372bf1945003e4ef9fc3d5e99a13da2d8f73dd96a59bf03cf373ecb34083ed8b7ae3de4f1af3d793176bf6e22083e525b063e851ca13d8ec8033ea3465dbffd5c3f3e3a6f023e5351b63d8160b83de36f74bfd276023e878a003ebfa0a83db315fc3d84a85bbf7a903a3e153764bbecac87bb3ae5e1bb5174443c5f4f5fbb7fbc6dbb873cbfba792063bb9142393c0714b1ba198b65baad220fbb775e6abb51b3d03b024f4eba408a7eba533f223bc6da55badcd9d23be740553a0631813b0f97ddbb6e2201bce329263cc8da863b1c39793bac44c9bbdab9843b704e193c9775d13b83d6b33b17cfa43a98b1d1b9d167bc3b567eb93bec68ae3b15a5dfba796eb53b5840ad3bdf9c093c1a9a293b246f0fb9cdc9ab3bd8be9f3b8d21323bbb5b203b4c4b983a6e4b2e3bac5c8c3b39a0a03b855406398e5db93a1108d8b955a29b3b7336923968421637e582243bedd15d395370953b907c233bc06d6b3bd3b2aa3a6ffa7b3bcef2b53b17c6703b426a5f3b25dd15b933376a3b41aeba3b5ceac53b136fbd3bcbada8bb99e44cbb2be39a3bcea1c23b38adb73baf3758bc8135bf3b66c4823b6c060a3c1120ff3a221386bb09a503bba338f93b00f4053b0266e93af8381fbc6486023bf385e83b0ddb953b262a173cd7bd83390fb0943bea5f413a1448183cd9c7153cc0e786398419183c7de0443a66062f3c4850733bded8aebbec91a5bb720caf3bff437a3b94d9693be2dca9b92b7a773b8c85a63b456dbd3b863c853bcc87f2ba4a5a9cbad696883bb804883b9cfc813b4c1debba7132873b62d27c3bcceac13b16caa1ba89b499ba85df22bbff563d3cef3b90ba3971b4ba5c04f2ba27a696ba663d393cca483b3a612cd03b41e9acbb1f0d0abcd1e1253b6dfad33b8403cb3bdda1ddba61bed03bcd3fe93a1839133cd060773bab4d3f3b1a60443b9bc7cc3b88d9803b4b4a6d3bdad54a3a3b4e7e3b7b7aba3b8406ce3bde1b243c478007bcb3a8f3bb987d0c38deb5253c4797213c1f12e4bb9b13253cf511b7b95506533cd8ca173c16a9243bd16b6d3bcae16fbbe23f193c8986163ceea7503b9b8f183c694c91bb7ba4303c4f672f3c56ccf9bafb92ffbb5a3436ba57d6303ca4722d3c037aa9bbda42303c625db9bac7b74c3ceab2083b02393d3b63ed2f3963c0053ca2b10d3bd5e0013b0a4b4b3bb36c083b63d2f03bd55e883bc94808bbedc1adba67bf37bbd84e1a3ce1f9f9baeea017bb9ffdfdba1cdd02bbe72d173c280e833a65c2173ab40a553903ad86bbd4230f3cb462443af4f5de3990a024b9490f293a11270f3cfca96d3b650cb43a52973f3ad3280b3b81c4d73bf47cbe3a2bd7a53ae50194bb0887b83a5d76d63bfc76503b573bf83d56c8b83d59f3b83ddb6c75bf9128f83de3a0f43d1c2daa3dd5abef3db47e5cbfcbbc323e5a31f73da6c8b33d809fb73d068872bfee35f73dd180f33ddda2a53d6b53ee3d74a359bfce5d333eef06033ee2f5bb3d30a7b33deb3e75bf07ff023ef92a013e5989af3d7b13fd3d675f5cbf56393b3e2e8f0a3ebd82ae3d26d4a93dfa4175bf128b0a3e5cb0083eadc0a03dc713063ec3775cbf69e5423e0062fb3defb8c63deb78c73d6e9074bf7545fb3d61b3f73d8621b93da583f23d68a55bbf481f353eb584f93d1fd5ae3d06bbb33d47c772bfe381f93d4be9f53dee8fa03dd8cbf03d351e5abf39d2333e6854053e426eae3dac79b13d27fc74bf524e053e5579033e922ba43d9bd3003eb5015cbf5eaf3d3e6dc5033ee956ad3d0024ad3da1b474bf46bb033e03e9013ecd31a53d669dfe3d1edb5bbf15ac3b3e08c7fb3d87bac43d8725c43dcf1075bf47cefb3d9d20f83d7830b33d7f08f33d553c5cbf036d353e2ea9fb3d8d8caa3de471a83de32a72bf629ffb3d14c6f73d6da4963d47b4f23da05759bf8487363e6b72013e9089b13dcb37ad3d69d473bfa068013ea234ff3d1b87a43d7efcf93dc0e45abf6881393ebaf6023ec465b13d3295b33daa3574bf2ff9023e9811013e062da13d8308fd3dc7df5abf47733b3eb21e053ea108ac3dba18aa3d0c7f73bf2319053e9748033eb420a13d02c0003e7ebc5abfe5bf3c3ed65c1f3c1520cfbb54ad0cbc4d9a893a20ed213c19941d3c9a0fd6bb90be203cd7cc043a29f1413c60c68b3b578ca63a34cf61b8841ad43b67d38e3b97ae883bad033cbba80c8d3b0ee4c13bcf74d73bb2755a3b18d28a3a94c969bbfb07843b83e2623b179c513b60302fbaa5b15d3b25c0813ba267c13bf5ed4a3b748d5a3bb441243b695a973b39d0533b7d953e3be344a63b54034d3bf386903ba753c03b5fbce03a500d29bbce44adbb83610e3c1f0ced3a0abac93a1061c4ba08a7e03a5156013ccdf4803b52dc3e3b4f4c10bcbb452bbcbb6db23b2449463be449333b7aefb0bb1b84423bf3f7b13b3a42ac3babe88d3b22478e3b616da13be2b8923b2a1c923b8e2e893b97948c3a66b18e3bda38913b26e8e63b94078fbb11da39bb75402ebb2a215d3cb1908abbe3ef92bb524e8ebb2f648cbb3dac513ce69714bbdc93003c88cbb23bc201033c5e10e139dac3023cc59dfb3b02eb2e3c737f013c88218939b3572d3cfce9d33b23e09abbb51e71baff02a53b022ad73b7802ce3b998600bbd2d5d43be04d8a3bc1d5153ce8aef4b9f19c043b148e0f3b28b6033cca69a0b9cd2814ba58aa6ebba35cb3b9925eee3bbcd7c83a93466d3b9478a9bbc45b29bbafe87a3bdb21753b933c653b4fe651ba778d713b21a87d3b66bab03bad86cb3b3d9cb6bb6fafd5bbe528ef3a59d4cd3bd2e5c63b768b0a3bef6ecb3b9d99ef3ab6eb063c8d4e293c8eb3673b237ed8b93078c8ba79902b3cda82263c4fcab8b9763a2a3c9ccde1ba160b543cad823a3b85d6c9b9ff62e8ba10ac013cb279423bf476303b37f1c4bb7a893d3b69abf03b9eb9b03bb16e103c15b471bb596884bbcd75853a9ba1113cfde10d3cf5691ebab7ac103c107bcc398e99313c92c87d3bbba974bc6ee358bc0258f3392087833b0b43743b36b442bc712f823b5b039db94d12cc3b2a5a553beee7383c8485233ce8530b3b787f5b3b8a024e3ba602a53b4b6b593bcf22143bf765a73b65abe73bd705f33938e9f038edd8183b1532eb3b2825e33b7315903b8968e73bca40233b11ad1d3cb819cf3b29b20fbba4c8dbba684c833b6328d33b1366c93b64de39bbeabdd03b8285683ba3ba0a3c4569b3bbef6253baf97d0b3b94a55a3c2d2baebb2c97b8bbd3ef343b4e93afbb45fa4f3c892e57bb350ebc3be37893bb00e836bb9f17fd3a7a41c13b1fddb63b087cadb9e05fbe3be451173bba0a0a3cc6eef73d348daf3da561b23dc6b173bf25ddf73ddc48f43dab41a83d0d3fef3dead85abf37ee323e19cd003e40f8a13d1120a43d162272bf8abc003efdecfd3dd0be973dafa6f83d922d59bff512383ecd59003e4720b03dc8ccb03dc6f472bfc86d003e59d0fc3d5730a63d82d4f73d912f5abfd3ed393e5ae8033eb8cca43d480ba63d2c0274bf6de7033e7b0d023e115c9a3d10fbfe3d38355bbf1ae33b3e1255ff3d843ba83d3e5caf3d206d74bfaf68ff3dee9efb3d6e4fa73db9b0f63d77af5bbf984f373e927cfc3dfc1fa13d0db8963d70d770bfe995fc3d68c5f83de7c5943d34d2f33d1d5b58bf3dee353ee305f13d7223b43d6efdb13d061f71bf05eff03de646ed3dd439aa3dc422e83d601c58bf693d303e2e6a023e755ec23d752ac13d8aaa76bf6f6b023ef48e003edf94b63dbdf9fb3dbfb45dbf29a53a3e9ad7013eae99b33d489aaf3dc0b574bf18d7013e78deff3de119a73dafc6fa3da1fc5bbfeaa83a3e4320ff3d3541be3d3d5dbc3dceed74bfb025ff3dac5bfb3de2aaad3d5f59f63d38fe5bbf678c373e893f083ece37a63d05429a3db92c73bf0444083e9d5e063e10df9a3d95d9033e56675abf838d403ef740fd3d8266b33d55e7ac3dbe9a73bffe2ffd3d499bf93de757a83dbd6af43dbef75abfefa6353e4ecd003e2266b13d7f79af3d027d73bfccc1003ed3e3fd3dd274a33d43cef83db99f5abf4f25383e99ca333b1da6873ad9c204392374503be3f63c3b807e2a3b1987813b1236383b3d204b3b616da53bd50cec3b0c4898bba18c2ebb9118903a897cf03bdfcce63bb38860bb97cfed3b93341f3aad351e3c7858a43bc64d89bb4fab4dbb5184903bf9fca63bcd62a13ba84161bb1d85a53b5323693b3b6de43b9d005a3babc4afb9715ae3ba5ca58a3b29e3603b38694f3b29530bbbbb55593bbf7b833b27a5c33b3d1b243c8635e5bb027382bb6b95a3396d5f263c96d9223ca3c3b1bb8f76253c82081039c3653c3c72c2723bb59703bb68326fbbb86a913b52ca793bdb8e683b49b3ddb97873743bef9c6e3bcd05cc3b3b8a353b02e0183b3d7e5a390fac993b5b783c3bc54e293b554575bb51c3363bce8a8e3b0aa3af3b153936ba3f0f573b2a08d3bad054333c725320ba783c5bbac7f78dba577d39bad564373c2bbdf63a9fa2583cc47f1dbaf3fc88baca2e41bb093c5a3cb067563cd8cdd2390c89583c84c855bb80d5803cb99bdc3a45394d3b11bbf13a9876173cc25af43a609fc83aeab4bb3bfcb9e13a4e681a3c2dd39c3b1ebbec3a4decad3acba2f03a59e3093c552ff83aea92db3ac977e13bab2fe83a26080b3cd77d8e3b9c75d73bbf812bbcdba95dbc6a77033bc789db3bd713d23b66c228bc9864d93b53fee83ac3e8113c9f118c3a0bc8d339c7a0303b81f9de3bcd229b3a86ae723abba947ba314c8d3a623de03b3e3e6b3b506e933b1ffde5ba8d4474ba577bcb3b38b9973be6508c3ba25ea339b3d2933b1d1bbf3ba345f33b248f99bb879233bb815cebbbf1aa4a3c913096bbbd8a9dbb0616fdbb4ab696bb54c9393ca6f841bbaec25aba24b2a33b7337d03b18730b3c85ad32ba844286ba23ea863bbfb13bbafd1d083c4d4edb3aac3d043c049420bb3dd91c3bbc14393b2a12063c1299013c2b7ecfba5bfa033c6b32223b3ccc303c7e8006ba987a8ebb735dfdbb44071e3c1993d5b9e9a929ba86e4be3bf00df0b997390e3c37cbf93ae73af73a16dceb3747e89ebb418cf43b8234053b028ae63acb3081bbc2b7003be054f33b189b8c3b654bdd3bdc4057bb929c9dbb28c1353aea6ae13b0c9cd83ba1ca0ebb1de0dd3b0ed15a3aad141b3c5a33c63b76033bb983564d3b17c14c3b6da5c93b7767c13b119e88bae819c63b36eb393b989b0c3cf0472e3c33485a3b25b68b3b5a8023ba4116303c84ea2b3c2bbff4393a672e3c79b045ba37525d3c3b78623fc7e48cbf44f88bbf413a62bd104e623fe25f5f3fe17f83bf63af5a3ff099c5bdfb82a03f7088663f03478dbf120e8cbffdb33dbd565d663f3464633f41b683bfcda05e3fb3b7b4bdbb47a33fae62673fbf458dbfe2488cbfa7ec37bd8f37673f7b3b643fbeaf83bfdb735f3f95dfb2bd21e4a33fb5cbc2be1f6fef3e5baced3e2d9fe63bc75fc2bea835c0be8428df3e7a80bbbe5ff4cd3b7bac0abff804c8bee92cf43e8069f23e4dacde3b2a93c7be0c78c5bec9dee33e68aac0bed5aac73bc5260dbfe5f7c3be724bf03ef086ee3ea23aff3be896c3be656bc1bee2f2df3e6bbcbcbe3743e53b38660bbfe2c7c4be3ed2f13ecf0df03eb7ee073c7354c4be683bc2be8e7ce13eef74bdbe3754f13b0d8e0bbf759fc4be3b99f03ed2d3ee3eae95be3bbf41c4be9b05c2be2d3ae03e1754bdbe946daa3beed70bbf77cdc3be91d9f13e5014f03ea804d53b5c58c3be1d3bc1be4e79e13e866fbcbe4152be3b450e0bbffb8ec1bebcd0ee3eae0bed3e96b02b3cde2ac1beaef3bebea772de3e053ebabef83a1a3c90110abf7b991440cfbf1ec005981dc0a8a82dc1e87c144080861240bdfd13c077610f401aef1bc1f11c54409c2f44bd9608803fdb1a7e3f2703813f10a857bdeca936bdaa7a6e3fa5b764bd8d2a743f650913be167021c0 \ No newline at end of file +363632656234393365653936383862396662623134303865623332333461363066633935643264653466326132626331353831633763636239616630353032653233326432366139383465346338376266393132363438333765656463366235613461313434346139333334396265306163333731646537376430643834323664623863366163343363643130313435636565623834363361316133393230363937653835653762353534626439663133633538623062353439646237616630333237666136663433386334626639643465303163653832333062643863333664336630376231643964316231663933656333386338656262303734376137313565643963396535653131303763646533393234333735613333633132353061393531333935623539643834373266303861633739373862366663376365383233326139383939363566373061373361613939336537366631353334346563313061373365663635633332663437653136383235343635623234366430373330366336363237623962656465373233346131343264313137653838643334616430346339363732613237623866636364313232613934343261643231386531356430343965303330306332326266336634626163333461643139653962326566303064343333623037313762303934626336363537616339343637306633633066333231613063623339333539376461316632653234353938616133616463623534356232346135666261616339646638373031356633306161626465643665633066616264623965656138613233353331303236363565616133323131653935643363353832366663633434626236376663386335666364333530336433353234383031636264353761616638663031613263353738326438656265623236653338323232386565626464393034633962373835363264656664616439353336623462376139333134656662373033626135336138333136643032636430653334303861616439333736306363383862306439623962646435383931613161656137356462666463346364333361623035396139323366643262383764363763323530613631653861653634666630333030316562323662346133633566316533346236663132363663663739623565366564343962313033386462653236663864613664303962623535363134336361326534666230613461643036356361623365363830623066373064386262326466363934623535633664373865313164633832393733616432306435663337366633306565386434613131613061636663616630643839623737366361623235653838653366303334613439393362313664373862653234396263323361663762626238373531633765333530666363393132333237616137663336333434326233313735303764323765346238313333393839646334396539663036633034396237393461336263306463333738313939303164393262336465656237616466643562303362333632303838336162666434636265363361333466646564333736303437383432623738636461313736336539396364306164393937313064353136643231663135306364643936646137386434613564373335643430363162623534623133366633353137633236613635643433306239653638666165343231636163376139643564653333336637393138356233633164623465396435396364353031663066643661353130633336303365313365323461343062346266323135653337393561396134306436306138343536666237623633633264313165383137303161323331653835616664346235646238316162633164643536643237313733326564303234356638363931626463356461643334373664366636333536303737623161613164363334613937633431303962353962373335343766393430633164633864323163306631383733383866326433613865656366656537666438363739303266643461633331626131633966646635316261613261343337626663303932613166373332653261386630363637363337343262303737306136653236653332663262653565343761333733303934663366396234313838633834336435633462656135613261346163346262626466376661633763653339623230363235373337373135343130663233646239373035346236656232373065666363666237636234313736663832363831343263656131366532386232316463393539383730306566316462386539616434346537653138343162313462313235636366333561313765386366666138663036383233626434366366383536653461663236336238616166393638626138353062346439616666626563343933666130666530613833336539653436336538376132636365306338633038613133636363613461326134313639623365396134633133666339346337613637616230393963653932636336386561343761626166313638386162386131373230326638646130353133633533353964323735303632336632343431363463396634353338346566303665373533363061373664346462643034663036336636303863636363373830663166313238363531353931346461393730353236373461643039653235366663353938646363643464333835646164353838623664643261303365363139346361393864383734623331623662306231653235333362633134303864306436613862616330613136316136393132323137343831363364646362313231303362316131313733366362386566353536356266663035356633313633323533383133633963343136313330346561336663653535616438303565656331373466386437396361363764323037616334366664653362323562366531643531633464616561653330386532663361613839666339613234623762623864303635363239323732633437313135623561393362653837653361396438373339666232653861316665613661613331383532616631393033383937333463353139343637613931376332666164316634363432366132316638656362363461336166626237613736336638306232313930636165346330323165386264366337316432613138666663393538363731333639396239666362393336373737643238636639643761343231346131333463353261623633343633343866663966663365346237313164613933626232343636366137616430363063643632626237306335353536396233353566633932393964346162636636376563656530396161306664656561626436396635656632346534653861376262386430376664633866626464346666653730326362316232653965343564313031306138623562363130623230313963396265366130303837616232366634326335646531303539353531353137366562353736323737393436626630343433346330356264653562376231356532663139306164303361383836396333393339616236383439363661313661303665643535633961666563613431303466333534346564633533373866306361633035363431646633643837383532663863303765623439623930373061333833633261383932383764356133323539336632353130303266643661643531313731316638343339653162613735393261653833333065316432643865616634333936326236626432663237656439643439633335656534373338313766633338646231316433363030393633613732386331363461303139393161316436323764326235316237613364393537303630633634616137656338386231636561313937653562653562623466633136316330383533636239353561353063623464363362636438656639613233323237373363323039386566366463587754430153274178ab274197be33430e5f1545710254412d9ade3b12f1c13df163bf3d6fc4c73bf7cbb4385e4ed83d0000803f2cb90e3da388cd3cc0d6913df226b13b37ea123df90a1fbdd201113df1081dbcfb240f3d5f280f3dfeddc33cb6dbcc3ca32f563d8f1439bd8f63c43c80ad8abb5381bd3ce8fd2bbc269fcd3cb97ec33c6ec41a3dc386f53c3d5d4d3d6cc298bb9f961a3db4aab0bc95381e3d5729653d44681d3dee941a3d90433fbc70e5923b3d7dbf3cff8520be91bf6dbc27c7c83b5d16f3bcca7df0bc45588fbccd02babc4734773bdea39e3b21d98cbc0efb3bbf915e7f3cbd8a693e81270f3df4800ebcfe88c53c1388723d8111eb3cc873ea3cd5229d3de385e33b7b1ff13c1f0716bde8d4ef3c7d7995bd3a59ef3c01ebed3ca01d9d3e56e4903efb8d87bf0ef454be0ec09f3e0fc3ba3d45b5853e4e5451be51c2933effca853e2089ba3e3c499f3eeca07abfd7e185bed66aba3e803e993c803c9f3e59ad82be5a3aad3e19029e3e40b19a3efa1c8f3e7f218ebf3d244abee2fa9b3e4c9ded3dafba803e967047be2b3c8e3e2442803ee242b13ec290993e4ed57dbf85b277beef3cb13e21ed283de6d4963eb12075be9ca4a43e7c86963e169ca43edf468d3e344288bf0a635fbe2d8da43e240a9a3dfe158b3e4e8862be131a983ede6d8a3e35359e3e6468843e6c3789bf41f853bea7ae9d3e4a86ec3d5b5a843e487c52be8037913e3569843e136ba13eb5b08c3e032d8cbfc8205abe124da13e52e7a33d2958873e76d758bed14e943eef50863e5b649a3e64bd9b3e616e88bfee8256be1afa993ed799e23de378833e478047be97698f3e063d833eefbea03e19e48e3ee8b784bf3e7294be1e05a03e55c5c83dbad5863e94de53bea379943e7209873ef5f9b23e1d759a3ea97e80bf32fb77becb04b33eb3e42b3d4049983e3eab73be7e12a63e9740973ecb00c43eb4cdd7be03fa8fbf8ad246be78a2b63ea3944e3e917d8d3e408b3bbeb9edad3e2c76933ee961ad3e6df49d3ec32481bf59c66ebe9b25ac3e42056c3df161933e17306ebe2feca03e2276923ea226ac3e6864953e869380bfca3a6fbed434ac3e93cf713dc559913e261366be5dcb9e3ecf8c8f3e8308303d1fa8f73c29b77a3d304de5bcc204303dd90991bd8200233dcf8b04bdfd2a2a3d3a5e223da196eb3cd8857e3cae62133d43e191bc3cc4eb3c88b65bbd0ec7d23c208ca8bc9c4ddf3c123fd23c388fd23c0543df3b72d31e3dd47550bc9099d23cb8078fbd9f0aba3c27607fbca930c73cbef0b83c4792f73c5ac3653ccce6d93cbd6898bcc4a1f73c17056dbd3133de3c99d0afbc2a31eb3cc8bfdc3cd64dc63c3223893c15f9053dcda1ceba5355c63cf64b8dbdd8bcac3c21517ebc3385ba3c287dab3cdf90923c831a0c3cc103253d785cd6bb6985923c6b0005bddf89743cc32410bce514873c708b813cd98bfcbbc6f28fbcc4494dbcd18fc53c7a33fcbb31fcc6bb639330bc1387963c4b7115bcbe7831bc34a0ca3cf3eaaa3b8c70263d7d688ebcd3baca3ced9ab2bd1af8bd3ca95b8abcc5dfc03c4e19c33c3e57c33c6d7e153c88ce473dc2c661bd4149c33cec8e39bdbbb4a83c79665bbcb664b63c9306a73ca846bc3c5e51bf3a57a7143d803c5d3b311dbc3c632858bd48fc9d3cdbf91fbcdca2b03c3ff0a33c5ee8143d5b98953c26eba93ce07bc33c9536153d64a087bd8fe1083dffb8dfbc21610f3dd624083d3cca21bcc31ee3bc7851873df730ebbdd9c821bce66076bb12fd52bc0b40943c48e339bc50e855bc7f51a63cf3c6fd3b80c6103d93bb3fbc0340a63cf3e489bdc830913c236b5dbc1a3f9d3ca1b3903cf7b4b23c1cdf853bd0845c3dd0bd8abc58a0b23c3aab2abdf0a89c3c348e66bcdf4ea93cf25a9b3caae50b3d52dd443c2cffa93ce424c03c51d10b3d8acab3bdeb00013d3a51d0bc84f9063d065f003d4303a73c7df79d3bc4fc0b3d804fbd3b0a15a73c82906cbdfd808b3cb7842bbccd80993cbed8893ce798d43c2d2b863cd80e133d73b586bc5ca8d43c09f402bd32ecbc3c383f84bcf163ca3c89e1bc3cb1dad63cd182ed3bdcc55a3de3801ebc1072d63ccba849bd60c8b83cfb299bbcc59fc73caeaeb63c6acc833cc716c9bbef34493d4061b83a43ea833c0617b0bde43c503c90da2cbb06d46e3cb3b3513c783b063d3d5b38bbe97b9d3dc22094bceb19063d1c62c6bdad65f33c0a63bdbc4fe8ff3cacc4f13c930c5d3c8bfe113b84e0443d81407dbb78d25c3c206262bde4282d3c823e5bbbe042483ce69a2b3c3651013d9acd873c07ac0d3d5ba1323bbc47013db7acaebd5359ec3c7a63bbbc53a4f73c704aeb3ce846aa3e5bc5933e155c81bf15296abef633aa3ea748863d5e938f3e088a67be4e479d3e0e278e3e2bb19d3e9d73c43ecbae8cbfd56543be6633a03eb6180b3e27f68c3e98f639be73119c3e9841873e0105a03eae559e3e0be287bfb3ef5dbe5700a03ec1a5a33db862863ed8b558be0436933e5b2d853e25d0b83eb7c09d3e264674bf8ae981beabadb83e10c62b3cf36e9e3ea3a07ebe7779ac3e22e59e3e8849bd3e8fdda23e94197abf5a3b87be853abd3e4d05443c55cea13ec2b984be82e5af3e3a40a03ea7f0bb3eccadb1bde73988bf9c1b6ebe252eb93edc77e13d325e9f3ed03970beefb1ae3e9cf59e3e94a6bb3e6bcaa03eda1f76bf1a6d85bef1a1bb3e080c223c9007a03e8a9482be525dae3e36e99e3eae5f973e822790be10f892bf8f0233be833ca33e112c503e59c27f3e6d2933bea41b943e81d07f3e3a75ac3ef97c943e349b82bfaa346fbe7775ac3e049e3c3d55fe903ed9ba69be0e689f3e9451913e601fa03e984e9d3ecb5988bf948b6cbebd049a3e0f9bd53d10d1853ee89d4ebef969913eefae813ea184923e6f74dc3e613992bf1d5ef2bdb8d27f3e1098493e190a703e826ad7bdf025803ee45d7b3e4e7e9d3ed220883e5c208bbf56964ebe564a9d3e5c20903d36f6823e33b747be0584903e647a813ee3f0a43e3c67903ee40d84bf1f6796becae5a43e7c49963d06228b3e1fbc5fbe1ea6983e68ec893ea15acf3c2013803c957fc13cdb357cbcdb90cf3cce0829bd492fbd3c34ba92bc9e80c63cf106bc3c6b5df43c91ab813c30a6e03c1888a53c5246f43c8d535dbd6205dc3c530ca2bc0127e83c1688da3c2dcef23cc81c2e3c6e022d3dbccf30bc35e3f23c4535a2bd5c38da3ce452a3bcd8bbe73cdc2ed93c5461db3c592fa43c05e8fb3c297c91bca84fdb3c29090fbd0d14c63c39b0a4bc8274d13c64bec43c2556523ddde19c3c3ef5113dc426283dc24c523dd0d6d1bda03f453df5df2fbd2ce44b3dc474443de402d63c672c6e3a61ec333d449869bceef7d53cbde897bd36cbc23cbdc581bc735ecc3c6624c03cdecd76bb47bcac3b9691a33c510366bdc19e7abbd27a2ebc1e2c92bb7c2b45bd98b587bb2bd290bb5c3e963c45a9fd3b00fa2c3d438d63bbaa3d963c3adc24bd32a3783c9c1915bcd75d893cbc5d7b3c52f5a83c1af84a3c82710b3d91790fbc7ae7a83c338218bd88a28f3cef2737bc4d559c3c05508e3c3866d33c6f19a33c670c023d73a51bbb62b1d33c563756bd3d50bb3c8cdf3fbc7f15c83ca09bba3c22478f3ca39fb33b0baa2f3d68addbbb83538f3c1c751bbd3885703c7a6a0abc67ac843ca718723cb056acbce32bd5bc32f9023d116e57bd5083acbcf553963c867cbdbc79a0ff3cb7a2b3bc58bebebca86a783ca906db3bd6f40a3d025f813b8583793cab2f3cbd6e654e3cde6424bbaa50653c6e454c3c17dac43b6bded03a16284f3c34b671bcb01bd73b072529bdd84eae3b97b5f2ba0d11bd3bc523ac3bf743df3ca411a03c7685bb3cc426863c6e17df3cf8c583bdf4aac93c1c18a5bc4531d53c0d47c83c9deec73cd80c4a3cf40eff3c036e8bbc3ccdc73cb3e72fbd17acb13ce69587bc08b4bd3cdf4bb03c175ebd3cd36b493c7c316b3d30ad47bcb786bd3c3f509dbdf735a73cb05d77bc3fa3b23c1b0ca63cc85ce43c75c93e3cf0aa063d232389bcbc4ce43c529f0dbd1305ce3cb8c49ebc869cd93c6eaccc3c4338ae3c12f5883c8666ff3c6addb43c7b2fae3c74fd67bd75b4963cf9f24dbca51ca33ccfcf953cbac4c83cb966b93bcb04103db8775bbc0cb1c83c4ccd56bdf87fb33cb37785bc7b74be3cb042b23cdfd73f3cba0f253bbf531d3d9cb5bcbcc90e403c934dd2bc109c123c63d911bae344293cee1e103c2c318a3c83aed33b4d92203d132343bb04718a3c566e48bdf281683c41df13bc96017f3c14bf653c7dac8f3e97c0763e2cee8dbf6d172cbe18778f3e01e5153ed9746a3e15a32bbe8f9e803e1879633e5496b23ebbcaa63e0f997fbf953778beb389b23ee506f73c824a973e89596fbe1648a53e0a41963ebf0ab63ec04c9b3ef9817abf6bdb7fbe53f6b53eaa16273d362e9c3ee6d079be157aa93e29f49a3ed4eb9c3e83ed9e3e529387bf3d0753be9d199e3e9e24d73d3e05843e214348bedeed8f3e69ef803e0ece983ec72a943e50528abf40573dbe1410983e779c1c3e0c19793e123b3bbe7cb28e3e5067743ecbd5a83e29af8f3e01b983bfa0ac68be68c3a83e1bf84c3d790e8e3efd7763be7adb9b3ea3a28c3ee9f0b93e46afa13eb4917cbfa22386be1cecb93ece69a93c6b029f3e3fb382beace9ac3e66ed9d3e40daae3e8b2ca03e5c6e80bf8af574be0f6eaf3e79c2903d73bd943ea09b70beba6ea13e3b3f933eb147a03e71c4893eadba89bf273f53be558aa03e3acc9f3d8cd3853e94344dbe1402943e1493853eb1f7a53e4927a13ed9e185bf65828ebe7b1fa73eb289bd3d6fb38b3e676860be2248983e7a998b3e7ac0a93e8f79963ed42987bfe8696bbe90e9a93e9c4a823d92e18f3e644868be26679c3e8bd68e3eea77903eeca0ba3e378590bf48673abe1194993eac2f1d3e1581713e01da3cbec9b68a3e15aa783ef257923e07d87c3ebc7e8dbf8e5d3ebef293923eb380cd3dfead6f3e96c53abe4c59853e01616d3e6c22c03ced16bc3b1a9b153dff7a20bdd42ac03c2b694cbd15c4a53cc91a4dbc1a32b33c21d8a33cbd319e3cb3918c3a7c041d3de002593c981a9e3c9854fdbd8432823c08e217bc5a3b913cbac1813cfdc58a3c94e459bb88d9143d612b2ebc45bb8a3c4ac952bd2ef5673c182717bcef037f3cd6356b3c2c2af83c3bd5053c20910d3da175abbc6a37f83c375461bd65abde3c962caebc6132ec3c9511dd3c9a7a8b3c93bcb13b6c3f2c3d4bf800bca5678b3c82b6febc872e683c97be20bccf39803c5689653cebc3cd3c39f40eba331c003dac9987bbc1a5cd3ca3d265bd6ec1b73ccd7b57bc7204c33c9f5bbc3c069fc1bbc6c4ebbc23a0793dd889f2bdbadfc1bbe438d8ba71950fbc4ef8943c1713efbb47f110bca31dc13cd28d1b3cee964f3dbfd759bb3c28c13c699ca0bda0dca43c6a6b3cbcf84fb33c701fa33ca7864c3ddb04fd3c777ef23bdae15a3d689a4c3d447f92bd9ff63f3dddbc2cbddad9463d26963f3dc42a9a3c19e7823b5f281e3d01d30dbc14ab9a3c23d31ebdf063843c9f9e49bc48938f3cdaeb823c5504b63c32e63bbca2e93a3d0c9b29bc4f9fb53cd414b1bde2869b3ca37b46bcf4c7a93c7ca69a3cb914d13cf389f23b3aac033d85e269bc2013d13c031c61bdd747ba3c36e690bc64dec53c3eefb83cac0b933c311910bcd170083dd00ec6ba9022933c069453bd95a1753cafc30fbc3d8c873c0e24763c6f0a2b3c03f36d3b9cb5413daa6320bd12242d3c627eb7bc5996153c05fc58bd2ac5243cc50e133c6d8ea43c0a10ce3a172d2f3d30c81e3cbd80a43c0a997abd28dd8a3ce1fd44bc4e12983c287b893c47c1113d8e2c9f3ce295123d15bde43cbda6113dfec5c1bd9516023d5e06bebce9700a3dde4a013d9886c43c6e20273b3c5ff53cc30e60bc4fc1c43cdf7fe8bcf370ae3c391a7ebca88eba3c1daaad3c6b86903ce290163b03360e3da759fbbbb857903ce37414bd2d0d753c11921cbce514863cdbc7733c3523923cd22736bb60f70c3dbd9dbabb621c923cad34f2bce147703ce156f3bbce1c863cb9796e3ce7da993c4782053c7415213d99f8c9bb2ff6993cbab564bd718f813c0d3f24bc513d8e3cbfd4893c30e5033da286243ca09b733dc912b6bcdce5033d3e2cafbdd645ee3ca92abdbc2c6cfb3ce2d7ec3c22b7ef3c6a973b3ce0d1d63c558b6fbcecdeef3c880274bda28ad73c0d07adbc9fc1e33ccbfcd53cc9f9ae3ede4e893eeeb685bf989567be5e7aae3e0fcb2b3df540933e825563be0dfca13e0951923e36c2ac3e6958b63d81ee89bf51ce64be8d0fad3ed4cfc23da1fb8e3ecd7a5fbe26639e3eda248d3ea1b0a63ee3288f3e21bb82bf69f961bea282a63ec633753dc6108b3e31915cbe40f6983e69b9893e1b37a33e7df58a3e70fa88bfadc060bea237a33eab35993d2582883e808c5abec00c963ebdda873e7cca9f3ea7e08b3efb3e88bfe92457be91ac9f3e76048c3d8794843e740451bed5a3923e9eec833ec662933e9a4d983e07be8dbf040249befff5933e342efa3dbcb2783e430243be8a388a3ed773783e8ff49a3e00aca53e190c8fbfaea851be838c9a3ef21a093efa2d823ef71732be9e66863e835e7b3e0128b23edb7d0bbea3638ebf347061be70deb03ee88ce93d87d7933ec9fb61bec783a23e244c943e7515a73ea5728f3e2e8387bf187f68be690ca73e140d7d3d22ab8c3ed07462be70219a3ece628b3e65eeb03eaf5a923e1df27fbf7e716fbee9b1b03e4371593d10f4953eab676fbe8305a33e6ef8933e4062a13e12cfab3ef2a385bf43fc91bec2e6a03e049bd33de41c8d3e4a1456be3908963e6cc5863e64d2bd3ef2d7a33e33b17dbfd26c88be0fd5bd3ea6e5823c22f1a23e4fc485beb13bb13ea079a23e40e7bb3e08eea03e2d2176bfd80286be2bdabb3ebaf34f3c6e95a13e998e82be5f3eaf3e0456a03ef02fbe3cb7835f3a6190183d0f6801bc7275be3cc390afbd1caca53c2f1975bc5d59b23c7928a73c087b393d96d11b3dc91e893c54fe903dee79393d4d4a9bbd5a392d3df75c16bdd48a333d63962c3d2210b93c7fdf463c94bf383dfa52fcbb3c0ab93c98a223bd39509e3c12295cbc7238ac3c5ab89c3c0a60a93cce1edb3b3cc6fe3cba1932bcd757a93cf7964abd6d40923cef4757bc78229e3cadd3903cf651de3cfee98e3c25dd503d8f2f9dbcc39cde3c76a96ebdeaaac63c995a9ebc5275d33c4531c73cb2150b3d7baf813c0c61903d2eb094bc541b0b3d006468bdd820fc3cabe0c7bc17b2043db0affa3c925b9e3cf9dd063c7c24593d9e9ae9bbb94c9e3c2fbf78bdbdad863cb47a26bcada1923c754c853c409eb63c83d7aa3b1a461b3d947342bc7ec7b63c7d5546bdee109e3cb2176cbc95eaaa3c08ca9c3c5892af3c78e1453c99010f3dc3a163bd22f9af3c00afd6bc4f1f9a3cbd0b50bcf6bba53cb989993ce353983c14be3e3cd90e2a3dff8d8ebdd685983cf087adbc208f833cb53383bdae108e3c97cf823c9607a43ca1a6933bad801b3d7c8894bb1f2aa43c4a9620bd2f8d8d3cbe9a38bc8bdf993c59f28b3c917fc63cbb0e173c7b66153d2a9bf13c2ceec63c2dfca8bd4532ae3c091f82bc2fadbb3cc07bad3c003bc83c5a9fa0bb9b0a183d73412ebcecacc63c6a48adbd0b78ac3c262250bc6f03bc3cf6e2a93ceb04d7bbf183bbbc4b88683dc410d1bdbe01d7bb6aabecbbe56619bcd335793cd9c801bc5b0d1cbc6e5c1a3ddb1ebf3c81c2863cb2ad113dac1b1a3d7d4ba3bde0e50e3dea50d5bc61b9143d43380e3d77d5cf3cdb7d3c3c41bf243d959285bc0ebbcf3ce85ed6bced6bb73c68d185bc4811c53c1d49b73cb3efd13c7733923c3594083d038a81bc98fbd13c502e40bdc842ba3c2b6194bceb3fc63cb3ebb83cc09bdc3c1ccc883c3e62043d22fb39bcc460dc3c6b2086bd90dbc03cd72655bc05e6ce3cfa2dbf3c2c2f8e3cea09d83b275b2f3d0211a8bbc4d88d3c110545bda4d16b3c3016f2bb1a25823c74a9683c341cbe3cd4740f3c67428a3c26b39f3a6c46c23c95a125bd282baf3c5f5b4fbbd274ba3cb679af3cc7829c3ce375df3bab4e313daad3d13a6a119c3c52c75fbd927b7d3c35b519bce6c88d3ca5c6793ca755983ce850843b5dd4043dd22b273c6592973c2fae55bd4b77853c3db180bb75c78e3cb3c4883cc96f9a3e96bc9c3ef0f887bf04d349be18239f3eb887be3d0219833eba374dbe2c38913e64c0843e4995aa3efc07923e30eb80bf49f16cbe5699aa3e489d823d1816903e748b67be8e729d3ee1ed8e3e07f4bb3e40b0a13e69fd7abfd56086be39d8bb3e2915803c10d9a03ee4d083be02ceae3e30e19f3e4797a73e87788f3edf1088bf87ae68bea18fa73e38c07c3dafec8c3eadd362beadfd9a3ec5838d3e38ceb23ea3b6993e14177ebfb09e7bbe3be2b23ed6bcee3cdb4c973e732075be889fa53e4d72963ef6dbb03eb8e29b3e2b527dbf121d8bbe1d24b13ebc1fc33c1b04963ec6b963be597ca33e8872953e8c37a53e42a5983e4e3988bfc6b669be1affa43e0fdfbc3d60978b3eebe361be9a42993e7ba18b3e5a289b3e2a96a33e4d3b87bffbb33ebe81ba973e08d6f63d38a5833e3e3137be80d98b3ea600773e60abab3e33f79a3e72ae86bf3b4b66be418bab3e82ab9b3dde9e8f3ec17767be7bb69c3ee3e68f3e20aaa93eb7a79a3ede4883bf95146bbe393da93e16324a3da3a18e3e003d65beb93c9c3ec4ec8c3ebc33973eee7aa23e73678abfeda138be796d973e070e1d3e66f77a3e644836be89ba8f3e75d6813ee98e9a3e95fa943ef28b87bfe66d4bbe38fc9e3e7f54fe3d5e8a823ea9e045be2fb3913e2676863e2e3c8c3e302aac3eb30b8fbf4df01ebece99833eb8b22c3e41e6593ed6b021be33d77c3eebe4653e64b6dd3c23c1213c08edd43cbf7d66bcddcbdd3c993b3ebdb445c43c3dc492bcf061d13cc981c33cb82ee93c1b7e5b3c7db3f83c503552bac311e93cce7b63bd788fd23ce40aa9bc9c33df3ceabdd03cdef0d43ca2f657b9c07b263d3f6a80bcaed9d43c201f63bd9787b83c338d74bc74d9c63c46b5b63c494aa03c443c7b3a3332fc3c2eecffbb0413a03c8e271ebd661b8b3c8bb044bc8610963c45d7893ce14ff33c1bbfa53a921bfd3c163974bcbafbf23c328f73bde18ada3c3e854abc3701e73cbf0cd93cbb66bd3c159717bb7d640e3d71bd79bbbb68bd3cfb1794bd1dd9a03c7eba3ebc6426af3cc50c9f3ced8b7d3ce15e453a9441f33cf626dcbb35747d3c65b892bda7a6533cbd8407bc7cdf693c950a523cbcc1be3c355c983a62b8443dcfc051bc8ba8be3cc388adbded13a53c791b67bcbf2fb23c7e82a33cc7e3083d3100063c956a093db14284bc4cc8083d18a252bd297cf43cd8a1bdbcbfaf013dc0bdf23c21e9cb3c1da5ea3b4b5b1b3d787a97ba6db7cb3c225c81bd42dbb33c232762bc26dcc23c75f3b13ccb47e5bba1c7c9bc7369843d6fb8d2bd3f8de5bbc2ac69bc6a9627bca1b5823cc3cc0cbc5db226bc39bb36b913854cbc64e94ebbf9b6963c26f937b9761305bce65e56bb1870903cea0de0baf6b063bb33920f3d7c73583c82d3733d0c4cc3bc548d0f3d195d90bdb191023d3f78d4bc301c093db6c5013d7f318a3c2fb774ba2c981e3d6f2a9e3cb4228a3c7c67b6bd21ff613cba810ebcda667b3c59435f3c3015483cab130c39dfff533d03441dbd5a5e473c6233b4bc348b1e3c24773abd800c353c9c1d1f3cf7a3673cc1903ab9f985323d79ba9d3a514a683c45a14abd3c8a393cfa88d3bb6bd5523c7193363c801d9f3c86f2fa3be68d1b3d29ec05bbf9759f3c595b65bd62ca853ccbeb19bcfd31933cf691843ce622953c589be53b8752243d0f6c32bc3536953c2d8242bce09f7c3cac121abc19248b3c75057b3ca00fa83ce060d83a64c5223da97a8abbc459a83c43becfbca5838d3c7c3f07bc22ad9a3c16d88b3c9927b03c4339ab3bea03f83c5f2122bcd312b03c608389bd06f6963cb44f3dbc3bd5a33c263b983cb7e5ce3cc5bb25bcba13393d3ced51bc2a61ce3c3d2781bd3121b83c2d8483bca0f9c33c7fbab63ccec6123d9c6f913cd42fa43c0d5d4e3d43b0123dc4ab98bd2095073d8e66e8bc32490d3dee28073d820c9c3ee5e7883e21d58bbf115e41be6998a63e8851ed3d4fa3823eb1323bbe3b03973eb62b833e9a929a3e9a719b3ef6b091bfd34d19bed30c8d3e09a5383ea0ec683e812e21befbcf8e3ec5e07c3ed040a33e91ef8b3e5e9b84bf75778bbee639a33ee8b2ac3d1d35893e7d095bbe6b60963ececc873e97d3a53ead48913e532387bfd3e364beed95a53e7944933dc5ce8b3e9c415fbe34e1983ed2ad8a3e1927b03ee5cc983e371882bfc65b78be4824b03e1f154b3df0ce953e79de72bea0a8a33e2d9f943e3e56c03ef31ca43e1b6972bf83bf88be0e48c03edc8e833ba500a43ef16385be98c7b23e34f9a23e544ca63e32dc7e3eb13a85bfafc55fbe54e6a53e1712b23d84678b3e234a5dbe45a5983e97808a3e42a3a83ebc55a23e10d084bf46ce64be9d7aa83e1b14a43d60648f3e808565be19669c3e62108e3e56d6a13eb755893e75b487bf84315dbe1bc7a13e3cde703d574f873ea04b56be5aeb943e5df0853e223d933edf64b73e54898bbf43cc20beb317943e6760343e0e13753eb3c526beccd9853efe237c3e591bad3e081d643ecd7b86bf56b86dbeab68ad3e41b2583df740913eaace67be689e9f3e288a8f3e93cea73e83a9923e6b6483bf176e69be48aca73e617c983d43498e3e029265be25fb9a3e87ae8c3e32d8a23ebf44843ef16887bfb02450be4c5fa33e7fecc33d8c718b3e782054be3aed963efd5c893ed197083dcaba823c6113003d1cae0f3cacb5083dd46f9fbda316f83cdbe4ccbc0474023d09acf63cf8a6a83c5da90d3ac2861d3dfc1c47bad185a83cebcf9abd2455903c090e47bc1b9f9c3c726e8f3cad5dbe3ba90d22bb26f5cbbac2f77ebbf18ebe3b8fbe61bccbda5d3b5457a93aa94f9b3b58725c3b8563c53cea9e0f3cdd7cec3c546664bc054cc53caf5188bdbf3caf3cd0847abc8750bb3c382cae3c96589c3c4c5e353bd5f21e3d3ec868bc097d9c3cdd365fbd631c863cb8fa28bc6aca923c92dc863c38d4023d838a433c574ec93c22538e3c5bcc023de06d7dbd2e20e93c2f91b5bc52f4f73c71cce73c4f17753c9c991dbbbba1763d53f09fbd9d567a3c9d630cbc6c514a3cb4e034bdee6c563c1e974c3ccdfd873cb2e7b4bb7f3f483da5fca1bbf09c873c78507bbdd2295f3c5f6b0ebc2687783c5f4c633c704d5c3c304cb33a9edd243d3909ac3add755c3cc30a80bdfd9e2b3c4af28bbb8f92453c8e9e293c339ba23c4f2696bbaa0e013d8d6e8fbbd490a23ce0cb93bd10dd893cd1033fbcebb0953cc9c6933cd4cfbe3c3d45ef3bd61b503d5fa76dbcd091be3cd92976bd2c77a73c6d477ebc56edb33c1d5aa63c3ae62a3d0a49a63c9efba73c13784c3d7a9f2a3d4e4ca7bdcad11e3db19005bd80fb243dde7b213db4ecc33c6c3e6f3bb1b8113d2bb68ebcd3b2c33cdf003fbd2ca4ac3ccd8e73bc81f4b83c7849ab3cd2a2c03cfc709339a2e0043d9f8a02bc674cc03c0e9944bd126bab3cb92c62bc6320b93c78eaaa3cca0b2cbc73e0e2bccd76913d2c9cedbd9d162cbc8d589dbbc6f962bc9c75a43c0cda46bc6fdb65bc15af763c68bd443b2e02223d90eb9c3cfa48763c99de89bd36dc4c3c1a0adfbb84da673ce7a74f3ceefdd93cfa98193cab141c3d4446613a71c4d93c1d159ebd08c2c03c465f82bc8be7ce3cd619bf3c282a963c5267223b7598253d5f769dbba94d963caddeeebc2d6b7d3c791924bc711f8b3c3272853ca4c5283b893cacbb26eeadbcb0603b3c27ad293ba5c885bcef5a083ae687fa3b27d5c93af2ad903a4cc7bc3c922b3d3c99ed353d0af795bb3addbc3c1dee89bd5326a13c16fa39bc0d1faf3cb1729f3cd0c2963cdb520439c743163daface8bb63d8963c72d358bdc0457b3cf4bdd0bb57548a3c9f707b3ce93eb03c516d2f3be5ea483d377824bcfc9ab03c24e597bd7440973cd88540bc90d1a43c7fce953c730ab03e7ed7a03e649b7fbfea7277bef776b23ee665563de5b2973ea9a975be64cea43e75dc963e1318963e01e98d3e0a908abf14b24abe0ada973e6d38cf3dfc3f7c3eab5145be6db48d3e8a187d3ea856aa3e3eeb943e098885bf636f6dbe9845aa3ec25c833d759c903e90d668be010d9e3ec46c8f3edf6ba13e098b973ee31087bf41665cbed9aba03e21aab53d048f863ed47554bed218943e8e2e863e7252a13e0b488c3e1ef985bf1e7a52bed3a0a13e0f0a923dc2ef863e4b774dbe0c93943e5c35863eb193a83e6f21973e641e85bff43869be96d4a83ecd648c3da6e68e3e92f464bec6209c3e80b58d3ea3f1ad3eb81e7e3ec19082bfa91d59be150bb53e0be5be3dc248943ee22866be46f7a23ee39b933e183ca03e868b873e1f0e89bfa7ef57becf28a03e59f58a3db348853eb2f651bed0f7923e290c843e66aaaa3ec9f78b3efe8886bf3ea95dbeadb1aa3e10eca03df3848e3eb8f563be7c409c3e33188e3e5086b83e91d987bedd0c8dbfdd9944be8ad8b13ee3c0353e5f5a953e190947be5457aa3ec113923e6b8d983ed309843e1c9a8cbfb46768be3855983e9869ec3db8227f3e0c164cbed8718c3e141e7e3e6b12be3ed1c9a23e500277bfa39387befff0bd3e2217f63bee7aa23e6d3784be1cdcb03ef3e2a03efebe9f3ec75c873e660a88bf546053be14b69f3e2d29853dfa5e843e830a51beb669923ee522833ed5f47d3c76dc8f3bab3a1f3ded72fdbb3be77d3cf4e503bd9edc4f3c1cf6debbaa59683cf778523c00b3ea3cd168373c2a152d3dc6670e3c4babea3c54a297bd60ffcc3c7c7996bc8041dc3ccf46cd3cd515a23c5d27983b1db9543dd402bcbb583ea23c993a54bd696e873cb54f1bbb634e953cb9c1853c112cf43ca1daba3ce697123d55674dbda931f53c184700bd0ea9dc3c3ca4b5bc630be93c6b22db3c428f083d9b33ca3c588b9b3dc59ebebc94b4083d08b764bdf676f63c7756bdbc0530023dc1fbf43c90ffbb3cc097523b7b69ea3ca90736bc6981bc3c5ab0fdbcd2f0a33c8a5b76bc1caab03c2b6ca43cf022953c81d5a5bac9373e3d626923bb3645953cf4dd58bdfafe7b3c2e1d30bc0dd8893ced6f7d3c6a1bcd3c1ff5543ccbeec83c1834f73caf07cd3cf0065dbdeccab73c856485bc0957c33c806ab63c38359f3c60ba3a3b3042253d8eaa443cc0ca9f3c50463abd6ccf813c26430eba7da2913c09c38a3c6a92b03c1852ce3bbcbae73c768b84bcfcbbb03c7bab71bd17b19b3c5c4068bc4b6da63ce9859a3cdbb3eb3cab0b733b4cbb053d146391bcfe6ceb3ca8806cbd100dd33cfeb3b0bcbf29e03c48f9d33ce397a43cc819043c0102583db3ab2fbc04c4a43c963166bd0319933ce3085abc9d119f3c5e04953cfcd6183d0831663c9cca383d6a098bbc8830193dccdf8fbded6e083dca0eaabca265113d73d9073d33b1373dcc15fb3cc9e0363c1692963df9a7373d8b2993bde8a9293d8abf11bdf1c7303d11d4283df46f653c1aab2bbcdbc1573d40e0e4babece673ccdbc5cbdcca1353c5092b2bb5d8a503c55ce323c0049d43c3a96863c4b83cd3c55c24d3d70b5d43cf82395bd9359bb3c3cd575bc2748c83c06d6ba3cdd90a63cbc29fd3b9fca203d928e5ebcd6d0a63cfc7b61bd96478e3cde524fbca0e99a3c67b18c3cf720153df7f3c63c58cde33c91acc0bc6815153de88348bd93ec073de7c2d7bcede90e3dc719073dfc32dcba0cccd8ba7f520f3b705e9ebdad49f9ba6f3624bc9c576abb2e7a213c846338bbb17f67bb9c86ebbb2e80c1bc7528853dcc27f6bd9108ebbb0db074bcc59127bc06ea863cdc230ebc1aa31fbcaef2f03cbdb8433cf0f3fb3cbc398dbce23df13c294209bd57edd63cd970a3bc5f74e43c5f97d53cc6e0f43b263ca03b333a563df14285bde690f53bfa138abb2adfb13bcf476fbd80ccd43b9cb0b03bdc17a23ef5c88f3efff784bf69036abe752da23e15b0863d8f6e883e32d259be758b953e431d873edc94b13efa2e9a3e72c780bfeb4281be3f9cb13ecf97363d9abb973e3ae279be51f9a43ea3b4983e4b5dab3e1ef4a03e18c185bf09666fbe89a1ac3e4846a63de1ef933e533e6dbe0a45a23e67bb933e7dcfb33efb11623e2b7384bfeebd54be3dd7b73ed5da9a3d0995973e0d055abe6469a93ef0e9993ec156a03eb186a33e1d4586bfd30f4ebe68999f3e821cd73d6ec3883e844d56be4742953e4ecc873e0d9d9a3e034f9c3e8a148cbfae2543bef7979e3e80d30a3e191f7d3e130448bed044903e62cd843e9721ab3ed76b913e2a5b83bf4ea18ebe5221ab3e6ba97a3de55c903e5cf566be3e6b9d3e78ae8e3e118c933e9fe9af3eb02f89bfd9d23cbe0cd9923ee37a073ec27f7c3e629338be808b893ef4c9823eedd4883e646f8d3e256995bfa1c925be98e1883e7eb2193e4e7f693ef5a325be5d467e3e009e6e3e32b4963ea7d58b3e0b758cbfcaa23bbe4b09953e186ae43d63f7783ec1613abe51bf873eee75753e2df1b73e59b59c3e00ed77bf176881be2fcfb73e8aa5023d55fe9b3ec86c7cbe9c84aa3e77be9a3e11f1a53ef114703eace587bf6d174bbe8258ac3e6a80f73db853893ee53154be25599c3e9281913e1a16983ecfd8ae3ecd138cbf996a47beddbb983efc4ff73d81387f3e681e49be65048c3e69b67c3ea8cadf3c28886e3c1307163d12f153bc72f3df3c4a7411bd18b9c53c287593bca1fdd23c4829c73c4a6e863cfaea7fbbdd75153da83e8bbbe749863cebb61dbd4300593c517fddbb0f98733c7af6583c0643e93c9dc787bb7be7553d801e88bc6611e93c8091a3bd098cd13c5bbda5bc684cdf3cc0dbd23c5d2c2c3d69548d3c0781c53c500ba33cc20a2c3d0393bdbda3421c3d70a1e1bc4bb6243dc4751b3d064a9b3c78d86a3a64dc1a3d7659193dfc439b3c5ae2a4bd5235813c4e7c0bbc5bbd8e3cdb707f3c25bcf23c49910b3c7d24253dc8acb4bcf699f23c2d5da7bd31a7d83c2912a2bc5437e73c7852db3c77f0c13cd0691d3b8678fb3c6094a33b33ecc13c4ec984bd3eeba53c14ed72bc013cb53c53b6a53cb0e92abb330ba8bcfb3a653ddc610ebe12dc29bb930b8839d9c7b2bba3eb373ca3787bbb902cb8bb43efd93c352ee83be083123dee1f8fbc66d7d93c57c392bc1001c23c36c58ebc0c95cf3c3e72c03c63b0ae3c889ecd3b4c7f143ddd0d40bccd99ae3cc33668bd25c6943c407656bc44e0a13c7e39933c0b68f83ce7dc593c7010f23cab366dbc4059f83c74bd8dbda48bde3ce4708dbcac03ec3ca118dd3cbb37c43c7473503cda1ded3c38583cbcd168c43cb401d7bc0550ac3cd0107bbc347db83c6ed9b33cca52a7bafadb3fbc4fa884bc8ab8953ccd17a8ba5a4d8fbcdb5694bb8895723c216b3dbb035c9abb1817ae3cd69cef3a4086193d7eae30ba6d33ae3c4dd42cbd0c80923c48ecbdbbf87ca03c7bb1903c1c9e813c68c040bc13403c3d74af21bda092813c584f23bd697d553ced45bbbbe61f6d3c0ed4523ce9d0c43c4944153c78c6103d209eedbb62e7c43c6ce52ebda354a83c510045bc745cb73c0685a73cd8a3823c1a4cb7bb733f333d30f0f13a44ee833c8cb91dbdf4885a3c54a6c0bba98f6f3c5a3a5a3cf714f13c10568f3c19b8393c8e53b83c249cf03ceeb8b8bded0cdc3c046facbceca5e63c59cada3c2dee9f3cb6e7ab3ad28c2d3dbd9f29bce43aa03c6d824dbdb36d883cd7f238bcf200973c8bec883c4dbee53c205f273b946af43cc5fe443b6ca3e53caa017dbdacbfc93ce4f38fbc0a3bd93c34d5c83ce4e46d3cc4784aba4a8e323d779a253b86e76d3c60f15bbd5dd0393c213e9dbbd1ba543c35dc373c6aaa293db865963c2eee063dc46f17bdfcce293df449c7bd8ab91e3d330905bd0985243dc79b1e3d6d82b33effd4973eafb089bf0c185bbed15cb43eab30603d6623993eb08774beee85a73e1581993e172e9d3eda51f53d0d938fbfcdda97bedad49b3eaedbec3d684d803e12de45bee7918e3ef8eb7d3ec828973ebd9f973ea62b8bbfa44749be47d3953e8b70073eefaf863ea4233dbeb66f953e8177843e6f06a83e0861a63ef8d587bff78551be175da93efb5dae3d14258d3efc625fbe2db69c3e688a8b3e2230a33e1a7b8f3e864289bfd23960be6f91a33e8f737c3d14c5893ef3245dbe3cca973e70d1883e5b4d9c3e2066953e4f0c8bbf4f3557be77639e3ea49cac3d9c05833e0d9351bea7b0913e31e1813e3edba43ebfd38d3e262284bf163b6bbe03daa43e878a5c3d3c8e8a3e61395abe780e983e6268893ec55bb33e540d9c3e3b8281bfcaba81beb853b33e40af323d37cc993eb6847dbe41b7a63e78c7983e2414993ef5f892bec46d94bf75fa1bbe10e59a3e7c9a7b3ea53a7f3e354107beed0fa63e1253933e638ba23ed9a78d3e7a8789bf98845fbe9a6ca23e0d587f3dd78e873e0eef53bead09953e26ee853eade2bb3ec607a13e6a437cbff23186bef9e9bb3e1204ed3cd250a03e4b9b82be7d7fae3ef81b9f3e3137b43e0c4d9c3ee8ce81bf636081be0f22b43ebb53263d0f4c9a3eabc37cbec4dda73e52e0993ee3d4b13e9ba9873eb3bd7cbf99a772be6eb7b13e20e7443d66d3953e3bdd6abe492ea43eb922943eb8170d3d72c6a73c9126893dbdc6bfbcdf0d0d3dee9b4fbda53c003d6c50d9bc9dc2063d54ecfe3c0aa2ce3c99dc5f3cd2dfa93c894494bcf151cf3c0b713bbd1753c03c8bc49ebcde53c83cf8bcbf3c827ecd3c8cb95e3caee7f53c00795c3d3d8acd3cb4ef8bbd5002b33c65d719bc3063c43c849cb83c55bdf83c4199863c7347633de43eb2bcfda6f83c79d4acbd9c5fe03c6c1ca1bc0008ed3c818ce03c2a63e23c0cc7a93c2588243d4809e43bf840e23c72d2cfbde883cc3c10eea3bcb72bd83c312ccb3c0449dc3c0f33f93b13cd243d419c45bc51f2db3c1c3d6fbd25b5c13cbc5e80bcf463cf3ce12cc03cd2199f3cb9a19d3b4482163d6c2bd0bbbc039f3cdf3306bd045b873c320a40bc024a933c5054863c9a5f9e3cca664d3bd7d3833d867369bd68de9e3c444a15bd4664863c67ad64bdb24a9b3cd9188d3ccae4a83cf4dbd13b06e4283d8a06fa3890cba83c949384bd2a1e8f3c05734cbc91689f3c96cb903cd1b4a93c94e4b33b7fd80f3d107f1bbcbb3baa3c68f10cbda64b923c592b56bca5849e3ca5de903c0334c13cce18b93b9f3f673dfa8631bca040c13c7daf86bd1ad4a63cf01572bcc302b53c6d42a53c2f36df3c9e22083cb016543d6d4c5cbcde08df3c42599dbdbc80c53c625f6ebc0591d23c4dd9c33cae59aa3cb2fc4e3b762c253d5761d03b7052aa3c3e618ebd4475913c859144bcc44a9e3c8df88f3c09aa863c47492b390b0fad3c17ce0b3d4656863cc1715ebde2766b3ce3cb05bc2905773c2235693c4e8ac63c0b59873c52cac93c5624b0bc8ab7c63cd514e2bc159fb23c646693bc63e9bc3c9409b33cb999833c0c33d93a95c2383df7b702bd367f833c061ddcbc0cca533c9ae4a5bb22fa6c3c695e513ccf92c83c9331823c65f1633dd4252fbc4f91c83cc1dd66bddd28ac3ce66a6abc93b2ba3ce865aa3c7050e33c31e34c3ce11c2c3d588d80bc4780e33c35f740bdd66bca3ca64099bcda44d73cae0ac93c0e3eb13cad4de33bebcc033d0a95b7bbb52fb13cbac576bd6831963ce05f09bca6f7a33c01ba943cc31f8a3cfc90e23bf2b0373dab405d39740b8a3cf67968bdb619603c647ee5bba66b7b3cc13d5d3ccd4ebabbd308b5bce3fe7d3d5385debd4859babb20e1cbbbbeb613bcbe229b3c4e8ae8bb26b811bc5d2fa13c032a2e3c0932063db9098ebcd252a13c6dbe5bbdd1e5903c48455bbc9e599b3cdbec8f3c88a39b3e65cf8a3eab9388bfa45656beb5739c3e26f6d43db006833e655e54be46568f3e0a13823e66d9a23e4a06943e1f0688bf09014bbecca1a23e89d19d3d34e3883e303b46befc1d963e7a31893e5835ad3eda26943e4e2884bfd92171be1235ad3e30d8133d1d47923e18446bbe2469a03ef82b913e26fdab3e23af963ecb9880bfaa8178bedaffab3eeb355f3d2a76913e28fc69be2c9b9e3e75a98f3eb27cb13ee4d1983e6f3581bf45917abe437eb13e3952293dcdbd963e6e1d74bed99ca43ee083953e3643a53e6b4e9f3e76e187bfc68e38be1fb9a23eda5abe3d5585893e977658be86e0963e2ccc8b3e3f47a33e92508d3eba0d88bf421a3abed58ea43e1bc1cd3d1015863e9e263fbeadef973e0f9d863e0875a53e23963a3e15e289bf9e524dbe4068a73e9f73e33d41028a3eaa9b4dbe8f6e973e614c8b3e7aa18a3ec72da33e84148fbfdc6e26beefca8e3eff033a3ef00d6f3eef7d28be002f803ec78c753e9a969b3e7619933e8e1b88bffca361be72929c3e64c3bc3d9dae823e435a4fbe25168f3ef835833e196bb13e6b369b3e198e84bf3d9180bed558b13e1453423d598e963e699974be397aa43e57d2953e5776a63e1be09f3e20ef80bfcda98cbee320a73e78e49a3d3e348f3e8e2964be6b1d9f3e69fd903e5109b93e36f5a03e069f7dbf872683be0203b93ed9d3a33c853a9e3ec0d380be58abab3ecfa79d3eb1eccb3c7bf5c53badcf1b3dfdf334bc3ee4cb3ca96835bd490bb23cf3ed79bc995bbf3c5279b03c5e1cb33cf6fb1e3a396f6b3dea2909bd1332b33c0de8cfbc1db79a3c5c0913bd1a3ca73cd736993ceca63c3c0c594abb63f8113d3d3b67bb14cf3c3c544b1bbd0b6e173c05b17cbb1aa62a3c3daa163cfc91093d7771953c37be013d3301e53b86ae093daa78c6bdd65cfd3c157ed1bccb9d043d5850fc3cb72106bc838eedbcfe987e3dbf3905beab7206bc638e69bbdf963abc6a8b963c04811ebca9ba3cbceae4f33cc580443c506d253d2ff2c1bb67f8f33c21bcb6bdbe22de3c43d8bcbc003ceb3c55aee43c05598b3caf968ebae5445a3d54c920bc2a2c8b3c0db078bd4896663c06d315bcca697f3c6903653c81ba553cbed6e83b2a243c3c5c26fc3b6c514a3c6340f2bc85cd233c9ecfd83954ca433cc67e2a3ce5bbac3c679e803b0900fa3c1a5f3ebc6eb3ac3c6df84ebd5ff5963c71cd6ebc23f9a13c41a5953c27929e3cc454133c9ea5093d77c040bc56ad9e3ce8acebbc2f72883c290a48bc484c943c845a873c91fdc03c46d3513c5b383a3d84fb79bc240ec13cbcf554bd7d10a73ce28d55bca73ab43c7686a53cebbfdb3cfe20e53bd2ff573de6ac73bca6bddb3c21d78ebd3d06bf3c4bba7abcea96cd3c0b54bd3cd449013cb35c20bc98b8023c3963b13bd39f013c04c9dcbcb219a83b37f0823bd29dd53b8b80a23be52df53cb222773b5f5b1d3d6ed672bce134f53c668f85bd5393da3cc44aa9bc9f62e83c1135da3c687a433d84fdf93c422c9f3cf78cac3d6d7f433d6009bdbdf7b0363d29111fbd456e3d3d58a0363d9255dc3ca950523bb778dc3c95428fbc794adc3c68cd46bd5314c13c1e81c0bbd319cf3c808ac03c1c6a8b3c39817e3a474e2e3dfa0f173caa568b3c2f2aaabd9d6c693c61ef07bcbf43803c9fad663c3faabf3c4a3d163c74e3f73c3d8c3dbb84a9bf3c83c79dbdd605a73c4a5e7fbc0c06b43c0cf9a53c2dc4da3cab84673c7056263d788f7dbc85a6da3c275122bd36cdc03cdfab94bc7cdbcd3c7141bf3c6edcf83b0712a6ba17098c3c483a0c3cd2f7fc3b0a50c6bc64deb73b5549193c21b3d83b5f52b43b78dec53c1403173c8fe6053d619053bc4aa0c53cd2bb41bdc518ac3c25ac7bbca3abb93ced81ab3cfda5e63ca2a9483cace2063dc2aa83bc2094e63cbaa92dbd3360cf3c1c7fa4bc9f76db3c3a8dce3c760a00404305a83fd2f79ebe24efc6bf787c004029cbdbbfae13e33f0c8ab3bfcb70f33f5832e13f1ff80140400cac3f9ce15dbea00ec9bf13c10240de3eefbf6f9de93fdb33c1bfe38ff83fce98e83fae8c064079bfac3fa3fc31be2af4c1bfd2ef06400bf0f3bfb7f4eb3f2aeebcbfab92fd3f0814ea3fe37673bfee12e2bed21e0f33fbc90f3f452076bfd3531a3f75943cbf80b40b3f71ef63bf163540bfcb0c8dbf5bde10bfd63996335731d83e56598abfa866263fd2ed4abffbbf0c3f39c278bfea2b5ebf503473bfc52da5be4f8d0837740d063f679f63bfaf30103fb6743fbfe75bfd3eba575dbf9d933abf5ffe77bf4abd08bf68351734e60ed93eb95b6cbfb45b073f8c2940bfbb28f03e49a74fbf0b6b36bf38ed89bfc856a8be7bf03234db750b3f2b707dbfc4831c3f3c3949bff020043faad57cbf11ff5bbf2a1651bf483afcbe862ec633063ce63ec33f55bf22a7083fb47f28bfae94f53e10fe4dbf690c33bfb3aa65bfb58025bf72474e34b17be83e6f5f81bf514a183f88b341bf01df013f36055cbf275a3ebfdf8d8e40dc717140d84d39c1f13b4bc06d6a8e40910d63c0accc7040928e45c01ab483401b416e40865ed3bf71c0a8bf29b7a53f8bcdfd3f034cd8bf4fb7e23f674cc9bf8eb0ff3fa92cd5bfa593cebff54f50bf \ No newline at end of file diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc index 348aa09aa6fb7fc0bf792436729a33b8f8b338b5..24cb56c3ac10c1b7f15dc8022497e5d875720cca 100644 --- a/gcc/opts-common.cc +++ b/gcc/opts-common.cc @@ -992,109 +992,6 @@ opts_concat (const char *first, ...) return newstr; } -static int -handle_lto_option (unsigned int lang_mask, - unsigned int num_decoded_options, - unsigned int argc, - const char **argv, - struct cl_decoded_option *&opt_array) -{ - int ret = 0; - char *lan = ""; - char *compiler = xstrdup (argv[0]); - lan = strrchr (compiler, '/'); - if (lan != NULL) - lan ++; - else - lan = compiler; - if (strstr (lan, "gcc") != NULL) - { - opt_array = XRESIZEVEC (struct cl_decoded_option, opt_array, argc + 2); - const char* lto_flag = "-flto=8"; - decode_cmdline_option (<o_flag, lang_mask, - &opt_array[num_decoded_options]); - ret++; - const char* ltopartition_flag = "-flto-partition=one"; - decode_cmdline_option (<opartition_flag, lang_mask, - &opt_array[num_decoded_options + 1]); - ret++; - } - else if (strstr (lan, "g++") != NULL - || strstr (lan, "gfortran") != NULL) - { - opt_array = XRESIZEVEC (struct cl_decoded_option, opt_array, argc + 1); - const char* lto_flag = "-flto=8"; - decode_cmdline_option (<o_flag, lang_mask, - &opt_array[num_decoded_options]); - ret++; - } - if (compiler) - free (compiler); - return ret; -} - -static int -handle_machine_option (unsigned int lang_mask, - unsigned int num_decoded_options, - unsigned int argc, - const char **argv, - struct cl_decoded_option *&opt_array) -{ - int ret = 0; - bool flag_Om = false; - bool flag_hip09 = false; - bool flag_hip12 = false; - char mcpu_name[64]; - for (unsigned i = 1; i < argc; i ++) - { - if (strcmp (argv[i], "-Om") == 0) - { - flag_Om = true; - } - if (strstr (argv[i], "mcpu=hip09") != NULL) - { - flag_hip09 = true; - strcpy(mcpu_name, "hip09"); - } - if (strstr (argv[i], "mcpu=hip12") != NULL) - { - flag_hip12 = true; - strcpy(mcpu_name, "hip12"); - } - } - if (!(flag_hip09 || flag_hip12) || !flag_Om) - { - return ret; - } - - const char *ai_infer_level = getenv ("AI_INFER_LEVEL"); - if (ai_infer_level) - { - return ret; - } - const int argc_hw = 6; - // global_options.x_param_ipa_prefetch_distance_factor - int64_t argv_hw[argc_hw] = { - global_options.x_param_simultaneous_prefetches, - global_options.x_param_l1_cache_size, - global_options.x_param_l1_cache_line_size, - global_options.x_param_l2_cache_size, - global_options.x_param_prefetch_latency, - global_options.x_param_ipa_max_switch_predicate_bounds, - }; - - int64_t output_pred = get_optimize_decision_from_optimizer ( - argc, argv, mcpu_name, argc_hw, argv_hw); - - if (output_pred != 1) - { - return ret; - } - - return handle_lto_option (lang_mask, num_decoded_options, - argc, argv, opt_array); -} - /* Decode command-line options (ARGC and ARGV being the arguments of main) into an array, setting *DECODED_OPTIONS to a pointer to that array and *DECODED_OPTIONS_COUNT to the number of entries in the @@ -1193,8 +1090,6 @@ decode_cmdline_options_to_array (unsigned int argc, const char **argv, num_decoded_options++; } - num_decoded_options += handle_machine_option (lang_mask, num_decoded_options, - argc, argv, opt_array); *decoded_options = opt_array; *decoded_options_count = num_decoded_options; prune_options (decoded_options, decoded_options_count); diff --git a/gcc/opts-global.cc b/gcc/opts-global.cc index a18c76940f971d8ff60ffacaa43ae1e8d41f1ed5..c42376593ee4496072982358262d33aadd8520de 100644 --- a/gcc/opts-global.cc +++ b/gcc/opts-global.cc @@ -337,6 +337,31 @@ decode_options (struct gcc_options *opts, struct gcc_options *opts_set, } } +/* handle lto options according to model inference result */ +void handle_lto_options(struct gcc_options *opts, char* compiler) +{ + const char *model_infer_level = getenv ("AI_INFER_LEVEL"); + if (model_infer_level) + { + char *lan = strrchr (compiler, '/'); + if (lan != NULL) + lan ++; + else + lan = compiler; + if (strstr (lan, "cc1") != NULL || strstr (lan, "lto1") != NULL) + { + global_options.x_flag_generate_lto = 1; + global_options.x_flag_lto_partition = LTO_PARTITION_ONE; + global_options.x_flag_lto = "8"; + } + else if (strstr (lan, "gfortran") || strstr (lan, "cc1plus") || strstr (lan, "f951")) + { + global_options.x_flag_generate_lto = 1; + global_options.x_flag_lto = "8"; + } + } +} + /* Hold command-line options associated with stack limitation. */ const char *opt_fstack_limit_symbol_arg = NULL; int opt_fstack_limit_register_no = -1; diff --git a/gcc/opts.h b/gcc/opts.h index a43ce66cffe10ef2e1ca0d06bfdf4c1302276e0d..92199638cf265c2c4609136ee2242351e9f655b0 100644 --- a/gcc/opts.h +++ b/gcc/opts.h @@ -386,6 +386,7 @@ extern void decode_options (struct gcc_options *opts, location_t loc, diagnostic_context *dc, void (*target_option_override_hook) (void)); +extern void handle_lto_options (struct gcc_options *opts, char* compiler); extern int option_enabled (int opt_idx, unsigned lang_mask, void *opts); extern bool get_option_state (struct gcc_options *, int, diff --git a/gcc/passes.def b/gcc/passes.def index c01e8ad861030005b2a503a01f8c4103ca3f303e..63322c38b035e6e1e9b9a431eaca0927b9dd1cb1 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -177,6 +177,20 @@ along with GCC; see the file COPYING3. If not see passes are executed after partitioning and thus see just parts of the compiled unit. */ INSERT_PASSES_AFTER (all_late_ipa_passes) + NEXT_PASS (pass_ipa_alignment_propagation); + PUSH_INSERT_PASSES_WITHIN (pass_ipa_alignment_propagation) + NEXT_PASS (pass_ccp, true /* nonzero_p */); + NEXT_PASS (pass_early_vrp); + NEXT_PASS (pass_cd_dce); + NEXT_PASS (pass_forwprop); + NEXT_PASS (pass_rebuild_cgraph_edges); + POP_INSERT_PASSES () + NEXT_PASS (pass_ipa_localize_array); + PUSH_INSERT_PASSES_WITHIN (pass_ipa_localize_array) + NEXT_PASS (pass_forwprop); + NEXT_PASS (pass_rebuild_cgraph_edges); + POP_INSERT_PASSES () + NEXT_PASS (pass_ipa_array_dse); NEXT_PASS (pass_ipa_hardware_detection); NEXT_PASS (pass_ipa_pta); NEXT_PASS (pass_ipa_struct_reorg); diff --git a/gcc/testsuite/g++.dg/pr83541.C b/gcc/testsuite/g++.dg/pr83541.C index f5b181e064a6b806c2540d2ea2aff762874c87a9..a55147a2bb588201dbf14625ad99d2930bf080a4 100644 --- a/gcc/testsuite/g++.dg/pr83541.C +++ b/gcc/testsuite/g++.dg/pr83541.C @@ -1,6 +1,6 @@ // PR tree-optimization/83541 // { dg-do compile } -// { dg-options "-O3 -std=c++17 -ffast-math -fdump-tree-evrp" } +// { dg-options "-O3 -std=c++17 -ffast-math -fdump-tree-evrp1" } #include @@ -13,5 +13,5 @@ int test(int x) return 42; } -// { dg-final { scan-tree-dump "return 42" evrp } } -// { dg-final { scan-tree-dump-not "return _" evrp } } +// { dg-final { scan-tree-dump "return 42" evrp1 } } +// { dg-final { scan-tree-dump-not "return _" evrp1 } } diff --git a/gcc/testsuite/g++.dg/pr96707.C b/gcc/testsuite/g++.dg/pr96707.C index 2653fe3d043177c4326a34b59e47d324c06977b1..add3ae0bb80709fe6037d9e0ea6d362f22ed264c 100644 --- a/gcc/testsuite/g++.dg/pr96707.C +++ b/gcc/testsuite/g++.dg/pr96707.C @@ -1,10 +1,10 @@ /* { dg-do compile} */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ bool f(unsigned x, unsigned y) { return (x >> y) <= x; } -/* { dg-final { scan-tree-dump "return 1" "evrp" } } */ +/* { dg-final { scan-tree-dump "return 1" "evrp1" } } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C b/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C index 6b1e879e6a987965623db525f07a5f7e498e9a7e..a65724c4bd0e8f02e91b92b6dab88571f893f4bf 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C +++ b/gcc/testsuite/g++.dg/tree-ssa/empty-loop.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-cddce2 -ffinite-loops" } */ +/* { dg-options "-O2 -fdump-tree-cddce3 -ffinite-loops" } */ #include #include @@ -29,5 +29,5 @@ int foo (vector &v, list &l, set &s, map &m return 0; } -/* { dg-final { scan-tree-dump-not "if" "cddce2"} } */ +/* { dg-final { scan-tree-dump-not "if" "cddce3"} } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/fwprop-align.C b/gcc/testsuite/g++.dg/tree-ssa/fwprop-align.C index 5cc5f0d7d9124bf37f2f02fdd2b7917483a2ce0f..2f8b6c04b4f06fec945aa682135a74241e7b9316 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/fwprop-align.C +++ b/gcc/testsuite/g++.dg/tree-ssa/fwprop-align.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-forwprop2" } */ +/* { dg-options "-O2 -fdump-tree-forwprop4" } */ struct A { @@ -16,4 +16,4 @@ int main() } /* We should eliminate the check if p points to a virtual function. */ -/* { dg-final { scan-tree-dump-times "& 1" 0 "forwprop2" } } */ +/* { dg-final { scan-tree-dump-times "& 1" 0 "forwprop4" } } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr106922.C b/gcc/testsuite/g++.dg/tree-ssa/pr106922.C index 2aec4975aa8ddcf6fd3f860d82a1143bf23bc8cf..11fcb4eb4b24b7f276086ba791177f30cb62b17b 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/pr106922.C +++ b/gcc/testsuite/g++.dg/tree-ssa/pr106922.C @@ -1,5 +1,5 @@ // { dg-require-effective-target c++20 } -// { dg-options "-O2 -fdump-tree-cddce3" } +// { dg-options "-O2 -fdump-tree-cddce4" } template struct __new_allocator { void deallocate(int *, int) { operator delete(0); } @@ -87,4 +87,4 @@ void testfunctionfoo() { } } -// { dg-final { scan-tree-dump-not "m_initialized" "cddce3" } } +// { dg-final { scan-tree-dump-not "m_initialized" "cddce4" } } diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr14814.C b/gcc/testsuite/g++.dg/tree-ssa/pr14814.C index f2177d257ad2a37c5295418a2347180f3037d77b..9372de88d31ec8a8abf399e4c3650a6127275610 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/pr14814.C +++ b/gcc/testsuite/g++.dg/tree-ssa/pr14814.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-forwprop2" } */ +/* { dg-options "-O2 -fdump-tree-forwprop4" } */ class YY { public: YY(const YY &v) { e[0] = v.e[0]; e[1] = v.e[1]; e[2] = v.e[2]; } @@ -14,6 +14,6 @@ int foo(XX& r) { if (r.direction().y() < 0.000001) return 0; return 1; } -/* { dg-final { scan-tree-dump-times "&this" 0 "forwprop2" } } */ -/* { dg-final { scan-tree-dump-times "&r" 0 "forwprop2" } } */ +/* { dg-final { scan-tree-dump-times "&this" 0 "forwprop4" } } */ +/* { dg-final { scan-tree-dump-times "&r" 0 "forwprop4" } } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr19476-6.C b/gcc/testsuite/g++.dg/tree-ssa/pr19476-6.C index f6b06c93c1428a441ebb2dd020581dd22d35f8e7..30918bcfd210a01382519e5a06b690418d1b4a3a 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/pr19476-6.C +++ b/gcc/testsuite/g++.dg/tree-ssa/pr19476-6.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fdelete-null-pointer-checks" } */ /* { dg-skip-if "" keeps_null_pointer_checks } */ // See pr19476-7.C for a version without including . @@ -12,5 +12,5 @@ int g(){ return 42 + (0 == new int[50]); } -/* { dg-final { scan-tree-dump "return 42" "evrp" } } */ -/* { dg-final { scan-tree-dump-not "return 33" "evrp" } } */ +/* { dg-final { scan-tree-dump "return 42" "evrp1" } } */ +/* { dg-final { scan-tree-dump-not "return 33" "evrp1" } } */ diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr19476-7.C b/gcc/testsuite/g++.dg/tree-ssa/pr19476-7.C index 38624f2bac37674976ade5f369e33350ea0a8b60..fb22528ccbcf38d8db9c235862b1f7fa93e8efcb 100644 --- a/gcc/testsuite/g++.dg/tree-ssa/pr19476-7.C +++ b/gcc/testsuite/g++.dg/tree-ssa/pr19476-7.C @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fdelete-null-pointer-checks" } */ /* { dg-skip-if "" keeps_null_pointer_checks } */ // See pr19476-6.C for a version that includes . @@ -8,4 +8,4 @@ int g(){ return 42 + (0 == new int[50]); } -/* { dg-final { scan-tree-dump "return 42" "evrp" } } */ +/* { dg-final { scan-tree-dump "return 42" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/graphite/fuse-1.c b/gcc/testsuite/gcc.dg/graphite/fuse-1.c index 61289d312c2b8dab136a71d567f8c6f8c9355008..0b36b37a19879ca0967f9bbf88c1e6c2ea5d07df 100644 --- a/gcc/testsuite/gcc.dg/graphite/fuse-1.c +++ b/gcc/testsuite/gcc.dg/graphite/fuse-1.c @@ -1,6 +1,6 @@ /* Check that the two loops are fused and that we manage to fold the two xor operations. */ -/* { dg-options "-O2 -fno-tree-vectorize -floop-nest-optimize -fdump-tree-forwprop4 -fdump-tree-graphite-all" } */ +/* { dg-options "-O2 -fno-tree-vectorize -floop-nest-optimize -fdump-tree-forwprop6 -fdump-tree-graphite-all" } */ /* Make sure we fuse the loops like this: AST generated by isl: @@ -12,7 +12,7 @@ for (int c0 = 0; c0 <= 99; c0 += 1) { /* { dg-final { scan-tree-dump-times "AST generated by isl:.*for \\(int c0 = 0; c0 <= 99; c0 \\+= 1\\) \\{.*S_.*\\(c0\\);.*S_.*\\(c0\\);.*S_.*\\(c0\\);.*\\}" 1 "graphite" } } */ /* Check that after fusing the loops, the scalar computation is also fused. */ -/* { dg-final { scan-tree-dump-times " \\^ 12;" 2 "forwprop4" } } */ +/* { dg-final { scan-tree-dump-times " \\^ 12;" 2 "forwprop6" } } */ #define MAX 100 int A[MAX]; diff --git a/gcc/testsuite/gcc.dg/lto/tbaa-1.c b/gcc/testsuite/gcc.dg/lto/tbaa-1.c index 74c0496711330758e2b944fe60808273cb7dc7e6..2047c70c85dc45b8d4c18f7090ca9d86dee57357 100644 --- a/gcc/testsuite/gcc.dg/lto/tbaa-1.c +++ b/gcc/testsuite/gcc.dg/lto/tbaa-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -flto -fdump-tree-evrp" } */ +/* { dg-options "-O2 -flto -fdump-tree-evrp1" } */ typedef struct rtx_def *rtx; typedef struct cselib_val_struct { @@ -38,4 +38,4 @@ discard_useless_locs (x, info) n_useless_values++; } } -/* { dg-final { scan-tree-dump-times "n_useless_values" 2 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "n_useless_values" 2 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr102738.c b/gcc/testsuite/gcc.dg/pr102738.c index cd58c258990937e780891dd5590578cdd14cf98c..a5439e8cb0b732e892a0885e354b80d0727f2cce 100644 --- a/gcc/testsuite/gcc.dg/pr102738.c +++ b/gcc/testsuite/gcc.dg/pr102738.c @@ -1,5 +1,5 @@ /* PR tree-optimization/102738 */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ /* { dg-do compile { target int128 } } */ /* Remove arithmetic shift right when the LHS is known to be 0 or -1. */ @@ -44,6 +44,6 @@ int a6(int f, int g) __builtin_unreachable(); } -/* { dg-final { scan-tree-dump-times " >> 127" 1 "evrp" } } */ -/* { dg-final { scan-tree-dump-times " >> 31" 1 "evrp" } } */ -/* { dg-final { scan-tree-dump-times " >> " 2 "evrp" } } */ +/* { dg-final { scan-tree-dump-times " >> 127" 1 "evrp1" } } */ +/* { dg-final { scan-tree-dump-times " >> 31" 1 "evrp1" } } */ +/* { dg-final { scan-tree-dump-times " >> " 2 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr102983.c b/gcc/testsuite/gcc.dg/pr102983.c index ef58af6def08df562fa27ba1f135f675ec01e23e..c4a3dfad63edf6d52cfb05beebfddd205fa221d3 100644 --- a/gcc/testsuite/gcc.dg/pr102983.c +++ b/gcc/testsuite/gcc.dg/pr102983.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void foo(void); static int a = 1; @@ -18,4 +18,4 @@ int main() { } } -/* { dg-final { scan-tree-dump-times "Folding predicate c_.* to 1" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Folding predicate c_.* to 1" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr103359.c b/gcc/testsuite/gcc.dg/pr103359.c index 13406f90d7d47d3c85f43e2055a98d9c1de3bd44..18785670258344e8eeeb7d3601bd98be15c222d4 100644 --- a/gcc/testsuite/gcc.dg/pr103359.c +++ b/gcc/testsuite/gcc.dg/pr103359.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O3 -fdump-tree-evrp" } */ +/* { dg-options "-O3 -fdump-tree-evrp1" } */ void foo(); static char a, c; @@ -18,4 +18,4 @@ int main() { foo(); } -/* { dg-final { scan-tree-dump-not "c = 0" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "c = 0" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr104288.c b/gcc/testsuite/gcc.dg/pr104288.c index 95eb196f9e41f87c58d0d95a21bebb339f1ecd57..020beb044120f0b888bd80ce69a15604ceabf55f 100644 --- a/gcc/testsuite/gcc.dg/pr104288.c +++ b/gcc/testsuite/gcc.dg/pr104288.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fdelete-null-pointer-checks" } */ /* { dg-skip-if "" { keeps_null_pointer_checks } } */ void keep(int result) __attribute__((noipa)); @@ -19,5 +19,5 @@ void bar (void *p) __builtin_abort (); } -/* { dg-final { scan-tree-dump-not "abort" "evrp" } } */ -/* { dg-final { scan-tree-dump-times "== 0B;" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-not "abort" "evrp1" } } */ +/* { dg-final { scan-tree-dump-times "== 0B;" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr104526.c b/gcc/testsuite/gcc.dg/pr104526.c index a29530829010fb93aed0609aa9e2b34cc596217a..574d414bc2e3da244130864623275fae49065159 100644 --- a/gcc/testsuite/gcc.dg/pr104526.c +++ b/gcc/testsuite/gcc.dg/pr104526.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void foo(void); @@ -12,4 +12,4 @@ int main() { } } -/* { dg-final { scan-tree-dump-not "foo" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "foo" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr106063.c b/gcc/testsuite/gcc.dg/pr106063.c index 467b31dea623e4509878b9834f9d5926cd56b85e..f715c295e3f2573bf4c3fd8e9e2282142348855c 100644 --- a/gcc/testsuite/gcc.dg/pr106063.c +++ b/gcc/testsuite/gcc.dg/pr106063.c @@ -1,5 +1,5 @@ /* { dg-do compile { target int128 } } */ -/* { dg-options "-O2 -fno-tree-forwprop --disable-tree-evrp" } */ +/* { dg-options "-O2 -fno-tree-forwprop --disable-tree-evrp1" } */ typedef __int128 __attribute__((__vector_size__ (16))) V; V diff --git a/gcc/testsuite/gcc.dg/pr43513.c b/gcc/testsuite/gcc.dg/pr43513.c index 9383a802c8ad2869df69761c779a482ffafe77eb..fb2673f08a970c0caed748ec2147519a432b6a9c 100644 --- a/gcc/testsuite/gcc.dg/pr43513.c +++ b/gcc/testsuite/gcc.dg/pr43513.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-ccp2" } */ +/* { dg-options "-O2 -fdump-tree-ccp3" } */ void bar (int *); void foo (char *, int); @@ -15,4 +15,4 @@ foo3 () foo ("%d ", results[i]); } -/* { dg-final { scan-tree-dump-times "alloca" 0 "ccp2"} } */ +/* { dg-final { scan-tree-dump-times "alloca" 0 "ccp3"} } */ diff --git a/gcc/testsuite/gcc.dg/pr68217.c b/gcc/testsuite/gcc.dg/pr68217.c index eb4f15e048fe3f84578283bf4fd7cdf0b87fb0ee..178cb5008b610472a8779b3e99048507bc8b89fb 100644 --- a/gcc/testsuite/gcc.dg/pr68217.c +++ b/gcc/testsuite/gcc.dg/pr68217.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1 -fno-tree-ccp" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1 -fno-tree-ccp" } */ int foo (void) { diff --git a/gcc/testsuite/gcc.dg/pr69047.c b/gcc/testsuite/gcc.dg/pr69047.c index d562663d86a3908aee6367d5bcb624bbbf20b993..d32e449e9c0682cd07f69b958832a4ece9a1d096 100644 --- a/gcc/testsuite/gcc.dg/pr69047.c +++ b/gcc/testsuite/gcc.dg/pr69047.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O -fdump-tree-forwprop4" } */ +/* { dg-options "-O -fdump-tree-forwprop6" } */ __UINT8_TYPE__ f(__UINT16_TYPE__ b) @@ -15,4 +15,4 @@ f(__UINT16_TYPE__ b) return a; } -/* { dg-final { scan-tree-dump "_\[0-9\]+ = \\(\[^)\]+\\) b" "forwprop4" } } */ +/* { dg-final { scan-tree-dump "_\[0-9\]+ = \\(\[^)\]+\\) b" "forwprop6" } } */ diff --git a/gcc/testsuite/gcc.dg/pr78888.c b/gcc/testsuite/gcc.dg/pr78888.c index 77a130cf11c2285db1fd2ec48bff38df40f75859..2ab0995e7a3bc624cc987ebfa7e16ccc7fd9bfa9 100644 --- a/gcc/testsuite/gcc.dg/pr78888.c +++ b/gcc/testsuite/gcc.dg/pr78888.c @@ -1,6 +1,6 @@ /* PR tree-optimization/78888 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void kill (void); void keep (void); @@ -25,5 +25,5 @@ void g (int x) if (__builtin_tolower ((unsigned char)x) == 'z') keep (); } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ -/* { dg-final { scan-tree-dump-times "keep" 4 "evrp"} } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ +/* { dg-final { scan-tree-dump-times "keep" 4 "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/pr81192.c b/gcc/testsuite/gcc.dg/pr81192.c index 6cab605655853ba5ed4e1efeff525b11df81dc5f..3e808d67cbe7fef7d8f4a2a57c5a40d0a167ac48 100644 --- a/gcc/testsuite/gcc.dg/pr81192.c +++ b/gcc/testsuite/gcc.dg/pr81192.c @@ -1,6 +1,6 @@ -/* { dg-options "-Os -fdump-tree-pre-details -fdisable-tree-evrp -fno-tree-dse" } */ +/* { dg-options "-Os -fdump-tree-pre-details -fdisable-tree-evrp1 -fno-tree-dse" } */ -/* Disable tree-evrp because the new version of evrp sees +/* Disable tree-evrp1 because the new version of evrp1 sees : if (j_8(D) != 2147483647) goto ; [50.00%] diff --git a/gcc/testsuite/gcc.dg/pr83072-2.c b/gcc/testsuite/gcc.dg/pr83072-2.c index f495f2582c437e411d1ecdfe002e526a6c20634e..c197a1a883824797f5f5f35359468cf3889008f5 100644 --- a/gcc/testsuite/gcc.dg/pr83072-2.c +++ b/gcc/testsuite/gcc.dg/pr83072-2.c @@ -1,5 +1,5 @@ /* { dg-do compile} */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ int f1(int a, int b, int c){ if(c==0)__builtin_unreachable(); @@ -15,4 +15,4 @@ int f2(int a, int b, int c){ return a == b; } -/* { dg-final { scan-tree-dump-times "gimple_simplified to" 2 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "gimple_simplified to" 2 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr83072.c b/gcc/testsuite/gcc.dg/pr83072.c index 3bed8d890139c407a01d5aead5a88fb23f3d268e..71116bd0e9bb3928d3f6b6730b6b18d8b9ca64b0 100644 --- a/gcc/testsuite/gcc.dg/pr83072.c +++ b/gcc/testsuite/gcc.dg/pr83072.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fno-tree-ccp -fno-tree-forwprop -fno-tree-fre" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fno-tree-ccp -fno-tree-forwprop -fno-tree-fre" } */ void kill (void); @@ -11,4 +11,4 @@ int f(int c){ return c; } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr83073.c b/gcc/testsuite/gcc.dg/pr83073.c index 1168ae822a41b8656b253d3b186b9c21d6dfe206..899a53caa40d8763c675b18f4f463e4bef50229a 100644 --- a/gcc/testsuite/gcc.dg/pr83073.c +++ b/gcc/testsuite/gcc.dg/pr83073.c @@ -1,5 +1,5 @@ /* { dg-do compile} */ -/* { dg-options "-O2 -fdump-tree-evrp-details -fno-tree-fre -fno-tree-ccp -fno-tree-forwprop" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details -fno-tree-fre -fno-tree-ccp -fno-tree-forwprop" } */ int f(int x) { @@ -7,4 +7,4 @@ int f(int x) return x & 1; } -/* { dg-final { scan-tree-dump "gimple_simplified to.* = 1" "evrp" } } */ +/* { dg-final { scan-tree-dump "gimple_simplified to.* = 1" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr90838.c b/gcc/testsuite/gcc.dg/pr90838.c index 7aa912525c9c1458b3adc691b255cff731195ce3..f7ad1e8779376e9c0369d02abc410b4a4d4b3271 100644 --- a/gcc/testsuite/gcc.dg/pr90838.c +++ b/gcc/testsuite/gcc.dg/pr90838.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-forwprop2-details" } */ +/* { dg-options "-O2 -fdump-tree-forwprop4-details" } */ /* { dg-additional-options "-mbmi" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } */ /* { dg-additional-options "-march=rv64gc_zbb" { target { rv64 } } } */ /* { dg-additional-options "-march=rv32gc_zbb" { target { rv32 } } } */ @@ -59,7 +59,7 @@ int ctz4 (unsigned long x) return table[(lsb * magic) >> 58]; } -/* { dg-final { scan-tree-dump-times {= \.CTZ} 4 "forwprop2" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } */ +/* { dg-final { scan-tree-dump-times {= \.CTZ} 4 "forwprop4" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } */ /* { dg-final { scan-assembler-times "tzcntq\t" 1 { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } */ /* { dg-final { scan-assembler-times "tzcntl\t" 3 { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } */ /* { dg-final { scan-assembler-times "andl\t" 2 { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } */ @@ -67,18 +67,18 @@ int ctz4 (unsigned long x) /* { dg-final { scan-assembler-not "imulq" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } */ /* { dg-final { scan-assembler-not "shrq" { target { { i?86-*-* x86_64-*-* } && { ! { ia32 } } } } } } */ -/* { dg-final { scan-tree-dump-times {= \.CTZ} 4 "forwprop2" { target aarch64*-*-* } } } */ +/* { dg-final { scan-tree-dump-times {= \.CTZ} 4 "forwprop4" { target aarch64*-*-* } } } */ /* { dg-final { scan-assembler-times "clz\t" 4 { target aarch64*-*-* } } } */ /* { dg-final { scan-assembler-times "and\t" 2 { target aarch64*-*-* } } } */ /* { dg-final { scan-assembler-not "cmp\t.*0" { target aarch64*-*-* } } } */ -/* { dg-final { scan-tree-dump-times {= \.CTZ} 4 "forwprop2" { target { rv64 } } } } */ +/* { dg-final { scan-tree-dump-times {= \.CTZ} 4 "forwprop4" { target { rv64 } } } } */ /* { dg-final { scan-assembler-times "ctz\t" 1 { target { rv64 } } } } */ /* { dg-final { scan-assembler-times "ctzw\t" 3 { target { rv64 } } } } */ /* { dg-final { scan-assembler-times "andi\t" 2 { target { rv64 } } } } */ /* { dg-final { scan-assembler-not "mul" { target { rv64 } } } } */ -/* { dg-final { scan-tree-dump-times {= \.CTZ} 3 "forwprop2" { target { rv32 } } } } */ +/* { dg-final { scan-tree-dump-times {= \.CTZ} 3 "forwprop4" { target { rv32 } } } } */ /* { dg-final { scan-assembler-times "ctz\t" 3 { target { rv32 } } } } */ /* { dg-final { scan-assembler-times "andi\t" 1 { target { rv32 } } } } */ /* { dg-final { scan-assembler-times "mul\t" 1 { target { rv32 } } } } */ diff --git a/gcc/testsuite/gcc.dg/pr91029.c b/gcc/testsuite/gcc.dg/pr91029.c index 4904764e1ee7110bd11471ddc5aab1a6f4171c28..f29212c011a042146ac481440ae8dbd1dd0ce27e 100644 --- a/gcc/testsuite/gcc.dg/pr91029.c +++ b/gcc/testsuite/gcc.dg/pr91029.c @@ -1,6 +1,6 @@ /* PR tree-optimization/91029 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void kill (void); int xx; @@ -45,4 +45,4 @@ void f4 (int i) } } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr93231.c b/gcc/testsuite/gcc.dg/pr93231.c index cd0b3f320f78ffdd3d82cf487a63e861d0bf8eab..1898935a996c145dc7482666307f29728051c4dc 100644 --- a/gcc/testsuite/gcc.dg/pr93231.c +++ b/gcc/testsuite/gcc.dg/pr93231.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-forwprop2-details -Wno-shift-count-negative" } */ +/* { dg-options "-O2 -fdump-tree-forwprop4-details -Wno-shift-count-negative" } */ int ctz_ice1 (int x) { @@ -32,4 +32,4 @@ int ctz_fail (unsigned x) return table[((x & -x) * 0x077CB531) >> 27]; } -/* { dg-final { scan-tree-dump-not {= \.CTZ} "forwprop2" } } */ +/* { dg-final { scan-tree-dump-not {= \.CTZ} "forwprop4" } } */ diff --git a/gcc/testsuite/gcc.dg/pr96542.c b/gcc/testsuite/gcc.dg/pr96542.c index 5014f2acad8348368335bd1ecb425947afe6a494..5b924d568717b799c9e0bbfe29691e3ee759fe3e 100644 --- a/gcc/testsuite/gcc.dg/pr96542.c +++ b/gcc/testsuite/gcc.dg/pr96542.c @@ -1,5 +1,5 @@ /* { dg-do compile} */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ unsigned char @@ -22,6 +22,6 @@ baz (unsigned int x) return (-1U >> x) * 16; } -/* { dg-final { scan-tree-dump-times "254" 2 "evrp" } } */ -/* { dg-final { scan-tree-dump "= PHI <32.*, 4294967280" "evrp" } } */ +/* { dg-final { scan-tree-dump-times "254" 2 "evrp1" } } */ +/* { dg-final { scan-tree-dump "= PHI <32.*, 4294967280" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/pr97505.c b/gcc/testsuite/gcc.dg/pr97505.c index f01d912067a0b38692d93cee68ced24d43b1dc69..efd011059e7873f2f9713beb0c9ecc2ac5714ba8 100644 --- a/gcc/testsuite/gcc.dg/pr97505.c +++ b/gcc/testsuite/gcc.dg/pr97505.c @@ -1,5 +1,5 @@ // { dg-do compile } -// { dg-options "-Os -fsanitize=signed-integer-overflow -fdump-tree-evrp" } +// { dg-options "-Os -fsanitize=signed-integer-overflow -fdump-tree-evrp1" } // Test that .UBSAN_CHECK_SUB(y, x) is treated as y-x for range // purposes, where X and Y are related to each other. @@ -20,4 +20,4 @@ int foobar(int x, int y) return 5; } -// { dg-final { scan-tree-dump-not "unreachable" "evrp" } } +// { dg-final { scan-tree-dump-not "unreachable" "evrp1" } } diff --git a/gcc/testsuite/gcc.dg/pr97515.c b/gcc/testsuite/gcc.dg/pr97515.c index b4f2481cb03f96327b64e7e7d5186a68f655d8a6..7b860cc7b78e106bc68dcf957bb1f196c5dc355b 100644 --- a/gcc/testsuite/gcc.dg/pr97515.c +++ b/gcc/testsuite/gcc.dg/pr97515.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-ccp2" } */ +/* { dg-options "-O2 -fdump-tree-ccp3" } */ int e7 (int gg) @@ -22,6 +22,6 @@ e7 (int gg) /* EVRP should be able to reduce this to a single goto when we can * revisit statements to try folding again based on changed inputs. - * Until then, make sure its gone by ccp2. */ + * Until then, make sure its gone by ccp3. */ -/* { dg-final { scan-tree-dump-times "goto" 1 "ccp2" } } */ +/* { dg-final { scan-tree-dump-times "goto" 1 "ccp3" } } */ diff --git a/gcc/testsuite/gcc.dg/pr97567-2.c b/gcc/testsuite/gcc.dg/pr97567-2.c index c3ead54eaa8eefa409adfa35e4268267f45720ef..69447736b448fe4aeed4ac31f0f96489806335a4 100644 --- a/gcc/testsuite/gcc.dg/pr97567-2.c +++ b/gcc/testsuite/gcc.dg/pr97567-2.c @@ -1,5 +1,5 @@ /* { dg-do compile} */ -/* { dg-options "-O2 -fdump-tree-evrp -fdisable-tree-ethread" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fdisable-tree-ethread" } */ char a[2]; @@ -21,4 +21,4 @@ void gg (void) foo (); } -/* { dg-final { scan-tree-dump-not "foo" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "foo" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/predict-1.c b/gcc/testsuite/gcc.dg/predict-1.c index d2e753e624ed287869ae056fa70be7795c06ee3f..96e7474fdd17a14a3ca3624996195dcf5f0a48cc 100644 --- a/gcc/testsuite/gcc.dg/predict-1.c +++ b/gcc/testsuite/gcc.dg/predict-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-profile_estimate --disable-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-profile_estimate --disable-tree-evrp1" } */ extern int global; diff --git a/gcc/testsuite/gcc.dg/predict-9.c b/gcc/testsuite/gcc.dg/predict-9.c index cb68a218a931cd1b98c85bf25094f73f4668105f..01a8361cad39e6d12088a7e9536c42ab90f42aec 100644 --- a/gcc/testsuite/gcc.dg/predict-9.c +++ b/gcc/testsuite/gcc.dg/predict-9.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-profile_estimate -fno-finite-loops -fdisable-tree-ethread" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-profile_estimate -fno-finite-loops -fdisable-tree-ethread" } */ /* Note: Threader causes removal of for loop. */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc-shadow_non_unique_init_const.c b/gcc/testsuite/gcc.dg/struct/dfc-shadow_non_unique_init_const.c new file mode 100644 index 0000000000000000000000000000000000000000..428c2720e976ecd153f770ebfd30bc749047b309 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc-shadow_non_unique_init_const.c @@ -0,0 +1,56 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + unsigned long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + unsigned long a; + unsigned long b; + + for (unsigned i = 0; i < MAX; i++) { + arcs[i].a = 100; + } + + for (unsigned i = 0; i < MAX; i++) { + printf("a: %ld, b: %ld\n", arcs[i].a, arcs[i].b); + } + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = a; + } + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + printf("a: %ld, b: %ld\n", arcs[i].a, arcs[i].b); + } + + // Should fail because of 100 is the init_const. + for (unsigned i = 0; i < MAX; i++) { + arcs[i].a = 100; + arcs[i].b = 100; + } + + for (unsigned i = 0; i < MAX; i++) { + printf("a: %ld, b: %ld\n", arcs[i].a, arcs[i].b); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Init const is not unique" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc-shadow_original_not_dfc_candidate.c b/gcc/testsuite/gcc.dg/struct/dfc-shadow_original_not_dfc_candidate.c new file mode 100644 index 0000000000000000000000000000000000000000..5fd15eb205f3c90c4fa4bad588ea964908b1c1f7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc-shadow_original_not_dfc_candidate.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + unsigned long b; + int c; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + int c; + + for (unsigned i = 0; i < MAX; i++) { + arcs[i].a = 100; + } + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld", &c); + arcs[i].a = i; + arcs[i].b = i; + arcs[i].c = c; + } + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + printf("a: %ld, b: %ld\n", arcs[i].a, arcs[i].b); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Fail checking dynamic shadow fields" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc-shadow_two_fields.c b/gcc/testsuite/gcc.dg/struct/dfc-shadow_two_fields.c new file mode 100644 index 0000000000000000000000000000000000000000..f5c0817090ebaafd8cee4f0684732885a3f11310 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc-shadow_two_fields.c @@ -0,0 +1,42 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + unsigned long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + unsigned long a; + unsigned long b; + + for (unsigned i = 0; i < MAX; i++) { + arcs[i].a = 100; + } + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = a; + } + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + printf("a: %ld, b: %ld\n", arcs[i].a, arcs[i].b); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Found shadow field: b" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_calloc_not_in_start_func.c b/gcc/testsuite/gcc.dg/struct/dfc_calloc_not_in_start_func.c new file mode 100644 index 0000000000000000000000000000000000000000..c7abf10197395f0db5b7af7c1053b20d7c5e4cce --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_calloc_not_in_start_func.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +void __attribute__((noinline)) start_func() { + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); +} + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + start_func(); + + for (unsigned i = 0; i < MAX; i++) { + if (arcs[i].a != arcs[i].b) + abort(); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Fail finding fc arrays" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_compress.c b/gcc/testsuite/gcc.dg/struct/dfc_compress.c new file mode 100644 index 0000000000000000000000000000000000000000..52fd343e0a33608fd165585102b4149c13be20b3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_compress.c @@ -0,0 +1,44 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; +arc_t* stop_arc; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + stop_arc = arcs + MAX; + + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); + + arc_t* arc = arcs; + for (arc = arcs; arc != stop_arc; arc++) { + if (arc->a != arc->b) + return 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Found a dynamic compression field: a, input var: a" "struct_reorg" } } */ +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Found a dynamic compression field: b, input var: b" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_dead_field.c b/gcc/testsuite/gcc.dg/struct/dfc_dead_field.c new file mode 100644 index 0000000000000000000000000000000000000000..fd1a0184115927518f7368db748fd3568fae9a6f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_dead_field.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fipa-struct-reorg=3" } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + if (arcs[i].a != i) + abort(); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Found a dynamic compression field: a, input var: a" "struct_reorg" } } */ +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Found a dynamic compression field: b, input var: b" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_fclose_not_in_start_func.c b/gcc/testsuite/gcc.dg/struct/dfc_fclose_not_in_start_func.c new file mode 100644 index 0000000000000000000000000000000000000000..34e2367011f6f943daee5d20801f9536d5398e0c --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_fclose_not_in_start_func.c @@ -0,0 +1,44 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; +FILE *file; + +void __attribute__((noinline)) start_func() { + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + unsigned long a; + long b; + + file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } +} + +int main() { + start_func(); + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + if (arcs[i].a != arcs[i].b) + abort(); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Fail finding fopen/fclose stmt" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_ignore_ref_after_startpoint.c b/gcc/testsuite/gcc.dg/struct/dfc_ignore_ref_after_startpoint.c new file mode 100644 index 0000000000000000000000000000000000000000..01b325dcd2d70286a88a1c6af03f1e8dca08d77d --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_ignore_ref_after_startpoint.c @@ -0,0 +1,45 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; +arc_t* stop_arc; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); + + // stop_arc should not be recorded as fc_ref. + stop_arc = arcs + MAX; + printf("test\n"); + + arc_t* arc = arcs; + for (arc = arcs; arc != stop_arc; arc++) { + if (arc->a != arc->b) + return 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Add fc_ref" "struct_reorg" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_local_ref_ptr.c b/gcc/testsuite/gcc.dg/struct/dfc_local_ref_ptr.c new file mode 100644 index 0000000000000000000000000000000000000000..6d51de7780c08e64af0288981bb1759910d4bcdd --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_local_ref_ptr.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + arc_t* stop_arc = arcs + MAX; + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); + + arc_t* arc = arcs; + for (arc = arcs; arc != stop_arc; arc++) { + if (arc->a != arc->b) + return 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Local usage not handled" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_multiple_call_path_to_startpoint.c b/gcc/testsuite/gcc.dg/struct/dfc_multiple_call_path_to_startpoint.c new file mode 100644 index 0000000000000000000000000000000000000000..86de3056860f443280479b2290860cdc86b68e11 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_multiple_call_path_to_startpoint.c @@ -0,0 +1,47 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +void __attribute__((noinline)) read() { + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + if (arcs[i].a != arcs[i].b) + abort(); + } +} + +int main() { + int flag; + if (flag) + read(); + else + read(); + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Fail finding fc paths" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_multiple_variable_same_array.c b/gcc/testsuite/gcc.dg/struct/dfc_multiple_variable_same_array.c new file mode 100644 index 0000000000000000000000000000000000000000..705b43ac5a7a17f1f9bf44e7e31a6e05d971ff27 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_multiple_variable_same_array.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs1; +arc_t* arcs2; + +int main() { + arcs1 = calloc(MAX, sizeof(arc_t)); + arcs2 = arcs1; + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs1[i].a = a; + arcs1[i].b = b; + } + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + if (arcs1[i].a != arcs2[i].b) + return 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Array assigned to multiple variable" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_not_direct_assign.c b/gcc/testsuite/gcc.dg/struct/dfc_not_direct_assign.c new file mode 100644 index 0000000000000000000000000000000000000000..4d4e994d11250a9547ea806e705981b78d75c83f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_not_direct_assign.c @@ -0,0 +1,49 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; +arc_t* stop_arc; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + stop_arc = arcs + MAX; + + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); + + arc_t* arc = arcs; + for (arc = arcs; arc != stop_arc; arc++) { + // a = a + 1, Value of field a may be outside the closure, and we can't guarantee the validity of its boundary + arc->a++; + arc->b = arc->a; + } + + for (arc = arcs; arc != stop_arc; arc++) { + if (arc->a != arc->b) + return 1; + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] Fail checking closure" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_variable_allocated_twice.c b/gcc/testsuite/gcc.dg/struct/dfc_variable_allocated_twice.c new file mode 100644 index 0000000000000000000000000000000000000000..fba127265a81dc37a3fa2429d5004a99efe07158 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_variable_allocated_twice.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ + +#include +#include + +#define MAX 16 + +struct arc { + unsigned long a; + long b; +}; +typedef struct arc arc_t; + +arc_t* arcs; + +int main() { + arcs = calloc(MAX, sizeof(arc_t)); + if (arcs[0].a == 0) + arcs = calloc(MAX, sizeof(arc_t)); + char line[101]; + unsigned long a; + long b; + + FILE* file = fopen("data.txt", "r"); + for (unsigned i = 0; i < MAX; i++) { + fgets(line, 100, file); + sscanf(line, "%ld %ld", &a, &b); + arcs[i].a = a; + arcs[i].b = b; + } + fclose(file); + + for (unsigned i = 0; i < MAX; i++) { + if (arcs[i].a != arcs[i].b) + abort(); + } + + return 0; +} + +/* { dg-final { scan-ipa-dump "\\\[field compress\\\] fc_array allocated twice before start-point" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/dfc_wholeaccess.c b/gcc/testsuite/gcc.dg/struct/dfc_wholeaccess.c new file mode 100644 index 0000000000000000000000000000000000000000..60d73f0f0ed79e3c8d436085e7a975c5b16ce257 --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/dfc_wholeaccess.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ + +struct Type_A; + +// Optimized type. +struct Type_B { + int b; +}; + +__attribute__((used)) static void test() { + struct Type_A* a; + struct Type_B* b; + + // MEM[(Type_B*)a] = *b; + *((struct Type_B*)a) = *b; +} + +// This testsuite should be compiled successfully without ICE. diff --git a/gcc/testsuite/gcc.dg/struct/rf_defined_by_addr_expr.c b/gcc/testsuite/gcc.dg/struct/rf_defined_by_addr_expr.c new file mode 100644 index 0000000000000000000000000000000000000000..65922be8e84fa44640ef433c95735ef4c49f7a1d --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/rf_defined_by_addr_expr.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ + +struct A { + int a; +}; + +extern unsigned long long offset(); + +int main() { + unsigned char num[16]; + ((struct A*)(num + offset() * 4))->a = 10; + + return num[0]; +} + +/* { dg-final { scan-ipa-dump "struct A(\\\(\[0-9\]*\\\))? has escaped: \"Type escapes a cast to a different pointer\"" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/sr_early_void_ptr.c b/gcc/testsuite/gcc.dg/struct/sr_early_void_ptr.c new file mode 100644 index 0000000000000000000000000000000000000000..c52636925e66412294892b24724b1a5807e058ac --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/sr_early_void_ptr.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fgimple" } */ + +#include + +struct S { + int a; + int b; + int c; +}; + +__attribute__((noinline)) void __GIMPLE(ssa,startwith("struct_reorg")) test() { + void* _1; + + __BB(2): + _1 = calloc(1UL, 12UL); + __MEM ((int*)_1 + 4UL) = 0; + __MEM ((struct S*)_1).a = 0; + + return; +} + +int main() { + test(); + return 0; +} + +/* { dg-final { scan-ipa-dump "struct S(\\\(\[0-9\]*\\\))? has escaped" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/sr_no_recorded_local_void_ptr.c b/gcc/testsuite/gcc.dg/struct/sr_no_recorded_local_void_ptr.c new file mode 100644 index 0000000000000000000000000000000000000000..d917b66f82fe2b1d6aff87872f5cac02889d977f --- /dev/null +++ b/gcc/testsuite/gcc.dg/struct/sr_no_recorded_local_void_ptr.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fgimple" } */ + +#include + +struct S { + int a; + int b; +}; + +__attribute__((noinline)) void __GIMPLE(startwith("struct_reorg")) test() { + void* ptr; + + ptr = calloc(1UL, 8UL); + __MEM ((struct S*)ptr).a = 0; +} + +int main() { + test(); + return 0; +} + +/* { dg-final { scan-ipa-dump "struct S has escaped: \"Type escapes via no record var\"" "struct_reorg" } } */ diff --git a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp index 850fc8f1b8a7cd148d745b33b9ce97642a360ceb..264df7c6ed315bf99c9fe10d8a83ef70c508d77c 100644 --- a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp @@ -73,6 +73,14 @@ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/sfc-bitfield_*.c]] \ gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/sfc-shadow_*.c]] \ "" "-fipa-reorder-fields -fipa-struct-sfc -fipa-struct-sfc-shadow -fdump-ipa-struct_reorg-details -flto-partition=one -fwhole-program" +# -fipa-struct-dfc +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/dfc_*.c]] \ + "" "-fipa-reorder-fields -fipa-struct-dfc -fdump-ipa-struct_reorg-details -flto-partition=one -fwhole-program" + +# -fipa-struct-dfc -fipa-struct-dfc-shadow +gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/dfc-shadow_*.c]] \ + "" "-fipa-reorder-fields -fipa-struct-dfc -fipa-struct-dfc-shadow -fdump-ipa-struct_reorg-details -flto-partition=one -fwhole-program" + # All done. torture-finish dg-finish diff --git a/gcc/testsuite/gcc.dg/torture/pr97812.c b/gcc/testsuite/gcc.dg/torture/pr97812.c index 4d468adf8faafa0d6b5f33de7b05f25dffd012b7..7df29a69e642b1fbbf01e0d8a86f69bb2ac0cac4 100644 --- a/gcc/testsuite/gcc.dg/torture/pr97812.c +++ b/gcc/testsuite/gcc.dg/torture/pr97812.c @@ -1,5 +1,5 @@ /* { dg-do run } */ -/* { dg-additional-options "-fdisable-tree-evrp" } */ +/* { dg-additional-options "-fdisable-tree-evrp1" } */ unsigned char c; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/20030808-1.c b/gcc/testsuite/gcc.dg/tree-ssa/20030808-1.c index 456f6f2712871627971c0845e0db4d59abfc5ab2..40b8e4cacb12bed091d86a4707e91dccfb995412 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/20030808-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/20030808-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O1 -fdump-tree-cddce3" } */ +/* { dg-options "-O1 -fdump-tree-cddce4" } */ extern void abort (void); @@ -33,8 +33,8 @@ delete_dead_jumptables () /* There should be no loads of ->code. If any exist, then we failed to optimize away all the IF statements and the statements feeding their conditions. */ -/* { dg-final { scan-tree-dump-times "->code" 0 "cddce3"} } */ +/* { dg-final { scan-tree-dump-times "->code" 0 "cddce4"} } */ /* There should be no IF statements. */ -/* { dg-final { scan-tree-dump-times "if " 0 "cddce3"} } */ +/* { dg-final { scan-tree-dump-times "if " 0 "cddce4"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/20040211-1.c b/gcc/testsuite/gcc.dg/tree-ssa/20040211-1.c index a9bdf26931aa758d85aee6174af37af5156eba7d..625426d2014e0b69748faa93585cd26336a8ec78 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/20040211-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/20040211-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-cddce2 -fno-finite-loops" } */ +/* { dg-options "-O2 -fdump-tree-cddce3 -fno-finite-loops" } */ struct rtx_def; typedef struct rtx_def *rtx; @@ -35,4 +35,4 @@ com (rtx insn, int blah) /* Cddce cannot remove possibly infinite loops and there is no way how to determine whether the loop in can_move_up ends. */ -/* { dg-final { scan-tree-dump "if " "cddce2"} } */ +/* { dg-final { scan-tree-dump "if " "cddce3"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/alias-17.c b/gcc/testsuite/gcc.dg/tree-ssa/alias-17.c index 62ef77622008c8bf611e19a43adc70dd2110a5db..0c05519df94449fd3ee17403191774c338bdafb4 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/alias-17.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/alias-17.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O -fno-early-inlining -fdump-tree-ccp2" } */ +/* { dg-options "-O -fno-early-inlining -fdump-tree-ccp3" } */ int *p; int inline bar(void) { return 0; } @@ -14,4 +14,4 @@ int foo(int x) return *q + *p; } -/* { dg-final { scan-tree-dump-not "NOTE: no flow-sensitive alias info for" "ccp2" } } */ +/* { dg-final { scan-tree-dump-not "NOTE: no flow-sensitive alias info for" "ccp3" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/andnot-2.c b/gcc/testsuite/gcc.dg/tree-ssa/andnot-2.c index e0955ce3ffd9d1cf9bd47e6a5da5b7d5e32c1047..6e1563d192af7810a43738d4a082526f1fe56cc1 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/andnot-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/andnot-2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-forwprop3-raw -w -Wno-psabi" } */ +/* { dg-options "-O2 -fdump-tree-forwprop5-raw -w -Wno-psabi" } */ typedef long vec __attribute__((vector_size(16))); vec f(vec x){ @@ -7,4 +7,4 @@ vec f(vec x){ return y & (y == 0); } -/* { dg-final { scan-tree-dump-not "_expr" "forwprop3" } } */ +/* { dg-final { scan-tree-dump-not "_expr" "forwprop5" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-22.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-22.c index 82eb5851c59b0bb6776de4a67a474cb8805adab0..e7fdd7390fb7fb2679762b1c35ba9c62371a9e94 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-22.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-22.c @@ -22,7 +22,7 @@ void g (char *s1, char *s2) [1] n_6: size_t [0, 1023] [2] d_8: size_t [0, 1023] - Whereas evrp can't really: + Whereas evrp1 can't really: [1] n_6: size_t [0, 9223372036854775805] [2] d_8: size_t [0, 9223372036854775805] diff --git a/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c index 886dc147ad1a955c63bc1a0a560bb833e4181191..787e41a8a6fff270a51e39cf3c007bcfd102ca0e 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-9.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-cunrolli-details -fdisable-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-cunrolli-details -fdisable-tree-evrp1" } */ void abort (void); int q (void); int a[10]; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp-ignore.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp-ignore.c index 9bfaed6a50a1bd6764fb95a824b79799a035632c..b4e687dfe82d3d1895d1dc8ec3804f36589a1b8a 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp-ignore.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp-ignore.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fno-tree-fre -fdisable-tree-ethread" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fno-tree-fre -fdisable-tree-ethread" } */ void kill(void); @@ -25,4 +25,4 @@ void foo (int x, int y, int z) kill(); } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans.c index 8ee8e3c3f42dca22bae83069c5d6fb6316c91ed4..f07eee75dc728ddced21e8198d909d1816caec6d 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ /* Simple tests to make sure transitives are working. */ void keep(); @@ -140,5 +140,5 @@ f9 (int x, int y, int z) } } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ -/* { dg-final { scan-tree-dump-times "keep" 13 "evrp"} } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ +/* { dg-final { scan-tree-dump-times "keep" 13 "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans2.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans2.c index d6fe42714f641a465fa2826df726c1aaba559e9c..7a39f126daab277042ea171a5c7c95ce98d01e97 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp-trans2.c @@ -1,8 +1,8 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ #define int unsigned #include "evrp-trans.c" -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ -/* { dg-final { scan-tree-dump-times "keep" 13 "evrp"} } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ +/* { dg-final { scan-tree-dump-times "keep" 13 "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp1.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp1.c index f5f38c4ce19daa188accae0e407201ae1362dcda..d2561f6c65a8bda9626d93e3326b22dbcb6e78d5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ int foo (int i); int bar (int j) @@ -10,4 +10,4 @@ int bar (int j) return j; } -/* { dg-final { scan-tree-dump "\\\[5, \\+INF" "evrp" } } */ +/* { dg-final { scan-tree-dump "\\\[5, \\+INF" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp11.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp11.c index d791305d426c9bc2b1d6cba73522acc78f5e88e8..457e155523901c42b3e354b5147ef087d1242181 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp11.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp11.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fdelete-null-pointer-checks" } */ extern void link_error (); @@ -20,4 +20,4 @@ void bar (char *x, int a) } } -/* { dg-final { scan-tree-dump-not "link_error" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "link_error" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp12.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp12.c index b3906c23465fe47ff86f53d91a9a3336e8b506db..e26fbf73d2a782f7d271ae0061fea5222be0b539 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp12.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp12.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ extern void link_error (); @@ -18,4 +18,4 @@ f3 (unsigned int s) } } -/* { dg-final { scan-tree-dump-not "link_error" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "link_error" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp13.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp13.c index cfa4e8256c07d8445561aff3367289bfcd31d369..4f3ec19c39373c5db133f87bb9736e4ea7452c9c 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp13.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp13.c @@ -1,7 +1,7 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ #define ADD_NW(A,B) (__extension__({ __typeof(A+B) R; if(__builtin_add_overflow(A,B,&R)) __builtin_unreachable(); R ;})) _Bool a_b2(unsigned A, unsigned B) { return ADD_NW(A,B) >= B; } -/* { dg-final { scan-tree-dump "return 1;" "evrp" } } */ +/* { dg-final { scan-tree-dump "return 1;" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp2.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp2.c index fc92cdfbc663900597255f8661e05413a3cc2ee5..099ca4a0cfd5b3e8870fae50e64f55dd66065447 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ int foo (int i); int bar2 (int j) @@ -15,4 +15,4 @@ int bar2 (int j) } -/* { dg-final { scan-tree-dump "\\\[4, 7\\\]" "evrp" } } */ +/* { dg-final { scan-tree-dump "\\\[4, 7\\\]" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp20.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp20.c index 7d4d55f7638f74ddc27d30e7408b12d02518e055..5f847daef636c66733f61f52650a747565fb5fbb 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp20.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp20.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void call (void); @@ -16,4 +16,4 @@ void foo (int base) } } -/* { dg-final { scan-tree-dump-not "call" "evrp"} } */ +/* { dg-final { scan-tree-dump-not "call" "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp21.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp21.c index dae788cc2b6c2e55ec2872164d6be0a6cedde14e..ea03aa6ed7b0649e7f76a32641b80c16d5e1f043 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp21.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp21.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ extern void vrp_keep (void); extern void vrp_kill (void); @@ -24,5 +24,5 @@ f2 (int s, int b) vrp_kill (); } -/* { dg-final { scan-tree-dump-times "vrp_keep \\(" 1 "evrp"} } */ -/* { dg-final { scan-tree-dump-times "vrp_kill \\(" 0 "evrp"} } */ +/* { dg-final { scan-tree-dump-times "vrp_keep \\(" 1 "evrp1"} } */ +/* { dg-final { scan-tree-dump-times "vrp_kill \\(" 0 "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp22.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp22.c index 3dd47e55d04d0dcf538068814b4372a978ea2dac..777087810b632f51df5963fd5137e52d2f624933 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp22.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp22.c @@ -1,6 +1,6 @@ /* See backwards thru casts if the range fits the LHS type. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ extern void kill(int i); extern void keep(int i); @@ -38,6 +38,6 @@ foo (int i) } } -/* { dg-final { scan-tree-dump-times "kill \\(" 0 "evrp"} } */ -/* { dg-final { scan-tree-dump-times "keep \\(" 1 "evrp"} } */ +/* { dg-final { scan-tree-dump-times "kill \\(" 0 "evrp1"} } */ +/* { dg-final { scan-tree-dump-times "keep \\(" 1 "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp3.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp3.c index 805652b956a338590e631bcd0c0f2ffd3a0af3be..e4ef79b38695ecb6527d27c74c87819674aa9738 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp3.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ int foo (int i); void bar (int j) @@ -11,5 +11,5 @@ void bar (int j) } } -/* { dg-final { scan-tree-dump "\\\[1, 10\\\]" "evrp" } } */ +/* { dg-final { scan-tree-dump "\\\[1, 10\\\]" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp30.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp30.c index 2c5ff41ecd9403597e654799095cb7b4d1b47381..a80ca9bfab4623ca21c8ff546b6e44798e5284c5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp30.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp30.c @@ -1,6 +1,6 @@ /* Confirm the ranger is picking up a relationship with equivalences. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ extern void foo (); @@ -12,5 +12,5 @@ void f (unsigned int a, unsigned int b) foo (); /* Unreachable */ } -/* { dg-final { scan-tree-dump-times "foo\\(" 0 "evrp"} } */ +/* { dg-final { scan-tree-dump-times "foo\\(" 0 "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp4.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp4.c index e3f4531b16c938f2cd1b6dffcf86db2a3951d922..500a6828a8f4037f0b653ebf051e63101608a524 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp4.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ int foo (int *p); @@ -17,4 +17,4 @@ int bar (struct st *s) foo (&s->a); } -/* { dg-final { scan-tree-dump "\\\[1B, \\+INF\\\]" "evrp" } } */ +/* { dg-final { scan-tree-dump "\\\[1B, \\+INF\\\]" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c index aaeec68866e25758387936e2da2f14de96950441..27d569090e40071a2088473710950676f6dc5c68 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ extern void abort (void); @@ -18,4 +18,4 @@ foo (int k, int j) return j; } -/* { dg-final { scan-tree-dump "\\\[12, \\+INF" "evrp" } } */ +/* { dg-final { scan-tree-dump "\\\[12, \\+INF" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp7.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp7.c index 16fbe65e4d9e69c465bc76078f81e4ff7a222e18..3c193d0cce5b171ec916cb61b5cc3e06cf897a97 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp7.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp7.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ int test1(int i, int k) { @@ -11,4 +11,4 @@ int test1(int i, int k) return 1; } -/* { dg-final { scan-tree-dump "Removing dead stmt \[^\r\n\]* = j_.* == 10" "evrp" } } */ +/* { dg-final { scan-tree-dump "Removing dead stmt \[^\r\n\]* = j_.* == 10" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp8.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp8.c index b7e5c7aa2de8f090a6e0d6ec17aff878fb48909c..a0690e1e2f7034abf3a309f4d99cdcc2bdcc586c 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp8.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp8.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ int foo(int i) { @@ -8,4 +8,4 @@ int foo(int i) return 1; } -/* { dg-final { scan-tree-dump "Removing dead stmt \[^\r\n\]* = i_.* == 1" "evrp" } } */ +/* { dg-final { scan-tree-dump "Removing dead stmt \[^\r\n\]* = i_.* == 1" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp9.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp9.c index fb7c319fc4359a9e476e47954824589fdc326515..853fd9b72fa86dd09e28deeb418bc4e5f008d631 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/evrp9.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp9.c @@ -1,6 +1,6 @@ /* PR tree-optimization/49039 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ extern void bar (void); @@ -24,7 +24,7 @@ foo (unsigned int x, unsigned int y) bar (); } -/* { dg-final { scan-tree-dump-times "Folding predicate minv_.* == 5 to 0" 1 "evrp" } } */ -/* { dg-final { scan-tree-dump-times "Folding predicate minv_.* == 6 to 0" 1 "evrp" } } */ -/* { dg-final { scan-tree-dump-times "Folding predicate maxv_.* == 5 to 0" 1 "evrp" } } */ -/* { dg-final { scan-tree-dump-times "Folding predicate maxv_.* == 6 to 0" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Folding predicate minv_.* == 5 to 0" 1 "evrp1" } } */ +/* { dg-final { scan-tree-dump-times "Folding predicate minv_.* == 6 to 0" 1 "evrp1" } } */ +/* { dg-final { scan-tree-dump-times "Folding predicate maxv_.* == 5 to 0" 1 "evrp1" } } */ +/* { dg-final { scan-tree-dump-times "Folding predicate maxv_.* == 6 to 0" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/foldconst-4.c b/gcc/testsuite/gcc.dg/tree-ssa/foldconst-4.c index 0e9b676f66bd10112cad6fe543ecafc49f064aa0..f58bf667f2b97bd36edc6f2a585c717dee9d6306 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/foldconst-4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/foldconst-4.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O -fdump-tree-ccp2" } */ +/* { dg-options "-O -fdump-tree-ccp3" } */ struct a {int a,b;}; const static struct a a; @@ -10,4 +10,4 @@ test() { return a.a+b[c]; } -/* { dg-final { scan-tree-dump "return 0;" "ccp2" } } */ +/* { dg-final { scan-tree-dump "return 0;" "ccp3" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-33.c b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-33.c index c7124deee11f3655d7c50bd9605bb7caddba6470..ab8b5ba3bb392fc90edbebe710f708a25d3e350a 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-33.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-33.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-forwprop3" } */ +/* { dg-options "-O2 -fdump-tree-forwprop5" } */ unsigned short test1 (unsigned short a) @@ -9,7 +9,7 @@ test1 (unsigned short a) a |= 0x8000; /* Simplify to ((a >> 1) ^ 0xa001). */ return a; } -/* { dg-final { scan-tree-dump "\\^ 40961" "forwprop3" } } */ +/* { dg-final { scan-tree-dump "\\^ 40961" "forwprop5" } } */ unsigned short test2 (unsigned short a) @@ -19,7 +19,7 @@ test2 (unsigned short a) a ^= 0x0001; /* Simplify to ((a << 1) | 0x8005). */ return a; } -/* { dg-final { scan-tree-dump "\\| 32773" "forwprop3" } } */ +/* { dg-final { scan-tree-dump "\\| 32773" "forwprop5" } } */ unsigned short test3 (unsigned short a) @@ -29,7 +29,7 @@ test3 (unsigned short a) a |= 0xc031; /* Simplify to ((a & 0xd123) | 0xe071). */ return a; } -/* { dg-final { scan-tree-dump "\\| 57457" "forwprop3" } } */ +/* { dg-final { scan-tree-dump "\\| 57457" "forwprop5" } } */ unsigned short test4 (unsigned short a) @@ -39,7 +39,7 @@ test4 (unsigned short a) a |= 0x8000; return a; } -/* { dg-final { scan-tree-dump "\\^ 49153" "forwprop3" } } */ +/* { dg-final { scan-tree-dump "\\^ 49153" "forwprop5" } } */ unsigned short test5 (unsigned short a) @@ -49,8 +49,8 @@ test5 (unsigned short a) a |= 0x8001; /* Only move shift inward: (((a >> 1) ^ 0x4001) | 0x8001). */ return a; } -/* { dg-final { scan-tree-dump "\\^ 16385" "forwprop3" } } */ -/* { dg-final { scan-tree-dump "\\| 32769" "forwprop3" } } */ +/* { dg-final { scan-tree-dump "\\^ 16385" "forwprop5" } } */ +/* { dg-final { scan-tree-dump "\\| 32769" "forwprop5" } } */ short test6 (short a) @@ -59,7 +59,7 @@ test6 (short a) a >>= 2; return a; } -/* { dg-final { scan-tree-dump "\\& 8191" "forwprop3" } } */ +/* { dg-final { scan-tree-dump "\\& 8191" "forwprop5" } } */ short test7 (short a) @@ -68,4 +68,4 @@ test7 (short a) a >>= 2; return a; } -/* { dg-final { scan-tree-dump "\\& -7169" "forwprop3" } } */ +/* { dg-final { scan-tree-dump "\\& -7169" "forwprop5" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c b/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c index f5cd23ab24201f04070a58386d0dae3214cd4193..37dcb2a5fab5a26b9a948f7dfe0c5959e24c1ed2 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdelete-null-pointer-checks -fisolate-erroneous-paths-attribute -fdump-tree-isolate-paths -fdump-tree-forwprop3" } */ +/* { dg-options "-O2 -fdelete-null-pointer-checks -fisolate-erroneous-paths-attribute -fdump-tree-isolate-paths -fdump-tree-forwprop5" } */ /* { dg-skip-if "" keeps_null_pointer_checks } */ @@ -37,6 +37,6 @@ bar (void) We also verify that after isolation cprop simplifies the return statement so that it returns &z directly. */ /* { dg-final { scan-tree-dump-times "__builtin_trap" 2 "isolate-paths"} } */ -/* { dg-final { scan-tree-dump-times "return &z;" 1 "forwprop3"} } */ +/* { dg-final { scan-tree-dump-times "return &z;" 1 "forwprop5"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c b/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c index f357e16d3d7cc3d4f1892ea3d70c537b6ffcac52..978d2936f1fcf35ff76d4ecce04aef6dd405d171 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdelete-null-pointer-checks -fisolate-erroneous-paths-attribute -fdump-tree-isolate-paths -fdump-tree-ccp3" } */ +/* { dg-options "-O2 -fdelete-null-pointer-checks -fisolate-erroneous-paths-attribute -fdump-tree-isolate-paths -fdump-tree-ccp4" } */ /* { dg-skip-if "" keeps_null_pointer_checks } */ @@ -26,6 +26,6 @@ bar (void) We also verify that after isolation phi-cprop simplifies the return statement so that it returns &z directly. */ /* { dg-final { scan-tree-dump-times "__builtin_trap" 2 "isolate-paths"} } */ -/* { dg-final { scan-tree-dump-times "foo .&z.;" 1 "ccp3"} } */ +/* { dg-final { scan-tree-dump-times "foo .&z.;" 1 "ccp4"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr14814.c b/gcc/testsuite/gcc.dg/tree-ssa/pr14814.c index 60d9649a189aeec8e60675642b5d19e4988990ad..5df4e49c22085fe5b02aac05bf3453f435207cc1 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr14814.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr14814.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-sra -fdump-tree-forwprop2" } */ +/* { dg-options "-O2 -fno-tree-sra -fdump-tree-forwprop4" } */ struct YY { double e[3]; }; @@ -18,4 +18,4 @@ int foo(const struct XX* r) { return 1; } -/* { dg-final { scan-tree-dump-times "= &" 0 "forwprop2" } } */ +/* { dg-final { scan-tree-dump-times "= &" 0 "forwprop4" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr20318.c b/gcc/testsuite/gcc.dg/tree-ssa/pr20318.c index 80fd72668715909ce45f57553db5e9601ce1adf7..2537855f68771eb34a3839d526e2a993adebeb9c 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr20318.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr20318.c @@ -1,5 +1,5 @@ /* { dg-do compile { target { ! keeps_null_pointer_checks } } } */ -/* { dg-options "-O2 -fdump-tree-original -fdump-tree-vrp1 -fdelete-null-pointer-checks -fdisable-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-original -fdump-tree-vrp1 -fdelete-null-pointer-checks -fdisable-tree-evrp1" } */ extern int* f(int) __attribute__((returns_nonnull)); extern void eliminate (); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr20657.c b/gcc/testsuite/gcc.dg/tree-ssa/pr20657.c index e67823150dab50a1e971f5fc329f096fa064d260..f7cab6fe2a0481552db4150aa2d3d2e18be374ea 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr20657.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr20657.c @@ -3,7 +3,7 @@ statement, which was needed to eliminate the second "if" statement. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-dominator-opts -fno-tree-fre -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fno-tree-dominator-opts -fno-tree-fre -fdump-tree-evrp1" } */ int foo (int a) @@ -14,4 +14,4 @@ foo (int a) return 0; } -/* { dg-final { scan-tree-dump-times "if" 1 "evrp"} } */ +/* { dg-final { scan-tree-dump-times "if" 1 "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr20702.c b/gcc/testsuite/gcc.dg/tree-ssa/pr20702.c index 81129674d8af33bc16155ee13aa61633a5b67e73..9374bcdc3fb8530758aa38565e6e5dddf905fc14 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr20702.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr20702.c @@ -4,7 +4,7 @@ immediate successors of the basic block. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1-details -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp1 -fdump-tree-vrp1-details -fdelete-null-pointer-checks" } */ extern void bar (int); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21001.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21001.c index f9216a44a754233353bee80c29679108301a9d5f..d2c8ecf64793476e6b22d900f0d41466d6cba345 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21001.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21001.c @@ -5,7 +5,7 @@ range information out of the conditional. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp -fno-thread-jumps -fdump-tree-vrp1-details" } */ +/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp1 -fno-thread-jumps -fdump-tree-vrp1-details" } */ /* { dg-additional-options "-fdisable-tree-ethread -fdisable-tree-thread1" } */ int diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21086.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21086.c index 9b93d39d4e440a6c46ebfd79ebb2c7c1bf74dd5e..19f15d0845759b671513c70834bb91ac215d75fa 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21086.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21086.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1 -fdump-tree-dce2 -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp1 -fdump-tree-vrp1 -fdump-tree-dce2 -fdelete-null-pointer-checks" } */ int foo (int *p) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c index 92a87688601366f2b401fd6f3e6dc75c2cd28a51..eaef51dc92caf5eecd0da5e24193b4e9af0f56a6 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21090.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1 -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp1 -fdump-tree-vrp1 -fdelete-null-pointer-checks" } */ int g, h; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21294.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21294.c index 8c8f4479a77cb6e2d1fa1dd3f49f0aeb4384ac96..6a5343fd793b760f44928422591ffdb3ba7ca517 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21294.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21294.c @@ -4,7 +4,7 @@ allows us to eliminate the second "if" statement. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-dominator-opts -fdisable-tree-evrp -fdisable-tree-ethread -fdisable-tree-threadfull1 -fdump-tree-vrp1-details" } */ +/* { dg-options "-O2 -fno-tree-dominator-opts -fdisable-tree-evrp1 -fdisable-tree-ethread -fdisable-tree-threadfull1 -fdump-tree-vrp1-details" } */ struct f { int i; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21458-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21458-2.c index 9610570e2729afffec8adc2849382f0548891cf3..4bdf1b1613bfe7b457169189a993df51d8384191 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21458-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21458-2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details -fdisable-tree-ethread" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details -fdisable-tree-ethread" } */ extern void g (void); extern void bar (int); @@ -16,4 +16,4 @@ foo (int a) } } -/* { dg-final { scan-tree-dump-times "Folding predicate.* to 1" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Folding predicate.* to 1" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21458.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21458.c index 97d17f21cfbbaf2f4fd5aba23574eefe4dc928b1..c6a0c51ffad24fa94023ea77ed31fb7b4d03f47f 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21458.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21458.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ extern void g (void); extern void bar (int); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21559.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21559.c index 83b7c802e358894498e5915edaae2f386ac4fd05..c477fdf75d695911f871078a1ebc538158a08127 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21559.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21559.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ static int blocksize = 4096; @@ -33,4 +33,4 @@ void foo (void) /* First, we should simplify the bits < 0 test within the loop. */ -/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21563.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21563.c index 504b3cce6e6a81e37b73d7142c58606854885293..2863d80e53e7aa8a43492cab08330c9865eb6bf7 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr21563.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr21563.c @@ -2,7 +2,7 @@ Make sure VRP folds the second "if" statement. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1-details" } */ +/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp1 -fdump-tree-vrp1-details" } */ int foo (int a) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr23744.c b/gcc/testsuite/gcc.dg/tree-ssa/pr23744.c index f70f2546bf1b3e06eb0911b12e46dda8deb721cd..0a339f2f700d8980524520c65a0d277fd7c732b6 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr23744.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr23744.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-ccp -fdisable-tree-evrp -fdump-tree-vrp1" } */ +/* { dg-options "-O2 -fno-tree-ccp -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ void h (void); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr25382.c b/gcc/testsuite/gcc.dg/tree-ssa/pr25382.c index 8634c0a78952812313fdababbb1c8169727151f7..a302ec63aa18b1f98499805073561d63d1336c29 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr25382.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr25382.c @@ -3,7 +3,7 @@ Check that VRP now gets ranges from BIT_AND_EXPRs. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-thread-jumps -fno-tree-ccp -fdisable-tree-evrp -fdump-tree-vrp1" } */ +/* { dg-options "-O2 -fno-thread-jumps -fno-tree-ccp -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ int foo (int a) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr45397.c b/gcc/testsuite/gcc.dg/tree-ssa/pr45397.c index 8eacb5187773b530ea89cfc39c8d1f1e06ef7958..79cfc9d00a0c49f629b18f8de6a83ea085f5ab7f 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr45397.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr45397.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-phiopt -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-phiopt -fdump-tree-evrp1" } */ int foo_add (const unsigned char *tmp, int i, int val) { @@ -20,7 +20,7 @@ int foo_mul (const unsigned char *tmp, int i, int val) have no control flow. */ /* EVRP leaves copies in the IL which confuses phiopt1 so we have to rely on phiopt2 instead. */ -/* { dg-final { scan-tree-dump-not " & 255;" "evrp" } } */ +/* { dg-final { scan-tree-dump-not " & 255;" "evrp1" } } */ /* { dg-final { scan-tree-dump-times "MAX_EXPR" 3 "phiopt1" { xfail *-*-* } } } */ /* { dg-final { scan-tree-dump-times "MIN_EXPR" 3 "phiopt1" { xfail *-*-* } } } */ /* { dg-final { scan-tree-dump-not "if " "phiopt1" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr49039.c b/gcc/testsuite/gcc.dg/tree-ssa/pr49039.c index 102b07346b434f96f031276430165d69acebe217..797a7cd444de71615f82d859f7eaf7b468d087f0 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr49039.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr49039.c @@ -1,6 +1,6 @@ /* PR tree-optimization/49039 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1 -fno-thread-jumps" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1 -fno-thread-jumps" } */ extern void bar (void); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr58480.c b/gcc/testsuite/gcc.dg/tree-ssa/pr58480.c index f11623b7c6b61c446d609f1c9e903b04c152c0ae..2767e7ed53cc55da3da378c96f62ce9e713967ab 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr58480.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr58480.c @@ -1,5 +1,5 @@ /* { dg-do compile { target { ! keeps_null_pointer_checks } } } */ -/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp -fdump-tree-vrp1 -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fno-thread-jumps -fdisable-tree-evrp1 -fdump-tree-vrp1 -fdelete-null-pointer-checks" } */ extern void eliminate (void); extern void* f1 (void *a, void *b) __attribute__((nonnull)); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr61839_1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr61839_1.c index f5af7a1d6b6758f6a1566d17138880e429bb0fda..61c2c45910563a0b5d92443a6c655764dc42b3a5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr61839_1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr61839_1.c @@ -1,6 +1,6 @@ /* PR tree-optimization/61839. */ /* { dg-do run } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdisable-tree-ethread -fdisable-tree-threadfull1 -fdump-tree-vrp1 -fdump-tree-optimized" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdisable-tree-ethread -fdisable-tree-threadfull1 -fdump-tree-vrp1 -fdump-tree-optimized" } */ /* { dg-require-effective-target int32plus } */ __attribute__ ((noinline)) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr61839_2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr61839_2.c index 0e0f4c02113163b76147189264c8cb93ed8b029d..ba1e9ba5eb74554a60b68c62a816b94009d953d7 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr61839_2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr61839_2.c @@ -1,6 +1,6 @@ /* PR tree-optimization/61839. */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ /* { dg-require-effective-target int32plus } */ __attribute__ ((noinline)) @@ -72,13 +72,13 @@ int mod (int a, int b) /* EVRP now makes transformations in all functions, leaving a single * builtin_abort call in bar2. */ -/* { dg-final { scan-tree-dump-times "__builtin_abort" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "__builtin_abort" 1 "evrp1" } } */ /* Make sure to optimize 972195717 / 0 in function foo. */ -/* { dg-final { scan-tree-dump-times "972195717 / " 0 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "972195717 / " 0 "evrp1" } } */ /* Make sure to optimize 972195717 % 0 in function bar. */ -/* { dg-final { scan-tree-dump-times "972195717 % " 0 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "972195717 % " 0 "evrp1" } } */ /* Make sure to optimize 972195717 % [1,2] function bar2. */ -/* { dg-final { scan-tree-dump-times "972195715 % " 0 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "972195715 % " 0 "evrp1" } } */ /* [12,12][24,24][48,48] % [0,0][3,3][6,6][12,12] == [0,0] */ -/* { dg-final { scan-tree-dump-times "%" 0 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "%" 0 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr61839_4.c b/gcc/testsuite/gcc.dg/tree-ssa/pr61839_4.c index a346912d12167de3d36ee4d29297095a8236332d..bc0290dc182e77eb085bb07a2243250259467657 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr61839_4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr61839_4.c @@ -1,6 +1,6 @@ /* PR tree-optimization/61839. */ /* { dg-do run } */ -/* { dg-options "-O2 -fdump-tree-vrp1 -fdisable-tree-evrp -fdump-tree-optimized" } */ +/* { dg-options "-O2 -fdump-tree-vrp1 -fdisable-tree-evrp1 -fdump-tree-optimized" } */ /* { dg-require-effective-target int32plus } */ __attribute__ ((noinline)) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c b/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c index b694ec171c156ff65af76135f6d2a26ea51b570c..17d71dcd9bbb9b42ad221e28025bd3f02dae3c58 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ __extension__ typedef __UINT32_TYPE__ uint32_t; @@ -14,6 +14,6 @@ int funsigned2 (uint32_t a) return (-1 * 0x1ffffffffL) / a == 0; } -/* { dg-final { scan-tree-dump "int \\\[2, 8589934591\\\]" "evrp" } } */ -/* { dg-final { scan-tree-dump "int \\\[-8589934591, -2\\\]" "evrp" } } */ +/* { dg-final { scan-tree-dump "int \\\[2, 8589934591\\\]" "evrp1" } } */ +/* { dg-final { scan-tree-dump "int \\\[-8589934591, -2\\\]" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr68431.c b/gcc/testsuite/gcc.dg/tree-ssa/pr68431.c index 9c42563b67b91ca13deff4f7335fed17323f2eb5..1de9aa5402da3c9b0cb6087ab8d8870ebe18b5b5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr68431.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr68431.c @@ -1,5 +1,5 @@ /* PR tree-optimization/68431 */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1-details" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1-details" } */ unsigned int x = 1; int diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr77445-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr77445-2.c index b3db1bca6173d58620a4307fa5fdcf4231761775..de49f747010f1c9c79d6fc78ebefa0f8f9240923 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr77445-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr77445-2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-thread-details-blocks-stats -fdump-tree-threadfull1-blocks-stats -fdump-tree-threadfull2-blocks-stats" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-thread-details-blocks-stats -fdump-tree-threadfull1-blocks-stats -fdump-tree-threadfull2-blocks-stats" } */ typedef enum STATES { START=0, INVALID, diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr78153-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr78153-1.c index 2530ba08e1ca53fd7df7d467b2da5602f42f6fe0..174adca338a4387d15d33e0007154e77989829e6 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr78153-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr78153-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-slim" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-slim" } */ void f(const char *s) { @@ -7,4 +7,4 @@ void f(const char *s) __builtin_abort (); } -/* { dg-final { scan-tree-dump-not "__builtin_abort" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "__builtin_abort" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr78153-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr78153-2.c index de70450f1c9f6715ca72e46823494eeeb2ad3a5d..11ace86d95c6265cde1e98e7d857bdf9950f98e4 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr78153-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr78153-2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-slim" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-slim" } */ void f(const char *s) { @@ -8,4 +8,4 @@ void f(const char *s) __builtin_abort (); } -/* { dg-final { scan-tree-dump-not "__builtin_abort" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "__builtin_abort" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr78154.c b/gcc/testsuite/gcc.dg/tree-ssa/pr78154.c index 3ba8f64e2664bbd9570f28df6699838e01ef8379..6dff0977a8c8d96d23d37bb558d6e295c8be26fa 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr78154.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr78154.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp-slim -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fdump-tree-evrp1-slim -fdelete-null-pointer-checks" } */ /* { dg-skip-if "" { keeps_null_pointer_checks } } */ void f(void *d, const void *s, __SIZE_TYPE__ n) @@ -41,4 +41,4 @@ void f(void *d, const void *s, __SIZE_TYPE__ n) __builtin_abort (); } -/* { dg-final { scan-tree-dump-not "__builtin_abort" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "__builtin_abort" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr78655.c b/gcc/testsuite/gcc.dg/tree-ssa/pr78655.c index e9158e072688e3dc741ac238f206ae3537f0d764..e406f085554a5774c67a01b66ac347edf021fabc 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr78655.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr78655.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-ccp -fno-tree-forwprop -fno-tree-fre -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fno-tree-ccp -fno-tree-forwprop -fno-tree-fre -fdump-tree-evrp1" } */ struct A{int a,b;}; inline int*f1(struct A*p){return&p->a;} /* offset of 0. */ @@ -33,5 +33,5 @@ main() } -/* { dg-final { scan-tree-dump-not "bad" "evrp"} } */ +/* { dg-final { scan-tree-dump-not "bad" "evrp1"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr91029-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr91029-1.c index d52734b20f98d89def89d5c53812e21c52831e34..3e079712b9adaf29aafddd9d292cdc236d79bca5 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr91029-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr91029-1.c @@ -1,6 +1,6 @@ /* PR tree-optimization/91029 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void kill (void); int xx; @@ -65,4 +65,4 @@ void f6 (int i, int j) } } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr91029-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr91029-2.c index ad9213a41b792dda3ad6680b7142d87acd8d689d..8b10645f39a482bb1619505bc54b8c33cc4ef02c 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr91029-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr91029-2.c @@ -1,6 +1,6 @@ /* PR tree-optimization/91029 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void kill (void); int xx; @@ -95,4 +95,4 @@ void f9 (unsigned int i, unsigned int j) } } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr93781-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr93781-1.c index b2505f3959de69447629ecde09c8e79f64169d94..9f0bb21a742a8183085ba4b048fc5992a97dd22b 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr93781-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr93781-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void kill (void); @@ -16,5 +16,5 @@ void foo (unsigned int arg) kill ();; } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr93781-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr93781-2.c index c9b28783c1249641ad94b5a05c4b81fd3fb40c90..fc4f2924839f8271111e98f543efa25f328587fb 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr93781-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr93781-2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void kill (void); @@ -14,4 +14,4 @@ void foo (unsigned int arg) kill (); } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr93781-3.c b/gcc/testsuite/gcc.dg/tree-ssa/pr93781-3.c index e1d2be0ea7fc031de7db5275bb579e66da2afb2f..0f9298bd612b96ef2ec0055a5c3a17c2c9564aab 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr93781-3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr93781-3.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void kill (void); @@ -18,4 +18,4 @@ void foo (unsigned int arg) kill (); } -/* { dg-final { scan-tree-dump-not "kill" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "kill" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr95906.c b/gcc/testsuite/gcc.dg/tree-ssa/pr95906.c index 3d820a58e9303823e146559ad3dfa1e2d714c81e..c9543d04e41c04eaa22109ddc361135e03449a3b 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr95906.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr95906.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-forwprop3-raw -w -Wno-psabi" } */ +/* { dg-options "-O2 -fdump-tree-forwprop5-raw -w -Wno-psabi" } */ // FIXME: this should further optimize to a MAX_EXPR typedef signed char v16i8 __attribute__((vector_size(16))); @@ -9,5 +9,5 @@ v16i8 f(v16i8 a, v16i8 b) return (cmp & a) | (~cmp & b); } -/* { dg-final { scan-tree-dump-not "bit_(and|ior)_expr" "forwprop3" } } */ -/* { dg-final { scan-tree-dump-times "vec_cond_expr" 1 "forwprop3" } } */ +/* { dg-final { scan-tree-dump-not "bit_(and|ior)_expr" "forwprop5" } } */ +/* { dg-final { scan-tree-dump-times "vec_cond_expr" 1 "forwprop5" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr98513.c b/gcc/testsuite/gcc.dg/tree-ssa/pr98513.c index c15d6bd708e9beedac4b9089d9a970e36c0dbbee..8a32f2c0e6a01ce2fc8b7aadce5ae13c09131e35 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr98513.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr98513.c @@ -2,7 +2,7 @@ /* { dg-options "-O2 -fgimple" } */ __attribute__((noipa)) -void __GIMPLE (ssa,startwith("evrp")) +void __GIMPLE (ssa,startwith("evrp1")) foo (int x, int minus_1) { int tem; diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-29.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-29.c index 3c1a848f50bc48914627d537430615397448e462..c93a8d0d23a7468787697ccf556dbd591d4759c8 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-29.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-29.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O -fdump-tree-ccp2" } */ +/* { dg-options "-O -fdump-tree-ccp3" } */ static double num; int foo (void) @@ -7,4 +7,4 @@ int foo (void) return *(unsigned *)# } -/* { dg-final { scan-tree-dump "return 0;" "ccp2" } } */ +/* { dg-final { scan-tree-dump "return 0;" "ccp3" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-3.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-3.c index 1c8c318ce4f0f6e8ef1acf48d296a8ef974760a8..48427de64934b45d058f1bbff49f185466f9c50d 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-3.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O -fdump-tree-ccp2" } */ +/* { dg-options "-O -fdump-tree-ccp3" } */ extern void link_error (void); @@ -133,4 +133,4 @@ int* test666 (int * __restrict__ rp1, int * __restrict__ rp2, int *p1) optimization has failed */ /* ??? While we indeed don't handle some of these, a couple of the restrict tests are incorrect. */ -/* { dg-final { scan-tree-dump-times "link_error" 0 "ccp2" { xfail *-*-* } } } */ +/* { dg-final { scan-tree-dump-times "link_error" 0 "ccp3" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c index 9f56b392cdd9d2abed37b4e0514b41ad7bef94de..1401d23e1a6493a34b51bb48d2d95af6a25ac546 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-30.c @@ -28,8 +28,8 @@ void test_bzero (void) /* { dg-final { scan-tree-dump-times "builtin_memset" 1 "dse1" } } */ -/* Merging the evrp folder into substitute_and_fold_engine shuffled - the order of gimple_fold a bit, so evrp is no longer folding the +/* Merging the evrp1 folder into substitute_and_fold_engine shuffled + the order of gimple_fold a bit, so evrp1 is no longer folding the memmove inline. This folding is instead done by forwprop. Thus, I have remmoved the |memmove in the test below as this is not done until after dse. diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-54.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-54.c index 02ebf068a619033119cb9a84842afb98602b9c3b..f0a1fab4cce08554096e1774871b7fbb6dc36f87 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-54.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-54.c @@ -1,6 +1,6 @@ /* { dg-do run } */ /* { dg-require-effective-target int32plus } */ -/* { dg-options "-O -fdump-tree-forwprop4 -fdump-tree-dse1" } */ +/* { dg-options "-O -fdump-tree-forwprop6 -fdump-tree-dse1" } */ extern void abort (void); @@ -51,6 +51,6 @@ int main() return 0; } -/* { dg-final { scan-tree-dump "\\(char\\) i_" "forwprop4" } } */ -/* { dg-final { scan-tree-dump "\\(short int\\) i_" "forwprop4" } } */ +/* { dg-final { scan-tree-dump "\\(char\\) i_" "forwprop6" } } */ +/* { dg-final { scan-tree-dump "\\(short int\\) i_" "forwprop6" } } */ /* { dg-final { scan-tree-dump-not "u.i =" "dse1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-sink-19.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-sink-19.c index e98d13fe85bbbfc9b2ff45f23d775f1e8f5265f1..ad6eb62700284b8bce90f138960d1a6dd2513b18 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-sink-19.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-sink-19.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-sink1-details -fdump-tree-cddce2-details" } */ +/* { dg-options "-O2 -fdump-tree-sink1-details -fdump-tree-cddce3-details" } */ static int b=4; int c; @@ -18,4 +18,4 @@ main() applying store motion to c and b. */ /* { dg-final { scan-tree-dump "Sinking # VUSE" "sink1" } } */ /* And remove the loop after final value replacement. */ -/* { dg-final { scan-tree-dump "fix_loop_structure: removing loop" "cddce2" } } */ +/* { dg-final { scan-tree-dump "fix_loop_structure: removing loop" "cddce3" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp02.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp02.c index 6e9c8df2f0a2336c1caa8bbd88ec35b258a13231..df83d6a45aef17acfb1bffb529b5ff1ca838c114 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp02.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp02.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-vrp1 -fdelete-null-pointer-checks -fdisable-tree-evrp -fno-thread-jumps" } */ +/* { dg-options "-O2 -fdump-tree-vrp1 -fdelete-null-pointer-checks -fdisable-tree-evrp1 -fno-thread-jumps" } */ struct A { diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c index 4cbaca4133212254a05276693f4d988dd699970c..8897d85bde313fd79e87d03bdd2603037ddf94f7 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1 -fno-thread-jumps" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1 -fno-thread-jumps" } */ struct A { diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp06.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp06.c index 898477e42fbceed499e958759aa3957ff04e12d0..59c4319113d2ee81e093b8f088dd8cb96a79c7c3 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp06.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp06.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1 -fno-thread-jumps" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1 -fno-thread-jumps" } */ int baz (void); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp07.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp07.c index ec5f6cce46fe995719a2670cf97b3527cc8b1fc1..f53ea54cca753acb406a72e1683344728082cc46 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp07.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp07.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp -fdump-tree-vrp1-details -fdelete-null-pointer-checks -fno-thread-jumps" } */ +/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp1 -fdump-tree-vrp1-details -fdelete-null-pointer-checks -fno-thread-jumps" } */ int foo (int i, int *p) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp08.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp08.c index c9d9023671559d00cf1d54591a4d3545cee65af8..a9bc856865a639b2560df3c0fff744d965e6b66f 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp08.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp08.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp -fdump-tree-vrp1-details -fno-thread-jumps -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp1 -fdump-tree-vrp1-details -fno-thread-jumps -fdelete-null-pointer-checks" } */ /* Compile with -fno-tree-fre -O2 to prevent CSEing *p. */ int diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp09.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp09.c index 354169692d66be185a02e37e578d9c18deb017d0..48a79e42824449b25ea30dc6368acb154b56118f 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp09.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp09.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp -fdump-tree-vrp1 -std=gnu89 -fno-thread-jumps" } */ +/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp1 -fdump-tree-vrp1 -std=gnu89 -fno-thread-jumps" } */ foo (int *p) { diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp111.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp111.c index cae2bc75aff9a37bcf7adeb942af2653648eddb8..a4e35833a4081689cb9e51e7f89e7b488a6abd93 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp111.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp111.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fdelete-null-pointer-checks" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fdelete-null-pointer-checks" } */ /* { dg-skip-if "" { keeps_null_pointer_checks } } */ void foo (void *p) __attribute__((nonnull(1))); @@ -11,4 +11,4 @@ void bar (void *p) __builtin_abort (); } -/* { dg-final { scan-tree-dump-not "abort" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "abort" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp113.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp113.c index dfe4989d313d98ae89f10884327c3a878cfed119..dc7e5e583102f09064134b795809c3710cb477df 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp113.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp113.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-vrp1 -fdisable-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-vrp1 -fdisable-tree-evrp1" } */ int f(int a) { switch (a & 1) { diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp114.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp114.c index 5c2c1a0b500b3290234e975fb826321cf69f0659..33adac61aba53474c0cd386bd22a9d2f492d5702 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp114.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp114.c @@ -1,5 +1,5 @@ /* { dg-do link { target int32plus } } */ -/* { dg-options "-O2 -fdump-tree-fre1 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-fre1 -fdump-tree-evrp1" } */ extern void link_error (); void foo (int a) @@ -21,4 +21,4 @@ int main() } /* { dg-final { scan-tree-dump-times "link_error" 1 "fre1" } } */ -/* { dg-final { scan-tree-dump-times "link_error" 0 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "link_error" 0 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp115.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp115.c index 6d1c9c50b37c4512d97d7ab874e0e0832a71da6f..47fecebe1ca824bf51e8d364f5394a8b2969b129 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp115.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp115.c @@ -1,7 +1,7 @@ /* PR tree-optimization/80558 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ -/* { dg-final { scan-tree-dump-not "link_error" "evrp" } } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ +/* { dg-final { scan-tree-dump-not "link_error" "evrp1" } } */ void link_error (void); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp117.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp117.c index d07a6722cd7ab346c9a2e2cb5d1ff1b6e23505da..47194eec09b0bb6099271fedec00cef8804ac5b2 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp117.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp117.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ void link_error (void); @@ -13,4 +13,4 @@ void foo (int i) } } -/* { dg-final { scan-tree-dump-not "link_error" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "link_error" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp120.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp120.c index 4dcee2341f77856d0194bf1e7882e5887d6be712..b4ea5b1c3aeafddb4e89addb89ca0f8ecb1c2f32 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp120.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp120.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ #include "vrp113.c" -/* { dg-final { scan-tree-dump "return 3;" "evrp" } } */ +/* { dg-final { scan-tree-dump "return 3;" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp16.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp16.c index d09f3aea46aa3eb4c501e215b4f5d01aa7c211f5..2243a87c3d40691399f416c9a1062e4ede0f0355 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp16.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp16.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-fre -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fno-tree-fre -fdump-tree-evrp1" } */ extern void abort (void) __attribute__ ((__noreturn__)); @@ -19,5 +19,5 @@ nonlocal_mentioned_p (rtx x) abort (); } -/* { dg-final { scan-tree-dump-times "if" 0 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "if" 0 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp17.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp17.c index b8470e7a3dbeb413859c68e93488b783baff1e0b..3c0ec8c11669dfe13e433c956f9ac4e175caec5e 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp17.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp17.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ extern void abort (void) __attribute__ ((__noreturn__)); union tree_node; @@ -27,5 +27,5 @@ gimplify_for_stmt (tree stmt) abort (); } -/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp18.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp18.c index d7ab3f69f3798731a26b6e5cf2265576dd83f954..393e7d402fb626835d8c00ad4817c76fd720d901 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp18.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp18.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp" } */ +/* { dg-options "-O2 -fdump-tree-evrp1" } */ static int blocksize = 4096; @@ -30,4 +30,4 @@ void foo (void) eof_reached = 1; } -/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp19.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp19.c index 98a8da6d05e9f1ac4313f02b9286d4f500b17e53..ea95ecb91c3f8292acb268ad0e6ffe741169b878 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp19.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp19.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fwrapv -O1 -ftree-vrp -fdisable-tree-evrp -fdump-tree-vrp1 -fdisable-tree-ethread -fdisable-tree-thread1" } */ +/* { dg-options "-fwrapv -O1 -ftree-vrp -fdisable-tree-evrp1 -fdump-tree-vrp1 -fdisable-tree-ethread -fdisable-tree-thread1" } */ #include extern void abort (); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp20.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp20.c index e5822992fe485a55b0196a8c5616f65d6e86f6df..52e489f9f424b78dcb99b6d00ea1ee9e77a7a8c7 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp20.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp20.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fwrapv -O1 -fno-tree-fre -fdisable-tree-evrp -ftree-vrp -fdump-tree-vrp1 -fdisable-tree-ethread -fdisable-tree-thread1" } */ +/* { dg-options "-fwrapv -O1 -fno-tree-fre -fdisable-tree-evrp1 -ftree-vrp -fdump-tree-vrp1 -fdisable-tree-ethread -fdisable-tree-thread1" } */ extern void abort (); extern void exit (int); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp23.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp23.c index 6ac8d55600d21ac359d13524999b38551dccd280..fa78a4c7f9b0097cdbf76be7818240b9cfa0d294 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp23.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp23.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-forwprop -fdump-tree-evrp-details" } */ +/* { dg-options "-O2 -fno-tree-forwprop -fdump-tree-evrp1-details" } */ void aa (void); void aos (void); @@ -45,5 +45,5 @@ L8: /* The n_sets > 0 test can be simplified into n_sets == 1 since the only way to reach the test is when n_sets <= 1, and the only value which satisfies both conditions is n_sets == 1. */ -/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Simplified relational" 1 "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp24.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp24.c index 91015da86aebe385c6d47256d2e8e1e0782bc640..b4a0ecc5cb23e7caa284b4648a12b630cae34c39 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp24.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp24.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fno-tree-forwprop -fdump-tree-evrp-details -fdump-tree-optimized -fno-tree-ccp" } */ +/* { dg-options "-O2 -fno-tree-forwprop -fdump-tree-evrp1-details -fdump-tree-optimized -fno-tree-ccp" } */ struct rtx_def; @@ -88,6 +88,6 @@ L7: n_sets can only have the values [0, 1] as it's the result of a boolean operation. */ -/* { dg-final { scan-tree-dump-times "Simplified relational" 2 "evrp" } } */ +/* { dg-final { scan-tree-dump-times "Simplified relational" 2 "evrp1" } } */ /* { dg-final { scan-tree-dump-times "if " 4 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp33.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp33.c index 470675ef50e5b1530afdd0987e2ac3222c6cc60c..82c63f556dd70b2b26113f7c8dfa37ead11b06a8 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp33.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp33.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-vrp1 -fno-tree-fre -fdisable-tree-evrp -fno-thread-jumps" } */ +/* { dg-options "-O2 -fdump-tree-vrp1 -fno-tree-fre -fdisable-tree-evrp1 -fno-thread-jumps" } */ /* This is from PR14052. */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp35.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp35.c index a372a18cc4355ea26a518476dab8ffdd0ab42a03..502070634d8568e7341c46b182a3250e1c1a0266 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp35.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp35.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1-details" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1-details" } */ int test1(int i, int k) { diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp36.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp36.c index 1f77b539d70e07669d1c85960a9387f947362d32..bff236e2c2473557a5306bd7bdce63933a74740b 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp36.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp36.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fdump-tree-vrp1-details" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1-details" } */ int foo(int i) { diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp92.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp92.c index 9e53547dadc3c62d527601221d46138caf381b07..7c3988ce0e02b92804da7ada08ca2bb71d92fe3b 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp92.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp92.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdisable-tree-evrp -fno-tree-fre -fdump-tree-vrp1 -fdisable-tree-ethread" } */ +/* { dg-options "-O2 -fdisable-tree-evrp1 -fno-tree-fre -fdump-tree-vrp1 -fdisable-tree-ethread" } */ void bar (void); int foo (int i, int j) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp98-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp98-1.c index daa3f073b9239405ac15469b9c180c2fafd9bd12..290a83a8f37dea09701a6f7bf9aad40b2d58768f 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp98-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp98-1.c @@ -1,6 +1,6 @@ /* { dg-do compile } */ /* { dg-require-effective-target int128 } */ -/* { dg-options "-Os -fdump-tree-evrp-details" } */ +/* { dg-options "-Os -fdump-tree-evrp1-details" } */ #include #include @@ -36,6 +36,6 @@ foo (bigger_than_word a, word b, uint8_t c) return ret; } -/* { dg-final { scan-tree-dump "Folded into: if \\(_\[0-9\]+ == 1\\)" "evrp" } } */ -/* { dg-final { scan-tree-dump-not "Folded into: if \\(_\[0-9\]+ == 2\\)" "evrp" } } */ -/* { dg-final { scan-tree-dump "Folded into: if \\(_\[0-9\]+ == 3\\)" "evrp" } } */ +/* { dg-final { scan-tree-dump "Folded into: if \\(_\[0-9\]+ == 1\\)" "evrp1" } } */ +/* { dg-final { scan-tree-dump-not "Folded into: if \\(_\[0-9\]+ == 2\\)" "evrp1" } } */ +/* { dg-final { scan-tree-dump "Folded into: if \\(_\[0-9\]+ == 3\\)" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp98.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp98.c index 78d3bbaf4999a02279b4983b3e24d6a3bf692314..b57c160d576187d16fef87ce3b3b23ec7458dd6e 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/vrp98.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp98.c @@ -1,6 +1,6 @@ /* { dg-do compile } */ /* { dg-require-effective-target int128 } */ -/* { dg-options "-Os -fdisable-tree-evrp -fdump-tree-vrp1-details" } */ +/* { dg-options "-Os -fdisable-tree-evrp1 -fdump-tree-vrp1-details" } */ #include #include diff --git a/gcc/testsuite/gcc.dg/vrp-min-max-1.c b/gcc/testsuite/gcc.dg/vrp-min-max-1.c index b9c8379c832b5a3628f599bc50903d5a9219e5e7..93ff9f8d7045dc197e36102e3ce01237735c260a 100644 --- a/gcc/testsuite/gcc.dg/vrp-min-max-1.c +++ b/gcc/testsuite/gcc.dg/vrp-min-max-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-vrp1 -fdisable-tree-evrp -fdump-tree-mergephi2" } */ +/* { dg-options "-O2 -fdump-tree-vrp1 -fdisable-tree-evrp1 -fdump-tree-mergephi2" } */ int bar (void); diff --git a/gcc/testsuite/gcc.dg/vrp-min-max-3.c b/gcc/testsuite/gcc.dg/vrp-min-max-3.c index 1fffee7bbd97da7e66393151442f88095a04f515..ab1fc1de0b1772972da160ca363579b5d369c57c 100644 --- a/gcc/testsuite/gcc.dg/vrp-min-max-3.c +++ b/gcc/testsuite/gcc.dg/vrp-min-max-3.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-evrp -fdump-tree-fre1" } */ +/* { dg-options "-O2 -fdump-tree-evrp1 -fdump-tree-fre1" } */ int bar (void); @@ -23,5 +23,5 @@ int foo2 (int x, int y) /* { dg-final { scan-tree-dump-times "MIN_EXPR" 1 "fre1" } } */ /* { dg-final { scan-tree-dump-times "MAX_EXPR" 1 "fre1" } } */ -/* { dg-final { scan-tree-dump-not "MIN_EXPR" "evrp" } } */ -/* { dg-final { scan-tree-dump-not "MAX_EXPR" "evrp" } } */ +/* { dg-final { scan-tree-dump-not "MIN_EXPR" "evrp1" } } */ +/* { dg-final { scan-tree-dump-not "MAX_EXPR" "evrp1" } } */ diff --git a/gcc/testsuite/gcc.target/i386/vect-gather-1.c b/gcc/testsuite/gcc.target/i386/vect-gather-1.c index 261b66be061dfb8fd5e6fac622cea61d4954e1ec..76c9322a4de0392867f16d745b125ce043eae181 100644 --- a/gcc/testsuite/gcc.target/i386/vect-gather-1.c +++ b/gcc/testsuite/gcc.target/i386/vect-gather-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-Ofast -msse2 -fdump-tree-vect-details -fdump-tree-forwprop4" } */ +/* { dg-options "-Ofast -msse2 -fdump-tree-vect-details -fdump-tree-forwprop6" } */ #ifndef INDEXTYPE #define INDEXTYPE int @@ -17,4 +17,4 @@ double vmul(INDEXTYPE *rowstart, INDEXTYPE *rowend, even with plain SSE2. */ /* { dg-final { scan-tree-dump "loop vectorized" "vect" } } */ /* The index vector loads and promotions should be scalar after forwprop. */ -/* { dg-final { scan-tree-dump-not "vec_unpack" "forwprop4" } } */ +/* { dg-final { scan-tree-dump-not "vec_unpack" "forwprop6" } } */ diff --git a/gcc/testsuite/gcc.target/powerpc/vect-gather-1.c b/gcc/testsuite/gcc.target/powerpc/vect-gather-1.c index bf98045ab03cf59439e718c125575ebfe0f4003a..bf00093c362e0e8d9d0811a1f2da2ab1bdecb85d 100644 --- a/gcc/testsuite/gcc.target/powerpc/vect-gather-1.c +++ b/gcc/testsuite/gcc.target/powerpc/vect-gather-1.c @@ -1,6 +1,6 @@ /* { dg-do compile } */ /* Profitable from Power8 since it supports efficient unaligned load. */ -/* { dg-options "-Ofast -mdejagnu-cpu=power8 -fdump-tree-vect-details -fdump-tree-forwprop4" } */ +/* { dg-options "-Ofast -mdejagnu-cpu=power8 -fdump-tree-vect-details -fdump-tree-forwprop6" } */ #ifndef INDEXTYPE #define INDEXTYPE unsigned int @@ -17,4 +17,4 @@ double vmul(INDEXTYPE *rowstart, INDEXTYPE *rowend, /* With gather emulation this should be profitable to vectorize from Power8. */ /* { dg-final { scan-tree-dump "loop vectorized" "vect" } } */ /* The index vector loads and promotions should be scalar after forwprop. */ -/* { dg-final { scan-tree-dump-not "vec_unpack" "forwprop4" } } */ +/* { dg-final { scan-tree-dump-not "vec_unpack" "forwprop6" } } */ diff --git a/gcc/testsuite/gfortran.dg/pr45636.f90 b/gcc/testsuite/gfortran.dg/pr45636.f90 index 958833c35bc44e594520b18f60a4f884b563812a..304efa7c6defc8caaf6e512df9d069c14587b704 100644 --- a/gcc/testsuite/gfortran.dg/pr45636.f90 +++ b/gcc/testsuite/gfortran.dg/pr45636.f90 @@ -1,6 +1,6 @@ ! PR fortran/45636 ! { dg-do compile } -! { dg-options "-O2 -fdump-tree-forwprop2" } +! { dg-options "-O2 -fdump-tree-forwprop4" } ! PR 45636 - make sure no memset is needed for a short right-hand side. program main character(len=2), parameter :: x='a ' @@ -12,4 +12,4 @@ program main end program main ! This test will fail on targets which prefer memcpy/memset over ! move_by_pieces/store_by_pieces. -! { dg-final { scan-tree-dump-times "memset" 0 "forwprop2" { xfail { { hppa*-*-* && { ! lp64 } } || { mips*-*-* && { ! nomips16 } } } } } } +! { dg-final { scan-tree-dump-times "memset" 0 "forwprop4" { xfail { { hppa*-*-* && { ! lp64 } } || { mips*-*-* && { ! nomips16 } } } } } } diff --git a/gcc/timevar.def b/gcc/timevar.def index d40738cdf55374f6907560ed4d8151e58ef12029..33eec6b86c5557e3d7fb5a6017f3014e0597635d 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -80,6 +80,9 @@ DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp") DEFTIMEVAR (TV_IPA_INLINING , "ipa inlining heuristics") DEFTIMEVAR (TV_IPA_FNSPLIT , "ipa function splitting") DEFTIMEVAR (TV_IPA_COMDATS , "ipa comdats") +DEFTIMEVAR (TV_IPA_ALIGNMENT_PROPAGATION, "ipa alignment propagation") +DEFTIMEVAR (TV_IPA_LOCALIZE_ARRAY , "ipa localize array") +DEFTIMEVAR (TV_IPA_ARRAY_DSE , "ipa array dse") DEFTIMEVAR (TV_IPA_HARDWARE_DETECTION, "ipa detection") DEFTIMEVAR (TV_IPA_STRUCT_REORG , "ipa struct reorg optimization") DEFTIMEVAR (TV_IPA_OPT , "ipa various optimizations") diff --git a/gcc/toplev.cc b/gcc/toplev.cc index 055e0642f775d5e6d0aa80863678cf492a4e1657..b4328fb1c4ddaf4dfb35be927dfe34f3df11cac6 100644 --- a/gcc/toplev.cc +++ b/gcc/toplev.cc @@ -88,6 +88,7 @@ along with GCC; see the file COPYING3. If not see #include "ipa-modref.h" #include "ipa-param-manipulation.h" #include "dbgcnt.h" +#include "ai4c-infer.h" #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) #include "dbxout.h" @@ -2265,6 +2266,9 @@ toplev::main (int argc, char **argv) UNKNOWN_LOCATION, global_dc, targetm.target_option.override); + char *compiler = xstrdup (argv[0]); + handle_lto_options(&global_options, compiler); + handle_common_deferred_options (); init_local_tick (); diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 3a2521eac816adfd96bd8b59e1e51a3066150dbe..dfd5fd706aaebe515c542bb0288bbec70e812a1b 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -473,6 +473,10 @@ extern gimple_opt_pass *make_pass_remove_cgraph_callee_edges (gcc::context *ctxt); extern gimple_opt_pass *make_pass_build_cgraph_edges (gcc::context *ctxt); extern gimple_opt_pass *make_pass_local_pure_const (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_alignment_propagation (gcc::context + *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_localize_array (gcc::context *ctxt); +extern simple_ipa_opt_pass *make_pass_ipa_array_dse (gcc::context *ctxt); extern gimple_opt_pass *make_pass_nothrow (gcc::context *ctxt); extern gimple_opt_pass *make_pass_tracer (gcc::context *ctxt); extern gimple_opt_pass *make_pass_warn_restrict (gcc::context *ctxt); diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/pr84955-1.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/pr84955-1.c index 44767cd27c32b8ed4e5f90b801a885dc73a85409..5a9fea46e405dbe3b95ec0629a94070e8107717c 100644 --- a/libgomp/testsuite/libgomp.oacc-c-c++-common/pr84955-1.c +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/pr84955-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-cddce2 -ffinite-loops" } */ +/* { dg-options "-O2 -fdump-tree-cddce3 -ffinite-loops" } */ int f1 (void) @@ -28,4 +28,4 @@ f2 (void) return i + j; } -/* { dg-final { scan-tree-dump-not "if" "cddce2"} } */ +/* { dg-final { scan-tree-dump-not "if" "cddce3"} } */