diff --git a/SOURCES/gcc8-array-dse-optimization.patch b/SOURCES/gcc8-array-dse-optimization.patch new file mode 100644 index 0000000000000000000000000000000000000000..9e0cca7057cef2d38dda640c2c962c65c1d75cc3 --- /dev/null +++ b/SOURCES/gcc8-array-dse-optimization.patch @@ -0,0 +1,7854 @@ +diff --git a/gcc/Makefile.in b/gcc/Makefile.in +index 11f5754f4..835cc8bd3 100644 +--- a/gcc/Makefile.in ++++ b/gcc/Makefile.in +@@ -1355,6 +1355,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 \ +@@ -1592,6 +1595,7 @@ OBJS = \ + typed-splay-tree.o \ + unique-ptr-tests.o \ + valtrack.o \ ++ value-range.o \ + value-prof.o \ + var-tracking.o \ + varasm.o \ +diff --git a/gcc/common.opt b/gcc/common.opt +index 639c92e56..e57c37753 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -1727,6 +1727,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 Report Var(flag_ipa_reorder_fields) Init(0) Optimization + Perform structure fields reorder optimizations. +diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c +index c052537db..5bbb7619e 100644 +--- a/gcc/config/aarch64/aarch64.c ++++ b/gcc/config/aarch64/aarch64.c +@@ -7542,6 +7542,9 @@ 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; +diff --git a/gcc/ipa-alignment-propagation.c b/gcc/ipa-alignment-propagation.c +new file mode 100644 +index 000000000..c634f3237 +--- /dev/null ++++ b/gcc/ipa-alignment-propagation.c +@@ -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 "backend.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->global.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.c b/gcc/ipa-array-dse.c +new file mode 100644 +index 000000000..bdf8599d2 +--- /dev/null ++++ b/gcc/ipa-array-dse.c +@@ -0,0 +1,3366 @@ ++/* 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" ++#include "ipa-param-manipulation.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 int_range ++make_range (HOST_WIDE_INT value) ++{ ++ tree v = build_value (value); ++ return int_range{v, v}; ++} ++ ++static inline int_range ++make_range (HOST_WIDE_INT min, HOST_WIDE_INT max) ++{ ++ return int_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; ++ ++ int_range 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; ++ ++ int_range 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. */ ++ ++int_range ++array_dse_callee::calc_tree_range (basic_block bb, tree t) ++{ ++ if (!t) ++ return int_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); ++ int_range range1 = calc_tree_range (bb, TREE_OPERAND (t, 0)); ++ int_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; ++ ++ int_range len_range = len_range_map.get_or_insert (bb); ++ HOST_WIDE_INT len_min = tree_to_shwi (len_range.min ()); ++ HOST_WIDE_INT len_max = tree_to_shwi (len_range.max ()); ++ HOST_WIDE_INT min1 = calc_tree_value (op[0], len_min); ++ HOST_WIDE_INT max1 = calc_tree_value (op[0], len_max); ++ HOST_WIDE_INT min2 = calc_tree_value (op[1], len_min); ++ HOST_WIDE_INT max2 = calc_tree_value (op[1], len_max); ++ ++ HOST_WIDE_INT min = TREE_CODE (t) == PLUS_EXPR ? min1 + min2 : min1 - min2; ++ HOST_WIDE_INT 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); ++} ++ ++int_range ++array_dse_callee::build_range (basic_block bb, tree_code op, ++ const int_range &r1, const int_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; ++ case BIT_AND_EXPR: ++ if (r1.min () != signed_len_var || r1.max () != signed_len_var ++ || !r2.singleton_p () || !integer_cst_p (r2.min ()) ++ || tree_to_shwi (r2.min ()) != -(HOST_WIDE_INT) elem_size_cst) ++ break; ++ min = mult_tree (bb, div_tree (bb, signed_len_var, elem_size), elem_size); ++ max = min; ++ default: ++ break; ++ } ++ ++ return min && max ? int_range{min, max} : int_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; ++ ++ compare_result 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); ++ int_range 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; ++ ++ edge return_edge; ++ edge_iterator ei; ++ FOR_EACH_EDGE (return_edge, ei, 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 (gimple_stmt_iterator 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 (tree 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 (gimple_stmt_iterator 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); ++ case BIT_AND_EXPR: ++ return check_and_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); ++ if (TREE_CODE (rhs1) == SSA_NAME) ++ { ++ gimple *s = SSA_NAME_DEF_STMT (rhs1); ++ if (is_gimple_assign (s) ++ && gimple_assign_rhs_code (s) == BIT_AND_EXPR) ++ return check_and_expr (s, len, size); ++ } ++ 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; ++} ++ ++bool ++array_dse_callee::check_and_expr (gimple *stmt, tree &len, unsigned &size) ++{ ++ tree rhs1 = gimple_assign_rhs1 (stmt); ++ if (len && len != rhs1) ++ return false; ++ len = rhs1; ++ ++ tree rhs2 = gimple_assign_rhs2 (stmt); ++ ++ /* Handle size. */ ++ if (TREE_CODE (rhs2) != INTEGER_CST) ++ return false; ++ ++ unsigned HOST_WIDE_INT value = -(HOST_WIDE_INT) TREE_INT_CST_LOW (rhs2); ++ if ((value & -value != value) || (size && size != value)) ++ return false; ++ size = value; ++ 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. */ ++ int_range 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)); ++ int_range 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 int_range &new_range) ++{ ++ for (basic_block bb : get_all_dominated_blocks (CDI_DOMINATORS, start)) ++ (*len_range_map.get (bb)).intersect (new_range); ++} ++ ++int_range ++array_dse_callee::invert_range (const int_range &range) const ++{ ++ int_range new_range = range; ++ new_range.invert (); ++ ++ return new_range; ++} ++ ++/* Get range of len_param from a condition. */ ++ ++int_range ++array_dse_callee::get_range_from_cond (gimple *stmt) ++{ ++ if (!stmt || gimple_code (stmt) != GIMPLE_COND) ++ return int_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 int_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 int_range{}; ++ } ++} ++ ++/* Get range of a variable, represented by len_param. If variable is a ++ pointer, return the range of its offset from array_param. */ ++ ++int_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 int_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. */ ++ int_range &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); ++ int_range 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 = int_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; ++ int_range range1 = get_var_range (bb, rhs1); ++ int_range range2 = int_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; ++ ++ edge succ; ++ edge_iterator ei; ++ FOR_EACH_EDGE (succ, ei, 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 (gimple_stmt_iterator 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; ++ ++ int_range range = get_var_range (bb, base); ++ /* offset >= 0. */ ++ compare_result 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); ++ int_range array_range = get_var_range (bb, array_args[i]); ++ int_range len_range = get_var_range (bb, len_args[i]); ++ ++ /* Check requirement 2. */ ++ compare_result 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); ++ ++ int_range 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] = int_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) ++ { ++ int_range range1 = get_var_range (bb, array_arg); ++ int_range 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 (gphi_iterator 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)); ++ int_range 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 (); ++ int_range new_range = int_range{min, max}; ++ for (basic_block bb : get_all_dominated_blocks (CDI_DOMINATORS, header)) ++ { ++ if (!find_var_range (result, bb)) ++ { ++ var_range[result][bb] = new_range; ++ continue; ++ } ++ ++ int_range &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 = int_range {min, max}; ++ } ++ } ++ ++ if (!check_loop_exits (loop)) ++ return false; ++ ++ return true; ++} ++ ++bool ++array_dse_callee::check_loop_exits (loop_p loop) ++{ ++ for (edge 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 (basic_block bb : get_all_dominated_blocks (CDI_DOMINATORS, loop->header)) ++ { ++ int_range lhs_range = get_var_range (bb, lhs); ++ int_range 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] = int_range{lhs_min, lhs_max}; ++ if (loop_var_p (loop, rhs) || iterate_var_p (loop, rhs)) ++ var_range[rhs][bb] = int_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; ++ ++ compare_result 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 (tree ptr : loop_ptrs[loop]) ++ { ++ int_range &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 = int_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) ++{ ++ std::map>::iterator iter1 = var_range.find (var); ++ if (iter1 == var_range.end ()) ++ return false; ++ ++ std::map::iterator 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 p : array_accesses) ++ if (!check_access_kind (p.second, p.first)) ++ 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 (gphi_iterator 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 (gimple_stmt_iterator 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 (); ++ ++ 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 (strip_ssa_copy (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)); ++ ++ access_kind &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 p : array_accesses) ++ { ++ tree var = p.first; ++ gimple *stmt = p.second; ++ tree lhs = gimple_get_lhs_or_phi_result (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; ++ ++ edge e; ++ edge_iterator ei; ++ FOR_EACH_EDGE (e, ei, bb->preds) ++ worklist.safe_push (e->src); ++ } ++ ++ for (auto p : array_accesses) ++ { ++ gimple *access_stmt = p.second; ++ 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 p : array_accesses) ++ { ++ tree var = p.first; ++ gimple *stmt = p.second; ++ if (!after_call_stmt_p (stmt) || !read_array_p (var)) ++ continue; ++ ++ int_range range = calc_ref_range (var); ++ if (!integer_cst_p (range.max ())) ++ return false; ++ ++ HOST_WIDE_INT 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; ++} ++ ++int_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) ++ { ++ int_range 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); ++ int_range r1 = calc_addr_range (TREE_OPERAND (var, 0)); ++ int_range r2 = calc_offset_range (TREE_OPERAND (var, 1)); ++ ++ return int_range{plus_tree (r1.min (), r2.min ()), ++ plus_tree (r1.max (), r2.max ())}; ++} ++ ++int_range ++array_dse_edge::calc_addr_range (tree var) ++{ ++ var = strip_ssa_copy (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)) ++ { ++ int_range r1 = calc_addr_range (gimple_assign_rhs1 (stmt)); ++ int_range r2 = calc_offset_range (gimple_assign_rhs2 (stmt)); ++ return int_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 int_range{RANGE_TYPE}; ++ ++ tree op = TREE_OPERAND (var, 0); ++ if (op == array) ++ return make_range (0); ++ ++ if (!array_ref_p (op)) ++ return int_range{RANGE_TYPE}; ++ ++ return calc_ref_range (op); ++} ++ ++int_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 int_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 int_range{RANGE_TYPE}; ++ ++ int_range range = calc_offset_range (gimple_assign_rhs1 (stmt)); ++ if (!integer_cst_p (range.min ()) || !integer_cst_p (range.max ())) ++ return int_range{RANGE_TYPE}; ++ ++ return make_range (tree_to_shwi (range.min ()) * elem_size, ++ tree_to_shwi (range.max ()) * elem_size); ++} ++ ++int_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 int_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)); ++ ++ int_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 int_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; ++ edge e; ++ edge_iterator ei; ++ FOR_EACH_EDGE (e, ei, 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_EACH_EDGE (e, ei, 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; ++ ++ int_range range = calc_addr_range (array_arg); ++ if (!range.singleton_p () || !integer_cst_p (range.min ())) ++ return false; ++ ++ HOST_WIDE_INT 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; ++ ++ int_range 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 p : array_accesses) ++ { ++ tree var = p.first; ++ gimple *stmt = p.second; ++ /* 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_or_phi_result (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; ++ ++ edge latch_edge = loop_latch_edge (loop); ++ edge 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) ++{ ++ access_kind *kind = access_kinds.get (var); ++ if (!kind) ++ return false; ++ ++ return *kind & WRITE; ++} ++ ++bool ++array_dse_edge::read_array_p (tree var) ++{ ++ access_kind *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->global.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 (array_dse_edge *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 (cgraph_node *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); ++ ++ array_dse_callee *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 (array_dse_callee *callee : candidate_callees) ++ { ++ cgraph_edge *e = callee->node->callers; ++ while (e && e->caller == callee->node) ++ e = e->next_caller; ++ ++ for (array_dse_callee *c : candidate_callees) ++ if (e->caller == c->node) ++ return false; ++ ++ array_dse_edge *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); ++ ++ gimple_stmt_iterator 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, false, ++ NULL, NULL, "array_dse"); ++ ++ 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 arg_decls = ipa_get_vector_of_formal_parms (cfun->decl); ++ ipa_parm_adjustment_vec new_params; ++ new_params.create (arg_decls.length () + 1); ++ ++ for (unsigned i = 0; i < arg_decls.length (); ++i) ++ { ++ ipa_parm_adjustment adj; ++ memset (&adj, 0, sizeof (adj)); ++ ++ adj.type = TREE_TYPE (arg_decls[i]); ++ adj.base = arg_decls[i]; ++ adj.base_index = i; ++ adj.op = IPA_PARM_OP_COPY; ++ new_params.safe_push (adj); ++ } ++ ++ tree param_name = DECL_NAME (param); ++ const char *name = concat (IDENTIFIER_POINTER (param_name), ".bound", NULL); ++ ipa_parm_adjustment adj; ++ memset (&adj, 0, sizeof (adj)); ++ adj.type = TREE_TYPE (param); ++ adj.base_index = arg_decls.length (); ++ adj.op = IPA_PARM_OP_NEW; ++ new_params.safe_push (adj); ++ ++ ipa_modify_formal_parameters (cfun->decl, new_params); ++ new_params.release (); ++ arg_decls.release (); ++ ++ arg_decls = ipa_get_vector_of_formal_parms (cfun->decl); ++ tree new_param = arg_decls.last (); ++ DECL_NAME (new_param) = get_identifier (name); ++ arg_decls.release (); ++ ++ 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); ++ } ++ ++ gimple_stmt_iterator 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); ++ gimple_stmt_iterator 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 (cgraph_edge *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 000000000..5b56cd456 +--- /dev/null ++++ b/gcc/ipa-array-dse.h +@@ -0,0 +1,266 @@ ++/* 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 "backend.h" ++#include "profile-count.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 "cgraph.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); ++ int_range calc_tree_range (basic_block bb, tree t); ++ bool get_factor (tree t, double &factor); ++ bool positive_factor_p (tree t); ++ int_range build_range (basic_block bb, tree_code op, ++ const int_range &r1, const int_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); ++ bool check_and_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 int_range &new_range); ++ int_range invert_range (const int_range &range) const; ++ int_range get_range_from_cond (gimple *stmt); ++ int_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 (); ++ int_range calc_ref_range (tree var); ++ int_range calc_addr_range (tree var); ++ int_range calc_offset_range (tree var); ++ int_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-localize-array.c b/gcc/ipa-localize-array.c +new file mode 100644 +index 000000000..8b92f936b +--- /dev/null ++++ b/gcc/ipa-localize-array.c +@@ -0,0 +1,652 @@ ++/* 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_single_p (use_stmt) ++ && ref_val == gimple_assign_rhs1 (use_stmt)) ++ { ++ tree lhs = gimple_assign_lhs (use_stmt); ++ if (TREE_CODE (lhs) == SSA_NAME) ++ { ++ gimple *stmt = nullptr; ++ imm_use_iterator iter; ++ FOR_EACH_IMM_USE_STMT (stmt, iter, lhs) ++ if ((!gimple_assign_load_p (stmt) && !gimple_store_p (stmt)) || ++ !scalar_memop_p (lhs, stmt)) ++ return false; ++ return true; ++ } ++ } ++ ++ 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_or_phi_result (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_or_phi_result (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; ++ edge e; ++ edge_iterator ei; ++ FOR_EACH_EDGE (e, ei, 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 if (gimple_assign_single_p (use_stmt) ++ && lhs == gimple_assign_rhs1 (use_stmt)) ++ { ++ tree t = gimple_assign_lhs (use_stmt); ++ gcc_assert (TREE_CODE (t) == SSA_NAME); ++ gimple *stmt = nullptr; ++ imm_use_iterator it; ++ FOR_EACH_IMM_USE_STMT (stmt, it, t) ++ { ++ if (is_gimple_debug (stmt)) ++ remove_referring_stmt (stmt); ++ else if (gimple_store_p (stmt)) ++ replace_store_with_ssa (stmt, var_ssa); ++ else if (gimple_assign_load_p (stmt)) ++ replace_load_with_ssa (stmt, var_ssa); ++ else ++ gcc_unreachable (); ++ } ++ remove_referring_stmt (use_stmt); ++ } ++ 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->global.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/passes.def b/gcc/passes.def +index 62de8d7d7..7d9645c0e 100644 +--- a/gcc/passes.def ++++ b/gcc/passes.def +@@ -177,6 +177,20 @@ along with GCC; see the file COPYING3. If not see + compiled unit. */ + INSERT_PASSES_AFTER (all_late_ipa_passes) + NEXT_PASS (pass_materialize_all_clones); ++ 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_forwprop); ++ NEXT_PASS (pass_cd_dce); ++ 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); + /* FIXME: this should a normal IP pass */ +diff --git a/gcc/testsuite/g++.dg/tree-ssa/fwprop-align.C b/gcc/testsuite/g++.dg/tree-ssa/fwprop-align.C +index 5cc5f0d7d..2f8b6c04b 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/pr14814.C b/gcc/testsuite/g++.dg/tree-ssa/pr14814.C +index f2177d257..9372de88d 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/gcc.dg/graphite/fuse-1.c b/gcc/testsuite/gcc.dg/graphite/fuse-1.c +index 204d3b207..ffcaa736b 100644 +--- a/gcc/testsuite/gcc.dg/graphite/fuse-1.c ++++ b/gcc/testsuite/gcc.dg/graphite/fuse-1.c +@@ -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 "gimple_simplified to\[^\\n\]*\\^ 12" 1 "forwprop4" } } */ ++/* { dg-final { scan-tree-dump-times "gimple_simplified to\[^\\n\]*\\^ 12" 1 "forwprop6" } } */ + + #define MAX 100 + int A[MAX]; +diff --git a/gcc/testsuite/gcc.dg/pr43513.c b/gcc/testsuite/gcc.dg/pr43513.c +index 9383a802c..fb2673f08 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 c5b0d1ff1..e921a5006 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" } */ ++/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ + + int foo (void) + { +diff --git a/gcc/testsuite/gcc.dg/predict-9.c b/gcc/testsuite/gcc.dg/predict-9.c +index 3823775e3..ecce0a7c6 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" } */ ++/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-profile_estimate" } */ + + extern int global; + extern int global2; +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/20030808-1.c b/gcc/testsuite/gcc.dg/tree-ssa/20030808-1.c +index 456f6f271..40b8e4cac 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 d289e5d0f..5c7a55596 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" } */ ++/* { dg-options "-O2 -fdump-tree-cddce3" } */ + + 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 62ef77622..0c05519df 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/cunroll-13.c b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-13.c +index dcc19f552..98afea339 100644 +--- a/gcc/testsuite/gcc.dg/tree-ssa/cunroll-13.c ++++ b/gcc/testsuite/gcc.dg/tree-ssa/cunroll-13.c +@@ -1,5 +1,5 @@ + /* { dg-do compile } */ +-/* { dg-options "-O3 -fdisable-tree-evrp -fdisable-tree-cunrolli -fdisable-tree-vrp1 -fdump-tree-cunroll-blocks-details" } */ ++/* { dg-options "-O3 -fdisable-tree-evrp1 -fdisable-tree-cunrolli -fdisable-tree-vrp1 -fdump-tree-cunroll-blocks-details" } */ + + #if __SIZEOF_INT__ < 4 + __extension__ typedef __INT32_TYPE__ i32; +@@ -15,7 +15,7 @@ t(struct a *a) + a->a[i]++; + } + /* This pass relies on the fact that we do not eliminate the redundant test for i early. +- It is necessary to disable all passes that do so. At the moment it is evrp, vrp1 and cunrolli. */ ++ It is necessary to disable all passes that do so. At the moment it is evrp1, vrp1 and cunrolli. */ + /* { dg-final { scan-tree-dump-times "Loop 1 iterates 123454 times" 1 "cunroll" } } */ + /* { dg-final { scan-tree-dump-times "Last iteration exit edge was proved true" 1 "cunroll" } } */ + /* { dg-final { scan-tree-dump-times "Exit condition of peeled iterations was eliminated" 1 "cunroll" } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp1.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp1.c +index 8c6e4e60a..87f5ab286 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" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1" } */ + + 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/evrp2.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp2.c +index e6d423555..759161e44 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" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1" } */ + + 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/evrp3.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp3.c +index 1a3bbd52a..0a3f84136 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" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1" } */ + + 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/evrp4.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp4.c +index ebb87ed38..f07c68ad9 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" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1" } */ + + int foo (int *p); + +@@ -17,4 +17,4 @@ int bar (struct st *s) + foo (&s->a); + } + +-/* { dg-final { scan-tree-dump "\~\\\[0B, 0B\\\]" "evrp" } } */ ++/* { dg-final { scan-tree-dump "\~\\\[0B, 0B\\\]" "evrp1" } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c b/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c +index 35d4d7459..238551ae5 100644 +--- a/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c ++++ b/gcc/testsuite/gcc.dg/tree-ssa/evrp6.c +@@ -1,6 +1,6 @@ + + /* { dg-do compile } */ +-/* { dg-options "-O2 -fdump-tree-evrp" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1" } */ + + extern void abort (void); + +@@ -19,4 +19,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 16fbe65e4..3c193d0cc 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 b7e5c7aa2..a0690e1e2 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 6e7828e43..7320189e9 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,5 +24,5 @@ foo (unsigned int x, unsigned int y) + bar (); + } + +-/* { dg-final { scan-tree-dump-not "== 5" "evrp" } } */ +-/* { dg-final { scan-tree-dump-not "== 6" "evrp" } } */ ++/* { dg-final { scan-tree-dump-not "== 5" "evrp1" } } */ ++/* { dg-final { scan-tree-dump-not "== 6" "evrp1" } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/foldconst-4.c b/gcc/testsuite/gcc.dg/tree-ssa/foldconst-4.c +index 0e9b676f6..f58bf667f 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 c7124deee..ab8b5ba3b 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/pr14814.c b/gcc/testsuite/gcc.dg/tree-ssa/pr14814.c +index 60d9649a1..5df4e49c2 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 80fd72668..2537855f6 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 e67823150..f7cab6fe2 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 c89685774..7a03c36f9 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-tree-dominator-opts -fdisable-tree-evrp -fdump-tree-vrp1-details -fdelete-null-pointer-checks" } */ ++/* { dg-options "-O2 -fno-tree-dominator-opts -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 719360a01..5a028d0fb 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-dominator-opts -fno-tree-fre -fdisable-tree-evrp -fdump-tree-vrp1-details" } */ ++/* { dg-options "-O2 -fno-tree-dominator-opts -fno-tree-fre -fdisable-tree-evrp1 -fdump-tree-vrp1-details" } */ + + int + foo (int a) +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr21086.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21086.c +index aadd53e22..4c9d83233 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 -fdisable-tree-evrp -fdump-tree-vrp1 -fdump-tree-dce2 -fdelete-null-pointer-checks" } */ ++/* { dg-options "-O2 -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 3909adb72..5abbecd5a 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 -fdisable-tree-evrp -fdump-tree-vrp1 -fdelete-null-pointer-checks" } */ ++/* { dg-options "-O2 -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 cc7d4cdf3..63d08b98f 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 -fdump-tree-vrp1-details" } */ ++/* { dg-options "-O2 -fno-tree-dominator-opts -fdisable-tree-evrp1 -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 2aee42f4c..29430f1f3 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" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1-details" } */ + + extern void g (void); + extern void bar (int); +@@ -16,4 +16,4 @@ foo (int a) + } + } + +-/* { dg-final { scan-tree-dump-times "Predicate evaluates to: 1" 1 "evrp" } } */ ++/* { dg-final { scan-tree-dump-times "Predicate evaluates 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 97d17f21c..c6a0c51ff 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/pr21563.c b/gcc/testsuite/gcc.dg/tree-ssa/pr21563.c +index 9c67a3acb..44dedb2bc 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-tree-dominator-opts -fdisable-tree-evrp -fdump-tree-vrp1-details" } */ ++/* { dg-options "-O2 -fno-tree-dominator-opts -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 3385aa1e4..6e49cf141 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 d74765551..4f701dd2e 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-tree-ccp -fdisable-tree-evrp -fdump-tree-vrp1" } */ ++/* { dg-options "-O2 -fno-tree-ccp -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ + + int + foo (int a) +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr49039.c b/gcc/testsuite/gcc.dg/tree-ssa/pr49039.c +index 4bc0a8175..501a68d40 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" } */ ++/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ + + 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 42898e72d..5e4bbd753 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 -fdisable-tree-evrp -fdump-tree-vrp1 -fdelete-null-pointer-checks" } */ ++/* { dg-options "-O2 -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_2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr61839_2.c +index 638189e66..717a45345 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)) +@@ -47,8 +47,8 @@ int bar2 () + + + /* Dont optimize 972195717 / 0 in function foo. */ +-/* { dg-final { scan-tree-dump-times "972195717 / " 1 "evrp" } } */ ++/* { dg-final { scan-tree-dump-times "972195717 / " 1 "evrp1" } } */ + /* Dont optimize 972195717 % 0 in function bar. */ +-/* { dg-final { scan-tree-dump-times "972195717 % " 1 "evrp" } } */ ++/* { dg-final { scan-tree-dump-times "972195717 % " 1 "evrp1" } } */ + /* May optimize in function bar2, but EVRP doesn't perform this yet. */ +-/* { dg-final { scan-tree-dump-times "972195715 % " 0 "evrp" { xfail *-*-* } } } */ ++/* { dg-final { scan-tree-dump-times "972195715 % " 0 "evrp1" { xfail *-*-* } } } */ +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c b/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c +index e068765e2..1ec36c1bf 100644 +--- a/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c ++++ b/gcc/testsuite/gcc.dg/tree-ssa/pr64130.c +@@ -1,6 +1,6 @@ + + /* { dg-do compile } */ +-/* { dg-options "-O2 -fdump-tree-evrp" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1" } */ + + __extension__ typedef __UINT32_TYPE__ uint32_t; + +@@ -15,6 +15,6 @@ int funsigned2 (uint32_t a) + return (-1 * 0x1ffffffffL) / a == 0; + } + +-/* { dg-final { scan-tree-dump ": \\\[2, 8589934591\\\]" "evrp" } } */ +-/* { dg-final { scan-tree-dump ": \\\[-8589934591, -2\\\]" "evrp" } } */ ++/* { dg-final { scan-tree-dump ": \\\[2, 8589934591\\\]" "evrp1" } } */ ++/* { dg-final { scan-tree-dump ": \\\[-8589934591, -2\\\]" "evrp1" } } */ + +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr68431.c b/gcc/testsuite/gcc.dg/tree-ssa/pr68431.c +index 9c42563b6..1de9aa540 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/pr78153-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr78153-1.c +index 2530ba08e..174adca33 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 de70450f1..11ace86d9 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 d908a39dc..3d36f59f2 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" } */ ++/* { dg-options "-O2 -fdump-tree-evrp1-slim" } */ + + void f(void *d, const void *s, __SIZE_TYPE__ n) + { +@@ -40,4 +40,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/ssa-ccp-29.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-ccp-29.c +index 3c1a848f5..c93a8d0d2 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 1c8c318ce..48427de64 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/vrp03.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp03.c +index bafb65a53..aba7eff02 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" } */ ++/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ + + struct A + { +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp06.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp06.c +index a872bc437..7bf288487 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" } */ ++/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ + + int baz (void); + +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp07.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp07.c +index 0f3f280e4..7e239907d 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" } */ ++/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp1 -fdump-tree-vrp1-details -fdelete-null-pointer-checks" } */ + + 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 c2da30b4b..57e72bb64 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 -fdelete-null-pointer-checks" } */ ++/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp1 -fdump-tree-vrp1-details -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 56cc50c5f..185c926c3 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" } */ ++/* { dg-options "-O2 -fno-tree-fre -fdisable-tree-evrp1 -fdump-tree-vrp1 -std=gnu89" } */ + + foo (int *p) + { +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp111.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp111.c +index 631442358..f6a850af3 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" } */ + + void foo (void *p) __attribute__((nonnull(1))); + +@@ -10,4 +10,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/vrp114.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp114.c +index 5c2c1a0b5..33adac61a 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 6d1c9c50b..47fecebe1 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 d07a6722c..47194eec0 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/vrp16.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp16.c +index d09f3aea4..2243a87c3 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/vrp19.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp19.c +index 40373fde1..adb1708fe 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" } */ ++/* { dg-options "-fwrapv -O1 -ftree-vrp -fdisable-tree-evrp1 -fdump-tree-vrp1" } */ + + #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 4a3b0d736..c8e20c6a8 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" } */ ++/* { dg-options "-fwrapv -O1 -fno-tree-fre -fdisable-tree-evrp1 -ftree-vrp -fdump-tree-vrp1" } */ + + extern void abort (); + extern void exit (int); +diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp35.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp35.c +index a372a18cc..502070634 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 1f77b539d..bff236e2c 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 213aa4760..ce43d980f 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 -fdump-tree-vrp1-details -fdisable-tree-ethread" } */ ++/* { dg-options "-O2 -fdisable-tree-evrp1 -fdump-tree-vrp1-details -fdisable-tree-ethread" } */ + + void bar (void); + int foo (int i, int j) +diff --git a/gcc/testsuite/gfortran.dg/pr45636.f90 b/gcc/testsuite/gfortran.dg/pr45636.f90 +index 958833c35..304efa7c6 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 69a052145..51536f87e 100644 +--- a/gcc/timevar.def ++++ b/gcc/timevar.def +@@ -77,6 +77,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/tree-pass.h b/gcc/tree-pass.h +index 1f53227ff..72176e97d 100644 +--- a/gcc/tree-pass.h ++++ b/gcc/tree-pass.h +@@ -509,6 +509,9 @@ extern ipa_opt_pass_d *make_pass_ipa_devirt (gcc::context *ctxt); + extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt); + extern ipa_opt_pass_d *make_pass_ipa_hsa (gcc::context *ctxt); + extern ipa_opt_pass_d *make_pass_ipa_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 simple_ipa_opt_pass *make_pass_ipa_hardware_detection (gcc::context * + ctxt); + extern simple_ipa_opt_pass *make_pass_ipa_struct_reorg (gcc::context *ctxt); +diff --git a/gcc/value-range.c b/gcc/value-range.c +new file mode 100644 +index 000000000..a7cd35bb7 +--- /dev/null ++++ b/gcc/value-range.c +@@ -0,0 +1,1840 @@ ++/* Support routines for value ranges. ++ Copyright (C) 2019-2022 Free Software Foundation, Inc. ++ Major hacks by Aldy Hernandez and ++ Andrew MacLeod . ++ ++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 "ssa.h" ++#include "tree-pretty-print.h" ++#include "fold-const.h" ++#include "value-range.h" ++ ++value_range_type ++irange::kind () const ++{ ++ return m_kind; ++} ++ ++// Number of sub-ranges in a range. ++ ++unsigned ++irange::num_pairs () const ++{ ++ if (m_kind == VR_ANTI_RANGE) ++ return constant_p () ? 2 : 1; ++ else ++ return m_num_ranges; ++} ++ ++tree ++irange::type () const ++{ ++ gcc_checking_assert (m_num_ranges > 0); ++ return TREE_TYPE (m_base[0]); ++} ++ ++// Return the lower bound of a sub-range expressed as a tree. PAIR is ++// the sub-range in question. ++ ++tree ++irange::tree_lower_bound (unsigned pair) const ++{ ++ return m_base[pair * 2]; ++} ++ ++// Return the upper bound of a sub-range expressed as a tree. PAIR is ++// the sub-range in question. ++ ++tree ++irange::tree_upper_bound (unsigned pair) const ++{ ++ return m_base[pair * 2 + 1]; ++} ++ ++// Return the highest bound of a range expressed as a tree. ++ ++tree ++irange::tree_upper_bound () const ++{ ++ gcc_checking_assert (m_num_ranges); ++ return tree_upper_bound (m_num_ranges - 1); ++} ++ ++tree ++irange::min () const ++{ ++ return tree_lower_bound (0); ++} ++ ++tree ++irange::max () const ++{ ++ if (m_num_ranges) ++ return tree_upper_bound (); ++ else ++ return NULL; ++} ++ ++bool ++irange::varying_compatible_p () const ++{ ++ if (m_num_ranges != 1) ++ return false; ++ ++ tree l = m_base[0]; ++ tree u = m_base[1]; ++ tree t = TREE_TYPE (l); ++ ++ if (m_kind == VR_VARYING && t == error_mark_node) ++ return true; ++ ++ unsigned prec = TYPE_PRECISION (t); ++ signop sign = TYPE_SIGN (t); ++ if (INTEGRAL_TYPE_P (t)) ++ return (wi::to_wide (l) == wi::min_value (prec, sign) ++ && wi::to_wide (u) == wi::max_value (prec, sign)); ++ if (POINTER_TYPE_P (t)) ++ return (wi::to_wide (l) == 0 ++ && wi::to_wide (u) == wi::max_value (prec, sign)); ++ return true; ++} ++ ++bool ++irange::varying_p () const ++{ ++ return m_kind == VR_VARYING; ++} ++ ++bool ++irange::undefined_p () const ++{ ++ return m_kind == VR_UNDEFINED; ++} ++ ++bool ++irange::zero_p () const ++{ ++ return (m_kind == VR_RANGE && m_num_ranges == 1 ++ && integer_zerop (tree_lower_bound (0)) ++ && integer_zerop (tree_upper_bound (0))); ++} ++ ++bool ++irange::nonzero_p () const ++{ ++ if (undefined_p ()) ++ return false; ++ ++ tree zero = build_zero_cst (type ()); ++ return *this == int_range (zero, zero, VR_ANTI_RANGE); ++} ++ ++bool ++irange::supports_type_p (tree type) ++{ ++ if (type && (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type))) ++ return type; ++ return false; ++} ++ ++bool ++range_includes_zero_p (const irange *vr) ++{ ++ if (vr->undefined_p ()) ++ return false; ++ ++ if (vr->varying_p ()) ++ return true; ++ ++ return vr->may_contain_p (build_zero_cst (vr->type ())); ++} ++ ++void ++gt_ggc_mx (irange *x) ++{ ++ for (unsigned i = 0; i < x->m_num_ranges; ++i) ++ { ++ gt_ggc_mx (x->m_base[i * 2]); ++ gt_ggc_mx (x->m_base[i * 2 + 1]); ++ } ++} ++ ++void ++gt_pch_nx (irange *x) ++{ ++ for (unsigned i = 0; i < x->m_num_ranges; ++i) ++ { ++ gt_pch_nx (x->m_base[i * 2]); ++ gt_pch_nx (x->m_base[i * 2 + 1]); ++ } ++} ++ ++void ++gt_pch_nx (irange *x, gt_pointer_operator op, void *cookie) ++{ ++ for (unsigned i = 0; i < x->m_num_ranges; ++i) ++ { ++ op (&x->m_base[i * 2], cookie); ++ op (&x->m_base[i * 2 + 1], cookie); ++ } ++} ++ ++void ++gt_ggc_mx (int_range *x) ++{ ++ gt_ggc_mx ((irange *) x); ++} ++ ++void ++gt_pch_nx (int_range *x) ++{ ++ gt_pch_nx ((irange *) x); ++} ++ ++void ++gt_pch_nx (int_range *x, gt_pointer_operator op, void *cookie) ++{ ++ gt_pch_nx ((irange *) x, op, cookie); ++} ++ ++// Constructors for irange ++ ++inline ++irange::irange (tree *base, unsigned nranges) ++{ ++ m_base = base; ++ m_num_ranges = 0; ++ m_max_ranges = nranges; ++ m_kind = VR_UNDEFINED; ++} ++ ++// Constructors for int_range<>. ++ ++int_range::int_range () ++ : irange (m_ranges, 1) ++{ ++} ++ ++int_range::int_range (const int_range &other) ++ : irange (m_ranges, 1) ++{ ++ irange::operator= (other); ++} ++ ++int_range::int_range (tree min, tree max, value_range_type kind) ++ : irange (m_ranges, 1) ++{ ++ irange::set (min, max, kind); ++} ++ ++int_range::int_range (tree type) ++ : irange (m_ranges, 1) ++{ ++ set_varying (type); ++} ++ ++int_range::int_range (tree type, const wide_int &wmin, const wide_int &wmax, ++ value_range_type kind) ++ : irange (m_ranges, 1) ++{ ++ tree min = wide_int_to_tree (type, wmin); ++ tree max = wide_int_to_tree (type, wmax); ++ set (min, max, kind); ++} ++ ++int_range::int_range (const irange &other) ++ : irange (m_ranges, 1) ++{ ++ irange::operator= (other); ++} ++ ++int_range& ++int_range::operator= (const int_range &src) ++{ ++ if (!m_base) ++ m_base = m_ranges; ++ irange::operator= (src); ++ return *this; ++} ++ ++void ++irange::set (tree val) ++{ ++ set (val, val); ++} ++ ++void ++irange::set_undefined () ++{ ++ m_kind = VR_UNDEFINED; ++ m_num_ranges = 0; ++} ++ ++void ++irange::set_varying (tree type) ++{ ++ m_kind = VR_VARYING; ++ m_num_ranges = 1; ++ ++ if (INTEGRAL_TYPE_P (type)) ++ { ++ // Strict enum's require varying to be not TYPE_MIN/MAX, but rather ++ // min_value and max_value. ++ wide_int min = wi::min_value (TYPE_PRECISION (type), TYPE_SIGN (type)); ++ wide_int max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); ++ if (wi::eq_p (max, wi::to_wide (TYPE_MAX_VALUE (type))) ++ && wi::eq_p (min, wi::to_wide (TYPE_MIN_VALUE (type)))) ++ { ++ m_base[0] = TYPE_MIN_VALUE (type); ++ m_base[1] = TYPE_MAX_VALUE (type); ++ } ++ else ++ { ++ m_base[0] = wide_int_to_tree (type, min); ++ m_base[1] = wide_int_to_tree (type, max); ++ } ++ } ++ else if (POINTER_TYPE_P (type)) ++ { ++ m_base[0] = build_int_cst (type, 0); ++ m_base[1] = build_int_cst (type, -1); ++ } ++ else ++ m_base[0] = m_base[1] = error_mark_node; ++} ++ ++bool ++irange::operator== (const irange &r) const ++{ ++ return equal_p (r); ++} ++ ++// Return the lower bound of a sub-range. PAIR is the sub-range in ++// question. ++ ++wide_int ++irange::lower_bound (unsigned pair) const ++{ ++ return legacy_lower_bound (pair); ++} ++ ++// Return the upper bound of a sub-range. PAIR is the sub-range in ++// question. ++ ++wide_int ++irange::upper_bound (unsigned pair) const ++{ ++ return legacy_upper_bound (pair); ++} ++ ++// Return the highest bound of a range. ++ ++wide_int ++irange::upper_bound () const ++{ ++ unsigned pairs = num_pairs (); ++ gcc_checking_assert (pairs > 0); ++ return upper_bound (pairs - 1); ++} ++ ++void ++irange::union_ (const irange &r) ++{ ++ dump_flags_t m_flags = dump_flags; ++ dump_flags &= ~TDF_DETAILS; ++ irange::union_ (&r); ++ dump_flags = m_flags; ++} ++ ++void ++irange::intersect (const irange &r) ++{ ++ dump_flags_t m_flags = dump_flags; ++ dump_flags &= ~TDF_DETAILS; ++ irange::intersect (&r); ++ dump_flags = m_flags; ++} ++ ++// Set value range VR to a nonzero range of type TYPE. ++ ++void ++irange::set_nonzero (tree type) ++{ ++ tree zero = build_int_cst (type, 0); ++ set (zero, zero, VR_ANTI_RANGE); ++} ++ ++// Set value range VR to a ZERO range of type TYPE. ++ ++void ++irange::set_zero (tree type) ++{ ++ tree z = build_int_cst (type, 0); ++ set (z); ++} ++ ++// Normalize a range to VARYING or UNDEFINED if possible. ++ ++void ++irange::normalize_kind () ++{ ++ if (m_num_ranges == 0) ++ m_kind = VR_UNDEFINED; ++ else if (varying_compatible_p ()) ++ { ++ if (m_kind == VR_RANGE) ++ m_kind = VR_VARYING; ++ else if (m_kind == VR_ANTI_RANGE) ++ set_undefined (); ++ else ++ gcc_unreachable (); ++ } ++} ++ ++// // Return the maximum value for TYPE. ++ ++// inline tree ++// vrp_val_max (const_tree type) ++// { ++// if (INTEGRAL_TYPE_P (type)) ++// return TYPE_MAX_VALUE (type); ++// if (POINTER_TYPE_P (type)) ++// { ++// wide_int max = wi::max_value (TYPE_PRECISION (type), TYPE_SIGN (type)); ++// return wide_int_to_tree (const_cast (type), max); ++// } ++// return NULL_TREE; ++// } ++ ++// // Return the minimum value for TYPE. ++ ++// inline tree ++// vrp_val_min (const_tree type) ++// { ++// if (INTEGRAL_TYPE_P (type)) ++// return TYPE_MIN_VALUE (type); ++// if (POINTER_TYPE_P (type)) ++// return build_zero_cst (const_cast (type)); ++// return NULL_TREE; ++// } ++ ++// Here we copy between any two irange's. The ranges can be legacy or ++// multi-ranges, and copying between any combination works correctly. ++ ++irange & ++irange::operator= (const irange &src) ++{ ++ m_num_ranges = src.m_num_ranges; ++ m_base[0] = src.m_base[0]; ++ m_base[1] = src.m_base[1]; ++ m_kind = src.m_kind; ++ return *this; ++} ++ ++// Swap MIN/MAX if they are out of order and adjust KIND appropriately. ++ ++static void ++swap_out_of_order_endpoints (tree &min, tree &max, value_range_type &kind) ++{ ++ gcc_checking_assert (kind != VR_UNDEFINED); ++ if (kind == VR_VARYING) ++ return; ++ /* Wrong order for min and max, to swap them and the VR type we need ++ to adjust them. */ ++ if (tree_int_cst_lt (max, min)) ++ { ++ tree one, tmp; ++ ++ /* For one bit precision if max < min, then the swapped ++ range covers all values, so for VR_RANGE it is varying and ++ for VR_ANTI_RANGE empty range, so drop to varying as well. */ ++ if (TYPE_PRECISION (TREE_TYPE (min)) == 1) ++ { ++ kind = VR_VARYING; ++ return; ++ } ++ ++ one = build_int_cst (TREE_TYPE (min), 1); ++ tmp = int_const_binop (PLUS_EXPR, max, one); ++ max = int_const_binop (MINUS_EXPR, min, one); ++ min = tmp; ++ ++ /* There's one corner case, if we had [C+1, C] before we now have ++ that again. But this represents an empty value range, so drop ++ to varying in this case. */ ++ if (tree_int_cst_lt (max, min)) ++ { ++ kind = VR_VARYING; ++ return; ++ } ++ kind = kind == VR_RANGE ? VR_ANTI_RANGE : VR_RANGE; ++ } ++} ++ ++void ++irange::irange_set_1bit_anti_range (tree min, tree max) ++{ ++ tree type = TREE_TYPE (min); ++ gcc_checking_assert (TYPE_PRECISION (type) == 1); ++ ++ if (operand_equal_p (min, max)) ++ { ++ // Since these are 1-bit quantities, they can only be [MIN,MIN] ++ // or [MAX,MAX]. ++ if (vrp_val_is_min (min)) ++ min = max = vrp_val_max (type); ++ else ++ min = max = vrp_val_min (type); ++ set (min, max); ++ } ++ else ++ { ++ // The only alternative is [MIN,MAX], which is the empty range. ++ gcc_checking_assert (vrp_val_is_min (min)); ++ gcc_checking_assert (vrp_val_is_max (max)); ++ set_undefined (); ++ } ++ if (flag_checking) ++ verify_range (); ++} ++ ++/* Set value range to the canonical form of {VRTYPE, MIN, MAX, EQUIV}. ++ This means adjusting VRTYPE, MIN and MAX representing the case of a ++ wrapping range with MAX < MIN covering [MIN, type_max] U [type_min, MAX] ++ as anti-rage ~[MAX+1, MIN-1]. Likewise for wrapping anti-ranges. ++ In corner cases where MAX+1 or MIN-1 wraps this will fall back ++ to varying. ++ This routine exists to ease canonicalization in the case where we ++ extract ranges from var + CST op limit. */ ++ ++void ++irange::set (tree min, tree max, value_range_type kind) ++{ ++ if (kind != VR_UNDEFINED) ++ { ++ if (TREE_OVERFLOW_P (min)) ++ min = drop_tree_overflow (min); ++ if (TREE_OVERFLOW_P (max)) ++ max = drop_tree_overflow (max); ++ } ++ ++ if (kind == VR_UNDEFINED) ++ { ++ set_undefined (); ++ return; ++ } ++ ++ if (kind == VR_VARYING ++ || POLY_INT_CST_P (min) ++ || POLY_INT_CST_P (max)) ++ { ++ set_varying (TREE_TYPE (min)); ++ return; ++ } ++ ++ // Nothing to canonicalize for symbolic ranges. ++ if (TREE_CODE (min) != INTEGER_CST ++ || TREE_CODE (max) != INTEGER_CST) ++ { ++ m_kind = kind; ++ m_base[0] = min; ++ m_base[1] = max; ++ m_num_ranges = 1; ++ return; ++ } ++ ++ swap_out_of_order_endpoints (min, max, kind); ++ if (kind == VR_VARYING) ++ { ++ set_varying (TREE_TYPE (min)); ++ return; ++ } ++ ++ // Anti-ranges that can be represented as ranges should be so. ++ if (kind == VR_ANTI_RANGE) ++ { ++ bool is_min = vrp_val_is_min (min); ++ bool is_max = vrp_val_is_max (max); ++ ++ if (is_min && is_max) ++ { ++ // Fall through. This will either be normalized as ++ // VR_UNDEFINED if the anti-range spans the entire ++ // precision, or it will remain an VR_ANTI_RANGE in the case ++ // of an -fstrict-enum where [MIN,MAX] is less than the span ++ // of underlying precision. ++ } ++ else if (TYPE_PRECISION (TREE_TYPE (min)) == 1) ++ { ++ irange_set_1bit_anti_range (min, max); ++ return; ++ } ++ else if (is_min) ++ { ++ tree one = build_int_cst (TREE_TYPE (max), 1); ++ min = int_const_binop (PLUS_EXPR, max, one); ++ max = vrp_val_max (TREE_TYPE (max)); ++ kind = VR_RANGE; ++ } ++ else if (is_max) ++ { ++ tree one = build_int_cst (TREE_TYPE (min), 1); ++ max = int_const_binop (MINUS_EXPR, min, one); ++ min = vrp_val_min (TREE_TYPE (min)); ++ kind = VR_RANGE; ++ } ++ } ++ ++ m_kind = kind; ++ m_base[0] = min; ++ m_base[1] = max; ++ m_num_ranges = 1; ++ normalize_kind (); ++ if (flag_checking) ++ verify_range (); ++} ++ ++// Check the validity of the range. ++ ++void ++irange::verify_range () ++{ ++ if (m_kind == VR_UNDEFINED) ++ { ++ gcc_checking_assert (m_num_ranges == 0); ++ return; ++ } ++ if (m_kind == VR_VARYING) ++ { ++ gcc_checking_assert (m_num_ranges == 1); ++ gcc_checking_assert (varying_compatible_p ()); ++ return; ++ } ++ if (m_kind == VR_RANGE || m_kind == VR_ANTI_RANGE) ++ { ++ gcc_checking_assert (m_num_ranges == 1); ++ int cmp = compare_values (tree_lower_bound (0), tree_upper_bound (0)); ++ gcc_checking_assert (cmp == 0 || cmp == -1 || cmp == -2); ++ } ++} ++ ++// Return the lower bound for a sub-range. PAIR is the sub-range in ++// question. ++ ++wide_int ++irange::legacy_lower_bound (unsigned pair) const ++{ ++ if (symbolic_p ()) ++ { ++ int_range numeric_range (*this); ++ numeric_range.normalize_symbolics (); ++ return numeric_range.legacy_lower_bound (pair); ++ } ++ gcc_checking_assert (m_num_ranges > 0); ++ gcc_checking_assert (pair + 1 <= num_pairs ()); ++ if (m_kind == VR_ANTI_RANGE) ++ { ++ tree typ = type (), t; ++ if (pair == 1 || vrp_val_is_min (min ())) ++ t = wide_int_to_tree (typ, wi::to_wide (max ()) + 1); ++ else ++ t = vrp_val_min (typ); ++ return wi::to_wide (t); ++ } ++ return wi::to_wide (tree_lower_bound (pair)); ++} ++ ++// Return the upper bound for a sub-range. PAIR is the sub-range in ++// question. ++ ++wide_int ++irange::legacy_upper_bound (unsigned pair) const ++{ ++ if (symbolic_p ()) ++ { ++ int_range numeric_range (*this); ++ numeric_range.normalize_symbolics (); ++ return numeric_range.legacy_upper_bound (pair); ++ } ++ gcc_checking_assert (m_num_ranges > 0); ++ gcc_checking_assert (pair + 1 <= num_pairs ()); ++ if (m_kind == VR_ANTI_RANGE) ++ { ++ tree typ = type (), t; ++ if (pair == 1 || vrp_val_is_min (min ())) ++ t = vrp_val_max (typ); ++ else ++ t = wide_int_to_tree (typ, wi::to_wide (min ()) - 1); ++ return wi::to_wide (t); ++ } ++ return wi::to_wide (tree_upper_bound (pair)); ++} ++ ++// Return true if TYPE1 and TYPE2 are compatible range types. ++ ++static inline bool ++range_compatible_p (tree type1, tree type2) ++{ ++ // types_compatible_p requires conversion in both directions to be useless. ++ // GIMPLE only requires a cast one way in order to be compatible. ++ // Ranges really only need the sign and precision to be the same. ++ return (TYPE_PRECISION (type1) == TYPE_PRECISION (type2) ++ && TYPE_SIGN (type1) == TYPE_SIGN (type2)); ++} ++ ++bool ++irange::legacy_equal_p (const irange &other) const ++{ ++ if (m_kind != other.m_kind) ++ return false; ++ if (m_kind == VR_UNDEFINED) ++ return true; ++ if (m_kind == VR_VARYING) ++ return range_compatible_p (type (), other.type ()); ++ return (vrp_operand_equal_p (tree_lower_bound (0), ++ other.tree_lower_bound (0)) ++ && vrp_operand_equal_p (tree_upper_bound (0), ++ other.tree_upper_bound (0))); ++} ++ ++bool ++irange::equal_p (const irange &other) const ++{ ++ return legacy_equal_p (other); ++} ++ ++/* Return TRUE if this is a symbolic range. */ ++ ++bool ++irange::symbolic_p () const ++{ ++ return (m_num_ranges > 0 ++ && (!is_gimple_min_invariant (min ()) ++ || !is_gimple_min_invariant (max ()))); ++} ++ ++/* Return TRUE if this is a constant range. */ ++ ++bool ++irange::constant_p () const ++{ ++ return (m_num_ranges > 0 ++ && TREE_CODE (min ()) == INTEGER_CST ++ && TREE_CODE (max ()) == INTEGER_CST); ++} ++ ++/* If range is a singleton, place it in RESULT and return TRUE. ++ Note: A singleton can be any gimple invariant, not just constants. ++ So, [&x, &x] counts as a singleton. */ ++ ++bool ++irange::singleton_p (tree *result) const ++{ ++ if (m_kind == VR_ANTI_RANGE) ++ { ++ if (nonzero_p ()) ++ { ++ if (TYPE_PRECISION (type ()) == 1) ++ { ++ if (result) ++ *result = max (); ++ return true; ++ } ++ return false; ++ } ++ if (num_pairs () == 1) ++ { ++ int_range vr0, vr1; ++ ranges_from_anti_range ((const int_range *) this, &vr0, &vr1); ++ return vr0.singleton_p (result); ++ } ++ } ++ // Catches non-numeric extremes as well. ++ if (m_kind == VR_RANGE ++ && vrp_operand_equal_p (min (), max ()) ++ && is_gimple_min_invariant (min ())) ++ { ++ if (result) ++ *result = min (); ++ return true; ++ } ++ return false; ++} ++ ++/* Return 1 if VAL is inside value range. ++ 0 if VAL is not inside value range. ++ -2 if we cannot tell either way. ++ ++ Benchmark compile/20001226-1.c compilation time after changing this ++ function. */ ++ ++int ++irange::value_inside_range (tree val) const ++{ ++ if (varying_p ()) ++ return 1; ++ ++ if (undefined_p ()) ++ return 0; ++ ++ int cmp1 = operand_less_p (val, min ()); ++ if (cmp1 == -2) ++ return -2; ++ if (cmp1 == 1) ++ return m_kind != VR_RANGE; ++ ++ int cmp2 = operand_less_p (max (), val); ++ if (cmp2 == -2) ++ return -2; ++ ++ if (m_kind == VR_RANGE) ++ return !cmp2; ++ else ++ return !!cmp2; ++} ++ ++/* Return TRUE if it is possible that range contains VAL. */ ++ ++bool ++irange::may_contain_p (tree val) const ++{ ++ return value_inside_range (val) != 0; ++} ++ ++/* Return TRUE if range contains INTEGER_CST. */ ++/* Return 1 if VAL is inside value range. ++ 0 if VAL is not inside value range. ++ ++ Benchmark compile/20001226-1.c compilation time after changing this ++ function. */ ++ ++ ++bool ++irange::contains_p (tree cst) const ++{ ++ if (undefined_p ()) ++ return false; ++ ++ gcc_checking_assert (TREE_CODE (cst) == INTEGER_CST); ++ if (symbolic_p ()) ++ { ++ int_range numeric_range (*this); ++ numeric_range.normalize_symbolics (); ++ return numeric_range.contains_p (cst); ++ } ++ return value_inside_range (cst) == 1; ++} ++bool ++irange::nonnegative_p () const ++{ ++ return wi::ge_p (lower_bound (), 0, TYPE_SIGN (type ())); ++} ++ ++bool ++irange::nonpositive_p () const ++{ ++ return wi::le_p (upper_bound (), 0, TYPE_SIGN (type ())); ++} ++ ++ ++ ++/* Normalize addresses into constants. */ ++ ++void ++irange::normalize_addresses () ++{ ++ if (undefined_p ()) ++ return; ++ ++ if (!POINTER_TYPE_P (type ()) || range_has_numeric_bounds_p (this)) ++ return; ++ ++ if (!range_includes_zero_p (this)) ++ { ++ gcc_checking_assert (TREE_CODE (min ()) == ADDR_EXPR ++ || TREE_CODE (max ()) == ADDR_EXPR); ++ set_nonzero (type ()); ++ return; ++ } ++ set_varying (type ()); ++} ++ ++/* Normalize symbolics and addresses into constants. */ ++ ++void ++irange::normalize_symbolics () ++{ ++ if (varying_p () || undefined_p ()) ++ return; ++ ++ tree ttype = type (); ++ bool min_symbolic = !is_gimple_min_invariant (min ()); ++ bool max_symbolic = !is_gimple_min_invariant (max ()); ++ if (!min_symbolic && !max_symbolic) ++ { ++ normalize_addresses (); ++ return; ++ } ++ ++ // [SYM, SYM] -> VARYING ++ if (min_symbolic && max_symbolic) ++ { ++ set_varying (ttype); ++ return; ++ } ++ if (kind () == VR_RANGE) ++ { ++ // [SYM, NUM] -> [-MIN, NUM] ++ if (min_symbolic) ++ { ++ set (vrp_val_min (ttype), max ()); ++ return; ++ } ++ // [NUM, SYM] -> [NUM, +MAX] ++ set (min (), vrp_val_max (ttype)); ++ return; ++ } ++ gcc_checking_assert (kind () == VR_ANTI_RANGE); ++ // ~[SYM, NUM] -> [NUM + 1, +MAX] ++ if (min_symbolic) ++ { ++ if (!vrp_val_is_max (max ())) ++ { ++ tree n = wide_int_to_tree (ttype, wi::to_wide (max ()) + 1); ++ set (n, vrp_val_max (ttype)); ++ return; ++ } ++ set_varying (ttype); ++ return; ++ } ++ // ~[NUM, SYM] -> [-MIN, NUM - 1] ++ if (!vrp_val_is_min (min ())) ++ { ++ tree n = wide_int_to_tree (ttype, wi::to_wide (min ()) - 1); ++ set (vrp_val_min (ttype), n); ++ return; ++ } ++ set_varying (ttype); ++} ++ ++/* Intersect the two value-ranges { *VR0TYPE, *VR0MIN, *VR0MAX } and ++ { VR1TYPE, VR0MIN, VR0MAX } and store the result ++ in { *VR0TYPE, *VR0MIN, *VR0MAX }. This may not be the smallest ++ possible such range. The resulting range is not canonicalized. */ ++ ++static void ++intersect_ranges (enum value_range_type *vr0type, ++ tree *vr0min, tree *vr0max, ++ enum value_range_type vr1type, ++ tree vr1min, tree vr1max) ++{ ++ bool mineq = vrp_operand_equal_p (*vr0min, vr1min); ++ bool maxeq = vrp_operand_equal_p (*vr0max, vr1max); ++ ++ /* [] is vr0, () is vr1 in the following classification comments. */ ++ if (mineq && maxeq) ++ { ++ /* [( )] */ ++ if (*vr0type == vr1type) ++ /* Nothing to do for equal ranges. */ ++ ; ++ else if ((*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ || (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE)) ++ { ++ /* For anti-range with range intersection the result is empty. */ ++ *vr0type = VR_UNDEFINED; ++ *vr0min = NULL_TREE; ++ *vr0max = NULL_TREE; ++ } ++ else ++ gcc_unreachable (); ++ } ++ else if (operand_less_p (*vr0max, vr1min) == 1 ++ || operand_less_p (vr1max, *vr0min) == 1) ++ { ++ /* [ ] ( ) or ( ) [ ] ++ If the ranges have an empty intersection, the result of the ++ intersect operation is the range for intersecting an ++ anti-range with a range or empty when intersecting two ranges. */ ++ if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ ; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ { ++ *vr0type = VR_UNDEFINED; ++ *vr0min = NULL_TREE; ++ *vr0max = NULL_TREE; ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ /* If the anti-ranges are adjacent to each other merge them. */ ++ if (TREE_CODE (*vr0max) == INTEGER_CST ++ && TREE_CODE (vr1min) == INTEGER_CST ++ && operand_less_p (*vr0max, vr1min) == 1 ++ && integer_onep (int_const_binop (MINUS_EXPR, ++ vr1min, *vr0max))) ++ *vr0max = vr1max; ++ else if (TREE_CODE (vr1max) == INTEGER_CST ++ && TREE_CODE (*vr0min) == INTEGER_CST ++ && operand_less_p (vr1max, *vr0min) == 1 ++ && integer_onep (int_const_binop (MINUS_EXPR, ++ *vr0min, vr1max))) ++ *vr0min = vr1min; ++ /* Else arbitrarily take VR0. */ ++ } ++ } ++ else if ((maxeq || operand_less_p (vr1max, *vr0max) == 1) ++ && (mineq || operand_less_p (*vr0min, vr1min) == 1)) ++ { ++ /* [ ( ) ] or [( ) ] or [ ( )] */ ++ if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ { ++ /* If both are ranges the result is the inner one. */ ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ /* Choose the right gap if the left one is empty. */ ++ if (mineq) ++ { ++ if (TREE_CODE (vr1max) != INTEGER_CST) ++ *vr0min = vr1max; ++ else if (TYPE_PRECISION (TREE_TYPE (vr1max)) == 1 ++ && !TYPE_UNSIGNED (TREE_TYPE (vr1max))) ++ *vr0min ++ = int_const_binop (MINUS_EXPR, vr1max, ++ build_int_cst (TREE_TYPE (vr1max), -1)); ++ else ++ *vr0min ++ = int_const_binop (PLUS_EXPR, vr1max, ++ build_int_cst (TREE_TYPE (vr1max), 1)); ++ } ++ /* Choose the left gap if the right one is empty. */ ++ else if (maxeq) ++ { ++ if (TREE_CODE (vr1min) != INTEGER_CST) ++ *vr0max = vr1min; ++ else if (TYPE_PRECISION (TREE_TYPE (vr1min)) == 1 ++ && !TYPE_UNSIGNED (TREE_TYPE (vr1min))) ++ *vr0max ++ = int_const_binop (PLUS_EXPR, vr1min, ++ build_int_cst (TREE_TYPE (vr1min), -1)); ++ else ++ *vr0max ++ = int_const_binop (MINUS_EXPR, vr1min, ++ build_int_cst (TREE_TYPE (vr1min), 1)); ++ } ++ /* Choose the anti-range if the range is effectively varying. */ ++ else if (vrp_val_is_min (*vr0min) ++ && vrp_val_is_max (*vr0max)) ++ { ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ /* Else choose the range. */ ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ /* If both are anti-ranges the result is the outer one. */ ++ ; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ /* The intersection is empty. */ ++ *vr0type = VR_UNDEFINED; ++ *vr0min = NULL_TREE; ++ *vr0max = NULL_TREE; ++ } ++ else ++ gcc_unreachable (); ++ } ++ else if ((maxeq || operand_less_p (*vr0max, vr1max) == 1) ++ && (mineq || operand_less_p (vr1min, *vr0min) == 1)) ++ { ++ /* ( [ ] ) or ([ ] ) or ( [ ]) */ ++ if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ /* Choose the inner range. */ ++ ; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ /* Choose the right gap if the left is empty. */ ++ if (mineq) ++ { ++ *vr0type = VR_RANGE; ++ if (TREE_CODE (*vr0max) != INTEGER_CST) ++ *vr0min = *vr0max; ++ else if (TYPE_PRECISION (TREE_TYPE (*vr0max)) == 1 ++ && !TYPE_UNSIGNED (TREE_TYPE (*vr0max))) ++ *vr0min ++ = int_const_binop (MINUS_EXPR, *vr0max, ++ build_int_cst (TREE_TYPE (*vr0max), -1)); ++ else ++ *vr0min ++ = int_const_binop (PLUS_EXPR, *vr0max, ++ build_int_cst (TREE_TYPE (*vr0max), 1)); ++ *vr0max = vr1max; ++ } ++ /* Choose the left gap if the right is empty. */ ++ else if (maxeq) ++ { ++ *vr0type = VR_RANGE; ++ if (TREE_CODE (*vr0min) != INTEGER_CST) ++ *vr0max = *vr0min; ++ else if (TYPE_PRECISION (TREE_TYPE (*vr0min)) == 1 ++ && !TYPE_UNSIGNED (TREE_TYPE (*vr0min))) ++ *vr0max ++ = int_const_binop (PLUS_EXPR, *vr0min, ++ build_int_cst (TREE_TYPE (*vr0min), -1)); ++ else ++ *vr0max ++ = int_const_binop (MINUS_EXPR, *vr0min, ++ build_int_cst (TREE_TYPE (*vr0min), 1)); ++ *vr0min = vr1min; ++ } ++ /* Choose the anti-range if the range is effectively varying. */ ++ else if (vrp_val_is_min (vr1min) ++ && vrp_val_is_max (vr1max)) ++ ; ++ /* Choose the anti-range if it is ~[0,0], that range is special ++ enough to special case when vr1's range is relatively wide. ++ At least for types bigger than int - this covers pointers ++ and arguments to functions like ctz. */ ++ else if (*vr0min == *vr0max ++ && integer_zerop (*vr0min) ++ && ((TYPE_PRECISION (TREE_TYPE (*vr0min)) ++ >= TYPE_PRECISION (integer_type_node)) ++ || POINTER_TYPE_P (TREE_TYPE (*vr0min))) ++ && TREE_CODE (vr1max) == INTEGER_CST ++ && TREE_CODE (vr1min) == INTEGER_CST ++ && (wi::clz (wi::to_wide (vr1max) - wi::to_wide (vr1min)) ++ < TYPE_PRECISION (TREE_TYPE (*vr0min)) / 2)) ++ ; ++ /* Else choose the range. */ ++ else ++ { ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ /* If both are anti-ranges the result is the outer one. */ ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ else if (vr1type == VR_ANTI_RANGE ++ && *vr0type == VR_RANGE) ++ { ++ /* The intersection is empty. */ ++ *vr0type = VR_UNDEFINED; ++ *vr0min = NULL_TREE; ++ *vr0max = NULL_TREE; ++ } ++ else ++ gcc_unreachable (); ++ } ++ else if ((operand_less_p (vr1min, *vr0max) == 1 ++ || operand_equal_p (vr1min, *vr0max, 0)) ++ && operand_less_p (*vr0min, vr1min) == 1 ++ && operand_less_p (*vr0max, vr1max) == 1) ++ { ++ /* [ ( ] ) or [ ]( ) */ ++ if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ *vr0max = vr1max; ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ *vr0min = vr1min; ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ if (TREE_CODE (vr1min) == INTEGER_CST) ++ *vr0max = int_const_binop (MINUS_EXPR, vr1min, ++ build_int_cst (TREE_TYPE (vr1min), 1)); ++ else ++ *vr0max = vr1min; ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ *vr0type = VR_RANGE; ++ if (TREE_CODE (*vr0max) == INTEGER_CST) ++ *vr0min = int_const_binop (PLUS_EXPR, *vr0max, ++ build_int_cst (TREE_TYPE (*vr0max), 1)); ++ else ++ *vr0min = *vr0max; ++ *vr0max = vr1max; ++ } ++ else ++ gcc_unreachable (); ++ } ++ else if ((operand_less_p (*vr0min, vr1max) == 1 ++ || operand_equal_p (*vr0min, vr1max, 0)) ++ && operand_less_p (vr1min, *vr0min) == 1 ++ && operand_less_p (vr1max, *vr0max) == 1) ++ { ++ /* ( [ ) ] or ( )[ ] */ ++ if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ *vr0min = vr1min; ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ *vr0max = vr1max; ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ if (TREE_CODE (vr1max) == INTEGER_CST) ++ *vr0min = int_const_binop (PLUS_EXPR, vr1max, ++ build_int_cst (TREE_TYPE (vr1max), 1)); ++ else ++ *vr0min = vr1max; ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ *vr0type = VR_RANGE; ++ if (TREE_CODE (*vr0min) == INTEGER_CST) ++ *vr0max = int_const_binop (MINUS_EXPR, *vr0min, ++ build_int_cst (TREE_TYPE (*vr0min), 1)); ++ else ++ *vr0max = *vr0min; ++ *vr0min = vr1min; ++ } ++ else ++ gcc_unreachable (); ++ } ++ ++ /* If we know the intersection is empty, there's no need to ++ conservatively add anything else to the set. */ ++ if (*vr0type == VR_UNDEFINED) ++ return; ++ ++ /* As a fallback simply use { *VRTYPE, *VR0MIN, *VR0MAX } as ++ result for the intersection. That's always a conservative ++ correct estimate unless VR1 is a constant singleton range ++ in which case we choose that. */ ++ if (vr1type == VR_RANGE ++ && is_gimple_min_invariant (vr1min) ++ && vrp_operand_equal_p (vr1min, vr1max)) ++ { ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++} ++ ++/* Helper for the intersection operation for value ranges. Given two ++ ranges VR0 and VR1, set VR0 to the intersection of both ranges. ++ This may not be the smallest possible such range. */ ++ ++void ++irange::legacy_intersect (irange *vr0, const irange *vr1) ++{ ++ /* If either range is VR_VARYING the other one wins. */ ++ if (vr1->varying_p ()) ++ return; ++ if (vr0->varying_p ()) ++ { ++ vr0->set (vr1->min (), vr1->max (), vr1->kind ()); ++ return; ++ } ++ ++ /* When either range is VR_UNDEFINED the resulting range is ++ VR_UNDEFINED, too. */ ++ if (vr0->undefined_p ()) ++ return; ++ if (vr1->undefined_p ()) ++ { ++ vr0->set_undefined (); ++ return; ++ } ++ ++ value_range_type vr0kind = vr0->kind (); ++ tree vr0min = vr0->min (); ++ tree vr0max = vr0->max (); ++ ++ intersect_ranges (&vr0kind, &vr0min, &vr0max, ++ vr1->kind (), vr1->min (), vr1->max ()); ++ ++ /* Make sure to canonicalize the result though as the inversion of a ++ VR_RANGE can still be a VR_RANGE. */ ++ if (vr0kind == VR_UNDEFINED) ++ vr0->set_undefined (); ++ else if (vr0kind == VR_VARYING) ++ { ++ /* If we failed, use the original VR0. */ ++ return; ++ } ++ else ++ vr0->set (vr0min, vr0max, vr0kind); ++} ++ ++/* Union the two value-ranges { *VR0TYPE, *VR0MIN, *VR0MAX } and ++ { VR1TYPE, VR0MIN, VR0MAX } and store the result ++ in { *VR0TYPE, *VR0MIN, *VR0MAX }. This may not be the smallest ++ possible such range. The resulting range is not canonicalized. */ ++ ++static void ++union_ranges (enum value_range_type *vr0type, ++ tree *vr0min, tree *vr0max, ++ enum value_range_type vr1type, ++ tree vr1min, tree vr1max) ++{ ++ int cmpmin = compare_values (*vr0min, vr1min); ++ int cmpmax = compare_values (*vr0max, vr1max); ++ bool mineq = cmpmin == 0; ++ bool maxeq = cmpmax == 0; ++ ++ /* [] is vr0, () is vr1 in the following classification comments. */ ++ if (mineq && maxeq) ++ { ++ /* [( )] */ ++ if (*vr0type == vr1type) ++ /* Nothing to do for equal ranges. */ ++ ; ++ else if ((*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ || (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE)) ++ { ++ /* For anti-range with range union the result is varying. */ ++ goto give_up; ++ } ++ else ++ gcc_unreachable (); ++ } ++ else if (operand_less_p (*vr0max, vr1min) == 1 ++ || operand_less_p (vr1max, *vr0min) == 1) ++ { ++ /* [ ] ( ) or ( ) [ ] ++ If the ranges have an empty intersection, result of the union ++ operation is the anti-range or if both are anti-ranges ++ it covers all. */ ++ if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ goto give_up; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ ; ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ { ++ /* The result is the convex hull of both ranges. */ ++ if (operand_less_p (*vr0max, vr1min) == 1) ++ { ++ /* If the result can be an anti-range, create one. */ ++ if (TREE_CODE (*vr0max) == INTEGER_CST ++ && TREE_CODE (vr1min) == INTEGER_CST ++ && vrp_val_is_min (*vr0min) ++ && vrp_val_is_max (vr1max)) ++ { ++ tree min = int_const_binop (PLUS_EXPR, ++ *vr0max, ++ build_int_cst (TREE_TYPE (*vr0max), 1)); ++ tree max = int_const_binop (MINUS_EXPR, ++ vr1min, ++ build_int_cst (TREE_TYPE (vr1min), 1)); ++ if (!operand_less_p (max, min)) ++ { ++ *vr0type = VR_ANTI_RANGE; ++ *vr0min = min; ++ *vr0max = max; ++ } ++ else ++ *vr0max = vr1max; ++ } ++ else ++ *vr0max = vr1max; ++ } ++ else ++ { ++ /* If the result can be an anti-range, create one. */ ++ if (TREE_CODE (vr1max) == INTEGER_CST ++ && TREE_CODE (*vr0min) == INTEGER_CST ++ && vrp_val_is_min (vr1min) ++ && vrp_val_is_max (*vr0max)) ++ { ++ tree min = int_const_binop (PLUS_EXPR, ++ vr1max, ++ build_int_cst (TREE_TYPE (vr1max), 1)); ++ tree max = int_const_binop (MINUS_EXPR, ++ *vr0min, ++ build_int_cst (TREE_TYPE (*vr0min), 1)); ++ if (!operand_less_p (max, min)) ++ { ++ *vr0type = VR_ANTI_RANGE; ++ *vr0min = min; ++ *vr0max = max; ++ } ++ else ++ *vr0min = vr1min; ++ } ++ else ++ *vr0min = vr1min; ++ } ++ } ++ else ++ gcc_unreachable (); ++ } ++ else if ((maxeq || cmpmax == 1) ++ && (mineq || cmpmin == -1)) ++ { ++ /* [ ( ) ] or [( ) ] or [ ( )] */ ++ if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ ; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ /* Arbitrarily choose the right or left gap. */ ++ if (!mineq && TREE_CODE (vr1min) == INTEGER_CST) ++ *vr0max = int_const_binop (MINUS_EXPR, vr1min, ++ build_int_cst (TREE_TYPE (vr1min), 1)); ++ else if (!maxeq && TREE_CODE (vr1max) == INTEGER_CST) ++ *vr0min = int_const_binop (PLUS_EXPR, vr1max, ++ build_int_cst (TREE_TYPE (vr1max), 1)); ++ else ++ goto give_up; ++ } ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ /* The result covers everything. */ ++ goto give_up; ++ else ++ gcc_unreachable (); ++ } ++ else if ((maxeq || cmpmax == -1) ++ && (mineq || cmpmin == 1)) ++ { ++ /* ( [ ] ) or ([ ] ) or ( [ ]) */ ++ if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ { ++ *vr0type = vr1type; ++ *vr0min = vr1min; ++ *vr0max = vr1max; ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ ; ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ *vr0type = VR_ANTI_RANGE; ++ if (!mineq && TREE_CODE (*vr0min) == INTEGER_CST) ++ { ++ *vr0max = int_const_binop (MINUS_EXPR, *vr0min, ++ build_int_cst (TREE_TYPE (*vr0min), 1)); ++ *vr0min = vr1min; ++ } ++ else if (!maxeq && TREE_CODE (*vr0max) == INTEGER_CST) ++ { ++ *vr0min = int_const_binop (PLUS_EXPR, *vr0max, ++ build_int_cst (TREE_TYPE (*vr0max), 1)); ++ *vr0max = vr1max; ++ } ++ else ++ goto give_up; ++ } ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ /* The result covers everything. */ ++ goto give_up; ++ else ++ gcc_unreachable (); ++ } ++ else if (cmpmin == -1 ++ && cmpmax == -1 ++ && (operand_less_p (vr1min, *vr0max) == 1 ++ || operand_equal_p (vr1min, *vr0max, 0))) ++ { ++ /* [ ( ] ) or [ ]( ) */ ++ if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ *vr0max = vr1max; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ *vr0min = vr1min; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ if (TREE_CODE (vr1min) == INTEGER_CST) ++ *vr0max = int_const_binop (MINUS_EXPR, vr1min, ++ build_int_cst (TREE_TYPE (vr1min), 1)); ++ else ++ goto give_up; ++ } ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ if (TREE_CODE (*vr0max) == INTEGER_CST) ++ { ++ *vr0type = vr1type; ++ *vr0min = int_const_binop (PLUS_EXPR, *vr0max, ++ build_int_cst (TREE_TYPE (*vr0max), 1)); ++ *vr0max = vr1max; ++ } ++ else ++ goto give_up; ++ } ++ else ++ gcc_unreachable (); ++ } ++ else if (cmpmin == 1 ++ && cmpmax == 1 ++ && (operand_less_p (*vr0min, vr1max) == 1 ++ || operand_equal_p (*vr0min, vr1max, 0))) ++ { ++ /* ( [ ) ] or ( )[ ] */ ++ if (*vr0type == VR_RANGE ++ && vr1type == VR_RANGE) ++ *vr0min = vr1min; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ *vr0max = vr1max; ++ else if (*vr0type == VR_ANTI_RANGE ++ && vr1type == VR_RANGE) ++ { ++ if (TREE_CODE (vr1max) == INTEGER_CST) ++ *vr0min = int_const_binop (PLUS_EXPR, vr1max, ++ build_int_cst (TREE_TYPE (vr1max), 1)); ++ else ++ goto give_up; ++ } ++ else if (*vr0type == VR_RANGE ++ && vr1type == VR_ANTI_RANGE) ++ { ++ if (TREE_CODE (*vr0min) == INTEGER_CST) ++ { ++ *vr0type = vr1type; ++ *vr0max = int_const_binop (MINUS_EXPR, *vr0min, ++ build_int_cst (TREE_TYPE (*vr0min), 1)); ++ *vr0min = vr1min; ++ } ++ else ++ goto give_up; ++ } ++ else ++ gcc_unreachable (); ++ } ++ else ++ goto give_up; ++ ++ return; ++ ++give_up: ++ *vr0type = VR_VARYING; ++ *vr0min = NULL_TREE; ++ *vr0max = NULL_TREE; ++} ++ ++/* Helper for meet operation for value ranges. Given two ranges VR0 ++ and VR1, set VR0 to the union of both ranges. This may not be the ++ smallest possible such range. */ ++ ++void ++irange::legacy_union (irange *vr0, const irange *vr1) ++{ ++ /* VR0 has the resulting range if VR1 is undefined or VR0 is varying. */ ++ if (vr1->undefined_p () ++ || vr0->varying_p ()) ++ return; ++ ++ /* VR1 has the resulting range if VR0 is undefined or VR1 is varying. */ ++ if (vr0->undefined_p ()) ++ { ++ vr0->set (vr1->min (), vr1->max (), vr1->kind ()); ++ return; ++ } ++ ++ if (vr1->varying_p ()) ++ { ++ vr0->set_varying (vr1->type ()); ++ return; ++ } ++ ++ value_range_type vr0kind = vr0->kind (); ++ tree vr0min = vr0->min (); ++ tree vr0max = vr0->max (); ++ ++ union_ranges (&vr0kind, &vr0min, &vr0max, ++ vr1->kind (), vr1->min (), vr1->max ()); ++ ++ if (vr0kind == VR_UNDEFINED) ++ vr0->set_undefined (); ++ else if (vr0kind == VR_VARYING) ++ { ++ /* Failed to find an efficient meet. Before giving up and ++ setting the result to VARYING, see if we can at least derive ++ a non-zero range. */ ++ if (range_includes_zero_p (vr0) == 0 ++ && range_includes_zero_p (vr1) == 0) ++ vr0->set_nonzero (vr0->type ()); ++ else ++ vr0->set_varying (vr0->type ()); ++ } ++ else ++ vr0->set (vr0min, vr0max, vr0kind); ++} ++ ++/* Meet operation for value ranges. Given two value ranges VR0 and ++ VR1, store in VR0 a range that contains both VR0 and VR1. This ++ may not be the smallest possible such range. */ ++ ++void ++irange::union_ (const irange *other) ++{ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "Meeting\n "); ++ dump_value_range (dump_file, this); ++ fprintf (dump_file, "\nand\n "); ++ dump_value_range (dump_file, other); ++ fprintf (dump_file, "\n"); ++ } ++ ++ legacy_union (this, other); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "to\n "); ++ dump_value_range (dump_file, this); ++ fprintf (dump_file, "\n"); ++ } ++} ++ ++void ++irange::intersect (const irange *other) ++{ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "Intersecting\n "); ++ dump_value_range (dump_file, this); ++ fprintf (dump_file, "\nand\n "); ++ dump_value_range (dump_file, other); ++ fprintf (dump_file, "\n"); ++ } ++ ++ legacy_intersect (this, other); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "to\n "); ++ dump_value_range (dump_file, this); ++ fprintf (dump_file, "\n"); ++ } ++} ++ ++// Multirange intersect for a specified wide_int [lb, ub] range. ++ ++void ++irange::intersect (const wide_int& lb, const wide_int& ub) ++{ ++ // Undefined remains undefined. ++ if (undefined_p ()) ++ return; ++ ++ intersect (int_range (type (), lb, ub)); ++} ++ ++// Return the inverse of a range. ++ ++void ++irange::invert () ++{ ++ // We can't just invert VR_RANGE and VR_ANTI_RANGE because we may ++ // create non-canonical ranges. Use the constructors instead. ++ if (m_kind == VR_RANGE) ++ *this = int_range (min (), max (), VR_ANTI_RANGE); ++ else if (m_kind == VR_ANTI_RANGE) ++ *this = int_range (min (), max ()); ++ else ++ gcc_unreachable (); ++} ++ ++static void ++dump_bound_with_infinite_markers (FILE *file, tree bound) ++{ ++ tree type = TREE_TYPE (bound); ++ 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) ++ && TREE_CODE (bound) == INTEGER_CST ++ && wi::to_wide (bound) == type_min ++ && TYPE_PRECISION (type) != 1) ++ fprintf (file, "-INF"); ++ else if (TREE_CODE (bound) == INTEGER_CST ++ && wi::to_wide (bound) == type_max ++ && TYPE_PRECISION (type) != 1) ++ fprintf (file, "+INF"); ++ else ++ print_generic_expr (file, bound); ++} ++ ++void ++irange::dump (FILE *file) const ++{ ++ if (undefined_p ()) ++ { ++ fprintf (file, "UNDEFINED"); ++ return; ++ } ++ print_generic_expr (file, type ()); ++ fprintf (file, " "); ++ if (varying_p ()) ++ { ++ fprintf (file, "VARYING"); ++ return; ++ } ++ ++ fprintf (file, "%s[", (m_kind == VR_ANTI_RANGE) ? "~" : ""); ++ dump_bound_with_infinite_markers (file, min ()); ++ fprintf (file, ", "); ++ dump_bound_with_infinite_markers (file, max ()); ++ fprintf (file, "]"); ++} ++ ++void ++irange::debug () const ++{ ++ dump (stderr); ++ fprintf (stderr, "\n"); ++} ++ ++void ++dump_value_range (FILE *file, const irange *vr) ++{ ++ vr->dump (file); ++} ++ ++DEBUG_FUNCTION void ++debug (const irange *vr) ++{ ++ dump_value_range (stderr, vr); ++ fprintf (stderr, "\n"); ++} ++ ++DEBUG_FUNCTION void ++debug (const irange &vr) ++{ ++ debug (&vr); ++} ++ ++DEBUG_FUNCTION void ++debug (const int_range *vr) ++{ ++ dump_value_range (stderr, vr); ++ fprintf (stderr, "\n"); ++} ++ ++DEBUG_FUNCTION void ++debug (const int_range &vr) ++{ ++ dump_value_range (stderr, &vr); ++ fprintf (stderr, "\n"); ++} ++ ++/* Create two value-ranges in *VR0 and *VR1 from the anti-range *AR ++ so that *VR0 U *VR1 == *AR. Returns true if that is possible, ++ false otherwise. If *AR can be represented with a single range ++ *VR1 will be VR_UNDEFINED. */ ++ ++bool ++ranges_from_anti_range (const int_range *ar, ++ int_range *vr0, int_range *vr1) ++{ ++ tree type = ar->type (); ++ ++ vr0->set_undefined (); ++ vr1->set_undefined (); ++ ++ /* As a future improvement, we could handle ~[0, A] as: [-INF, -1] U ++ [A+1, +INF]. Not sure if this helps in practice, though. */ ++ ++ if (ar->kind () != VR_ANTI_RANGE ++ || TREE_CODE (ar->min ()) != INTEGER_CST ++ || TREE_CODE (ar->max ()) != INTEGER_CST ++ || !vrp_val_min (type) ++ || !vrp_val_max (type)) ++ return false; ++ ++ if (tree_int_cst_lt (vrp_val_min (type), ar->min ())) ++ vr0->set (vrp_val_min (type), ++ wide_int_to_tree (type, wi::to_wide (ar->min ()) - 1)); ++ if (tree_int_cst_lt (ar->max (), vrp_val_max (type))) ++ vr1->set (wide_int_to_tree (type, wi::to_wide (ar->max ()) + 1), ++ vrp_val_max (type)); ++ if (vr0->undefined_p ()) ++ { ++ *vr0 = *vr1; ++ vr1->set_undefined (); ++ } ++ ++ return !vr0->undefined_p (); ++} ++ ++bool ++range_has_numeric_bounds_p (const irange *vr) ++{ ++ return (!vr->undefined_p () ++ && TREE_CODE (vr->min ()) == INTEGER_CST ++ && TREE_CODE (vr->max ()) == INTEGER_CST); ++} +diff --git a/gcc/value-range.h b/gcc/value-range.h +new file mode 100644 +index 000000000..0a9d074c5 +--- /dev/null ++++ b/gcc/value-range.h +@@ -0,0 +1,160 @@ ++/* Support routines for value ranges. ++ Copyright (C) 2019-2022 Free Software Foundation, Inc. ++ Contributed by Aldy Hernandez and ++ Andrew Macleod . ++ ++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 GCC_VALUE_RANGE_H ++#define GCC_VALUE_RANGE_H ++ ++#include "tree-vrp.h" ++ ++// Range of values that can be associated with an SSA_NAME. ++// ++// This is the base class without any storage. ++ ++class GTY((user)) irange ++{ ++ friend class irange_allocator; ++public: ++ // In-place setters. ++ void set (tree, tree, value_range_type = VR_RANGE); ++ void set_nonzero (tree); ++ void set_zero (tree); ++ void set_varying (tree type); ++ void set_undefined (); ++ ++ // Range types. ++ static bool supports_type_p (tree); ++ tree type () const; ++ ++ // Iteration over sub-ranges. ++ unsigned num_pairs () const; ++ wide_int lower_bound (unsigned = 0) const; ++ wide_int upper_bound (unsigned) const; ++ wide_int upper_bound () const; ++ ++ // Predicates. ++ bool zero_p () const; ++ bool nonzero_p () const; ++ bool undefined_p () const; ++ bool varying_p () const; ++ bool singleton_p (tree *result = NULL) const; ++ bool contains_p (tree) const; ++ bool nonnegative_p () const; ++ bool nonpositive_p () const; ++ ++ // In-place operators. ++ void union_ (const irange &); ++ void intersect (const irange &); ++ void intersect (const wide_int& lb, const wide_int& ub); ++ void invert (); ++ ++ // Operator overloads. ++ irange& operator= (const irange &); ++ bool operator== (const irange &) const; ++ bool operator!= (const irange &r) const { return !(*this == r); } ++ ++ // Misc methods. ++ bool fits_p (const irange &r) { return m_max_ranges >= r.num_pairs (); } ++ void dump (FILE * = stderr) const; ++ void debug () const; ++ ++ // Deprecated legacy public methods. ++ enum value_range_type kind () const; // DEPRECATED ++ tree min () const; // DEPRECATED ++ tree max () const; // DEPRECATED ++ bool symbolic_p () const; // DEPRECATED ++ bool constant_p () const; // DEPRECATED ++ void normalize_symbolics (); // DEPRECATED ++ void normalize_addresses (); // DEPRECATED ++ bool may_contain_p (tree) const; // DEPRECATED ++ void set (tree); // DEPRECATED ++ bool equal_p (const irange &) const; // DEPRECATED ++ void union_ (const class irange *); // DEPRECATED ++ void intersect (const irange *); // DEPRECATED ++ ++protected: ++ irange (tree *, unsigned); ++ // potential promotion to public? ++ tree tree_lower_bound (unsigned = 0) const; ++ tree tree_upper_bound (unsigned) const; ++ tree tree_upper_bound () const; ++ ++ // In-place operators. ++ void normalize_kind (); ++ ++ bool legacy_equal_p (const irange &) const; ++ void legacy_union (irange *, const irange *); ++ void legacy_intersect (irange *, const irange *); ++ void verify_range (); ++ wide_int legacy_lower_bound (unsigned = 0) const; ++ wide_int legacy_upper_bound (unsigned) const; ++ int value_inside_range (tree) const; ++ void copy_to_legacy (const irange &); ++ ++private: ++ friend void gt_ggc_mx (irange *); ++ friend void gt_pch_nx (irange *); ++ friend void gt_pch_nx (irange *, gt_pointer_operator, void *); ++ ++ void irange_set_1bit_anti_range (tree, tree); ++ bool varying_compatible_p () const; ++ ++ unsigned char m_num_ranges; ++ unsigned char m_max_ranges; ++ ENUM_BITFIELD(value_range_type) m_kind : 8; ++ ++protected: ++ tree *m_base; ++}; ++ ++class GTY((user)) int_range : public irange ++{ ++public: ++ int_range (); ++ int_range (tree, tree, value_range_type = VR_RANGE); ++ int_range (tree type, const wide_int &, const wide_int &, ++ value_range_type = VR_RANGE); ++ int_range (tree type); ++ int_range (const int_range &); ++ int_range (const irange &); ++ int_range& operator= (const int_range &); ++private: ++ friend void gt_ggc_mx (int_range *); ++ friend void gt_pch_nx (int_range *); ++ friend void gt_pch_nx (int_range *, gt_pointer_operator, void *); ++ ++ tree m_ranges[2]; ++}; ++ ++// This is a special int_range with only one pair, plus ++// VR_ANTI_RANGE magic to describe slightly more than can be described ++// in one pair. It is described in the code as a "legacy range" (as ++// opposed to multi-ranges which have multiple sub-ranges). It is ++// provided for backward compatibility with code that has not been ++// converted to multi-range irange's. ++// ++// There are copy operators to seamlessly copy to/fro multi-ranges. ++ ++extern bool range_has_numeric_bounds_p (const irange *); ++extern bool ranges_from_anti_range (const int_range *, ++ int_range *, int_range *); ++extern void dump_value_range (FILE *, const irange *); ++ ++#endif // GCC_VALUE_RANGE_H diff --git a/SOURCES/gcc8-struct-dfc-optimization.patch b/SOURCES/gcc8-struct-dfc-optimization.patch new file mode 100644 index 0000000000000000000000000000000000000000..5b10db4d49a37b27d1b61644811494e520d20bc4 --- /dev/null +++ b/SOURCES/gcc8-struct-dfc-optimization.patch @@ -0,0 +1,6733 @@ +diff --git a/gcc/common.opt b/gcc/common.opt +index 45d15667f..639c92e56 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -1753,6 +1753,17 @@ 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 Report Var(flag_ipa_vrp) Optimization +diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c +index 2100635fd..c052537db 100644 +--- a/gcc/config/aarch64/aarch64.c ++++ b/gcc/config/aarch64/aarch64.c +@@ -7547,10 +7547,16 @@ override_C_optimize_options (struct gcc_options *opts) + 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_array_widen_compare = 1; + opts->x_flag_ccmp2 = 1; + opts->x_flag_gnu89_inline = 1; ++ maybe_set_param_value (PARAM_PARAM_POINTER_COMPRESSION_SIZE, ++ 16, ++ opts->x_param_values, ++ global_options_set.x_param_values); + maybe_set_param_value (PARAM_MAX_INLINE_INSNS_AUTO, + 64, + opts->x_param_values, +diff --git a/gcc/gimple.c b/gcc/gimple.c +index 18fc2f86a..6deb9d80a 100644 +--- a/gcc/gimple.c ++++ b/gcc/gimple.c +@@ -1710,6 +1710,20 @@ gimple_get_lhs (const gimple *stmt) + return NULL_TREE; + } + ++tree ++gimple_get_lhs_or_phi_result (const gimple *stmt) ++{ ++ enum gimple_code code = gimple_code (stmt); ++ ++ if (code == GIMPLE_ASSIGN) ++ return gimple_assign_lhs (stmt); ++ else if (code == GIMPLE_CALL) ++ return gimple_call_lhs (stmt); ++ else if (code == GIMPLE_PHI) ++ return gimple_phi_result (stmt); ++ else ++ return NULL_TREE; ++} + + /* Set the LHS of a statement that performs an assignment, + either a GIMPLE_ASSIGN or a GIMPLE_CALL. */ +diff --git a/gcc/gimple.h b/gcc/gimple.h +index 224463b33..1d3f52e64 100644 +--- a/gcc/gimple.h ++++ b/gcc/gimple.h +@@ -1498,6 +1498,7 @@ void gimple_assign_set_rhs_from_tree (gimple_stmt_iterator *, tree); + void gimple_assign_set_rhs_with_ops (gimple_stmt_iterator *, enum tree_code, + tree, tree, tree); + tree gimple_get_lhs (const gimple *); ++tree gimple_get_lhs_or_phi_result (const gimple *); + void gimple_set_lhs (gimple *, tree); + gimple *gimple_copy (gimple *); + bool gimple_has_side_effects (const gimple *); +diff --git a/gcc/ipa-struct-reorg/escapes.def b/gcc/ipa-struct-reorg/escapes.def +index 3663e064c..0eb321272 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.c b/gcc/ipa-struct-reorg/ipa-struct-reorg.c +index 15219d675..802a5af3f 100644 +--- a/gcc/ipa-struct-reorg/ipa-struct-reorg.c ++++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.c +@@ -95,30 +95,46 @@ along with GCC; see the file COPYING3. If not see + #include "symbol-summary.h" + #include "alloc-pool.h" + #include "ipa-prop.h" ++#include "bitmap.h" + #include "params.h" + #include "ipa-struct-reorg.h" + #include "tree-eh.h" +-#include "bitmap.h" + #include "cfgloop.h" + #include "langhooks.h" + #include "ipa-param-manipulation.h" + #include "tree-ssa-live.h" /* For remove_unused_locals. */ + #include "gimplify-me.h" + #include "cfgexpand.h" ++#include "gimplify.h" ++#include + + #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) ++ + /* Check whether in C language or LTO with only C language. */ + bool + lang_c_p (void) +@@ -321,6 +337,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 (fc_field *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=. */ + +@@ -336,6 +558,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; + +@@ -359,6 +590,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. */ + +@@ -475,9 +731,8 @@ 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++) + newfield[i] = NULL_TREE; +@@ -505,7 +760,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; +@@ -675,6 +931,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 * +@@ -693,8 +963,19 @@ 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 (srfield *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. */ ++ is there not already. */ + + void + srtype::add_function (srfunction *fn) +@@ -889,8 +1170,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)))) ++ static unsigned HOST_WIDE_INT field_bitsize = get_bitsize (field); ++ static unsigned HOST_WIDE_INT tmp_bitsize = get_bitsize (tmp); ++ if (field_bitsize > tmp_bitsize) + { + DECL_CHAIN (field) = tmp; + newfields[clusternum] = field; +@@ -898,12 +1180,8 @@ 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)))))) +- { +- tmp = DECL_CHAIN (tmp); +- } ++ && field_bitsize <= get_bitsize (DECL_CHAIN (tmp))) ++ tmp = DECL_CHAIN (tmp); + + /* now tmp size > field size + insert field: tmp -> xx ==> tmp -> field -> xx. */ +@@ -923,25 +1201,22 @@ srfield::create_new_optimized_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; +- } ++ /* Common var. */ ++ nt = fc_f ? fc_f->new_type : fieldtype; + else +- { +- /* RECORD_TYPE var. */ +- if (type->has_escaped ()) +- { +- nt = type->type; +- } +- else +- { +- nt = type->newtype[0]; +- } +- } ++ /* RECORD_TYPE var. */ ++ nt = type->has_escaped () ? type->type : type->newtype[0]; ++ + tree field = make_node (FIELD_DECL); + + /* Used for recursive types. +@@ -987,24 +1262,28 @@ srfield::create_new_optimized_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; + } + } + } +@@ -1023,6 +1302,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 { +@@ -1096,22 +1388,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 (srfield *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) +- { +- fields[i]->static_fc_field = fc_f; +- break; +- } ++ tree field = srf->fielddecl; ++ if (fc_info->static_fc_p) ++ srf->fc_f = find_fc_field (fc_info->static_fc_fields, field); ++ else ++ { ++ 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 +@@ -1125,7 +1433,7 @@ srtype::create_new_type (void) + + visited = true; + +- if (escapes != does_not_escape) ++ if (has_escaped ()) + { + newtype[0] = type; + return false; +@@ -1177,8 +1485,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); + } + } +@@ -1208,10 +1518,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; +@@ -1281,7 +1593,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,8 +1700,8 @@ srfunction::create_new_decls (void) + } + } + +- set_cfun (NULL); +- ++ if (node) ++ pop_cfun (); + } + + /* Dump out the field structure to FILE. */ +@@ -1464,34 +1776,34 @@ sraccess::dump (FILE *f) const + + } + +-/* 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. */ +@@ -1509,6 +1821,135 @@ 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 (gimple_stmt_iterator 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 (gimple_stmt_iterator 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 + { +@@ -1550,8 +1991,39 @@ fc_field_class::get_field_index (srfield *field) const + return -1; + } + +-fc_field_class * +-fc_type_info::find_field_class_by_type (tree type) const ++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 + { + for (fc_field_class *field_class : field_classes) + { +@@ -1582,6 +2054,130 @@ fc_type_info::record_field_class (srfield *srf) + return field_class; + } + ++fc_cond * ++fc_type_info::find_cond (tree type) const ++{ ++ for (fc_cond *cond : fc_conds) ++ { ++ if (cond->old_type == type) ++ return cond; ++ } ++ ++ return NULL; ++} ++ ++fc_cond * ++fc_type_info::create_cond (tree type) ++{ ++ fc_cond *cond = find_cond (type); ++ if (cond) ++ return cond; ++ ++ /* New cond will be stored in an auto_vec_del(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_vec *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; ++ ++ edge e; ++ edge_iterator ei; ++ if (prev) ++ FOR_EACH_EDGE (e, ei, bb->preds) ++ worklist.safe_push (e->src); ++ else ++ FOR_EACH_EDGE (e, ei, 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"); ++ ++ unsigned i; ++ basic_block bb; ++ FOR_EACH_VEC_ELT (*store_list, i, bb) ++ fprintf (dump_file, "%d ", bb->index); ++ fprintf (dump_file, "\n\n"); ++ } ++ ++ return true; ++} ++ + } // namespace struct_reorg + + namespace struct_relayout { +@@ -1699,7 +2295,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 +@@ -1728,12 +2328,15 @@ public: + void dump_functions (FILE *f); + void record_accesses (void); + void detect_cycles (void); +- bool walk_field_for_cycles (srtype*); ++ 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); +@@ -1742,9 +2345,10 @@ 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); +@@ -1867,8 +2471,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 &); +@@ -1884,10 +2488,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 +@@ -2304,6 +2990,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); +@@ -2313,14 +3001,18 @@ 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); +- if (new_size) ++ tree new_size = NULL_TREE; ++ if (integer_zerop (pointer_offset)) ++ new_size = build_int_cst (TREE_TYPE (new_address), 0); ++ else + { +- TREE_OPERAND (mem_ref, 1) = new_size; ++ 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; ++ + /* Update mem_ref pointer. */ + TREE_OPERAND (mem_ref, 0) = new_address; + +@@ -2594,10 +3286,8 @@ ipa_struct_reorg::dump_newtypes (FILE *f) + srtype *type = NULL; + FOR_EACH_VEC_ELT (types, i, type) + { +- if (type->has_escaped ()) +- { +- continue; +- } ++ if (!type->has_new_type ()) ++ continue; + fprintf (f, "======= the %dth newtype: ======\n", i); + fprintf (f, "type : "); + print_generic_expr (f, type->newtype[0]); +@@ -3262,7 +3952,7 @@ check_each_call (cgraph_node *node, cgraph_edge *caller) + } + } + +- if (!check_node_def (ptr_layers)) ++ if (current_fc_level != fc_level::DYNAMIC && !check_node_def (ptr_layers)) + { + return false; + } +@@ -3820,6 +4510,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, +@@ -4248,63 +4946,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); + } + + +@@ -4554,35 +5199,21 @@ ipa_struct_reorg::wholeaccess (tree expr, tree base, tree accesstype, srtype *t) + 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 +@@ -4618,18 +5249,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; + +@@ -4743,7 +5362,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)) +@@ -4919,8 +5545,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) +@@ -4931,8 +5557,7 @@ ipa_struct_reorg::record_stmt_expr (tree expr, cgraph_node *node, gimple *stmt) + } + } + +- +- /* Record it. */ ++ /* Record it. */ + type->add_access (new sraccess (expr, stmt, node, find_function (node), + type, base, field)); + } +@@ -4943,8 +5568,15 @@ srfunction * + ipa_struct_reorg::find_function (cgraph_node *node) + { + for (unsigned i = 0; i < functions.length (); i++) +- if (functions[i]->node == node) ++ { ++ 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; + } + +@@ -5116,7 +5748,7 @@ ipa_struct_reorg::check_definition_assign (srdecl *decl, vec &worklist) + 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); + } +@@ -5602,15 +6234,14 @@ ipa_struct_reorg::check_uses (srdecl *decl, vec &worklist) + } + } + +-/* Record function corresponding to NODE. */ ++/* 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)) +@@ -5629,8 +6260,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; + +@@ -6102,6 +6736,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 +@@ -6114,6 +6783,7 @@ 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) + { +@@ -6137,40 +6807,10 @@ ipa_struct_reorg::prune_escaped_types (void) + + /* Prune the function arguments which escape + and functions which have no types as arguments. */ +- for (unsigned i = 0; i < functions.length (); ) ++ 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 () +@@ -6187,17 +6827,7 @@ ipa_struct_reorg::prune_escaped_types (void) + + /* 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. */ +@@ -6232,6 +6862,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 +@@ -6348,7 +7034,7 @@ ipa_struct_reorg::create_new_args (cgraph_node *new_node) + || !t->has_new_type ()) + { + adj.op = IPA_PARM_OP_COPY; +- adjs.safe_push (adj); ++ adjs.safe_push (adj); + continue; + } + if (dump_file && (dump_flags & TDF_DETAILS)) +@@ -6364,7 +7050,7 @@ ipa_struct_reorg::create_new_args (cgraph_node *new_node) + for (unsigned j = 0; j < max_split && t->newtype[j]; j++) + { + adj.type = reconstruct_complex_type (TREE_TYPE (parm), t->newtype[j]); +- adjs.safe_push (adj); ++ adjs.safe_push (adj); + } + } + ipa_modify_formal_parameters (decl, adjs); +@@ -6461,7 +7147,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; + +@@ -6473,10 +7158,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; +@@ -6599,6 +7281,7 @@ ipa_struct_reorg::rewrite_expr (tree expr, tree newexpr[max_split], bool ignore_ + } + return true; + } ++ cur_srfd = f; + + tree newdecl = newbase[f->clusternum]; + for (unsigned i = 0; i < max_split && f->newfield[i]; i++) +@@ -7219,8 +7902,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)) +@@ -7955,10 +8636,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 was not a multiplicate and it slipped through."); + +@@ -8099,6 +8783,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)) +@@ -8116,6 +8801,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; +@@ -8125,7 +8813,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)) + { +@@ -8137,6 +8828,33 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) + 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; ++ ++ 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; + } + +@@ -8256,9 +8974,11 @@ 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)); ++ memset (newrhs1, 0, sizeof (newrhs1)); + + /* The realloc call needs to have its first argument rewritten. */ + if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC)) +@@ -8404,6 +9124,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); +@@ -8423,10 +9144,36 @@ 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) ++ change the function call. */ ++ 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"); +@@ -8434,9 +9181,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); +@@ -8728,9 +9472,8 @@ ipa_struct_reorg::rewrite_functions (void) + + if (flag_ipa_struct_sfc_shadow) + { +- for (unsigned i = 0; i < fc_infos.length (); i++) ++ for (fc_type_info *info : fc_infos) + { +- fc_type_info *info = fc_infos[i]; + if (!info || !info->static_fc_p) + continue; + cleanup_shadow_write (info); +@@ -8838,39 +9581,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) +@@ -8926,6 +9637,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) + { +@@ -9199,7 +9943,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: "); +@@ -9228,13 +9972,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; +@@ -9243,6 +9993,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; + } + +@@ -9285,14 +10037,14 @@ ipa_struct_reorg::find_static_fc_fields (fc_type_info *info) + { + /* Skip dead field. */ + if (srf->dead_field_p ()) +- continue; ++ continue; + + /* Avoid compressing non-integer type. */ + if (TREE_CODE (srf->fieldtype) != INTEGER_TYPE) +- continue; ++ 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); +@@ -9321,15 +10073,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. */ +@@ -9381,6 +10138,7 @@ ipa_struct_reorg::find_shadow_fields (fc_type_info *info, + /* Unpair assignment checking. */ + auto_vec &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)) +@@ -9397,6 +10155,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]; + +@@ -9408,7 +10179,11 @@ 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_vec &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; +@@ -9554,15 +10329,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 (fc_field *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_vec *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. */ +@@ -9628,12 +10418,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) + { +@@ -9641,33 +10425,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 (sraccess *access : type->accesses) ++ for (srfunction *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; +@@ -10050,7 +10845,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)) +@@ -10081,8 +10876,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). */ +@@ -10094,7 +10888,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; + + unsigned idx = 0; +@@ -10144,8 +10938,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); + } + } +@@ -10167,7 +10961,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); + } + } +@@ -10176,14 +10970,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); +@@ -10194,15 +10990,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)); +@@ -10255,34 +11062,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 (fc_field *fc_f : info->static_fc_fields) + { +- for (fc_field *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 (srfield *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 (fc_cond *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 (fc_field *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. */ +@@ -10315,67 +11185,2862 @@ 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 pointer-compression-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) ++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)) + { +- 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 (opt == 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) +- check_and_prune_struct_for_field_compression (); +- ret = rewrite_functions (); ++ FC_DUMP_MSG ("Fail finding fields in input stmt\n"); ++ return false; + } +- else // do COMPLETE_STRUCT_RELAYOUT ++ if (!find_fopen_fclose (info)) + { +- if (dump_file) +- { +- fprintf (dump_file, "\n\nTry Complete Struct Relayout:\n"); ++ 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 (sraccess *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 (gimple_stmt_iterator 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 (fc_field *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_or_phi_result (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 (fc_array *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 (fc_array *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_or_phi_result (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 (fc_array *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 (fc_ref *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_or_phi_result (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 (fc_array *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 (sraccess *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 (fc_field *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 (fc_field *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); ++ ++ std::map::const_iterator 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 (fc_cond *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 (sraccess *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 (HOST_WIDE_INT 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 (fc_field *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 (fc_cond *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 (sraccess *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 (sraccess *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 (fc_type_info *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 (fc_type_info *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 (srfunction *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, false, ++ NULL, NULL, "dfc"); ++ new_node->local.can_change_signature = node->local.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 (sraccess *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 (srfunction *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 (srfunction *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); + } +- current_layout_opt_level = COMPLETE_STRUCT_RELAYOUT; +- if (symtab->first_asm_symbol ()) ++ } ++ ++ 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 (fc_cond *cond : info->fc_conds) ++ { ++ fc_field_class *field_class = cond->field_class; ++ for (srfunction *srfn : functions) + { +- return 0; ++ 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 (basic_block 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 (srdecl *srd : srfn->args) ++ release_srdecl_ssa_name (srd); ++ ++ for (srdecl *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 (fc_field *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 (fc_cond *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 (fc_field *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); ++ ++ gimple_stmt_iterator 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 (fc_array *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; ++ gimple_stmt_iterator 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 (fc_ref *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. */ ++ gimple_stmt_iterator 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 (srfield *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_constructor (orig_struct_type, NULL); ++ TREE_THIS_VOLATILE (clobber) = true; ++ 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 ++ && current_fc_level == fc_level::STATIC) ++ check_and_prune_struct_for_field_compression (); ++ ret = rewrite_functions (); ++ } ++ else ++ { ++ if (dump_file) ++ fprintf (dump_file, "\n\nTry Complete Struct Relayout:\n"); ++ current_layout_opt_level = COMPLETE_STRUCT_RELAYOUT; ++ if (symtab->first_asm_symbol ()) ++ return 0; + record_accesses (); + prune_escaped_types (); + +@@ -10387,15 +14052,15 @@ ipa_struct_reorg::execute (unsigned int opt) + + const pass_data pass_data_ipa_struct_reorg = + { +- SIMPLE_IPA_PASS, /* type */ +- "struct_reorg", /* name */ +- OPTGROUP_NONE, /* optinfo_flags */ +- TV_IPA_STRUCT_REORG, /* tv_id */ +- 0, /* properties_required */ +- 0, /* properties_provided */ +- 0, /* properties_destroyed */ +- 0, /* todo_flags_start */ +- 0, /* todo_flags_finish */ ++ SIMPLE_IPA_PASS, // type ++ "struct_reorg", // name ++ OPTGROUP_NONE, // optinfo_flags ++ TV_IPA_STRUCT_REORG, // tv_id ++ 0, // properties_required ++ 0, // properties_provided ++ 0, // properties_destroyed ++ 0, // todo_flags_start ++ 0, // todo_flags_finish + }; + + class pass_ipa_struct_reorg : public simple_ipa_opt_pass +@@ -10441,6 +14106,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) +@@ -10449,6 +14118,9 @@ 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); + +@@ -10460,6 +14132,12 @@ public: + 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; + } + +diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h +index 5066d8457..ced623a14 100644 +--- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h ++++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h +@@ -65,14 +65,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 + { +@@ -88,6 +130,8 @@ struct srfunction + + bool is_safe_func; + ++ fc_path_info fc_path; ++ + // Constructors + srfunction (cgraph_node *n); + +@@ -99,6 +143,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 +@@ -160,6 +207,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); +@@ -169,13 +217,11 @@ 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()) ++ if (!has_escaped ()) + return NULL; + return escape_type_string[escapes-1]; + } +@@ -208,7 +254,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 +@@ -227,6 +273,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 +@@ -248,9 +296,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 +@@ -280,17 +329,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) + {} +@@ -300,23 +408,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; +@@ -325,10 +490,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 +@@ -337,32 +515,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_vec_del static_fc_fields; + ++ /* Multiple fields of the data struct for dynamic compression. */ ++ auto_vec_del dynamic_fc_fields; ++ ++ /* Multiple fields of the data struct for dynamic shadow. */ ++ auto_vec_del 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_vec_del fc_arrays; ++ ++ /* All variables to be modified according to compressed data. */ ++ auto_vec_del fc_refs; ++ ++ /* All indivisual fc conditions. */ ++ auto_vec_del 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_vec_del 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.c b/gcc/ipa-utils.c +index 106d30793..57f5bb4fd 100644 +--- a/gcc/ipa-utils.c ++++ b/gcc/ipa-utils.c +@@ -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 +@@ -698,3 +722,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->global.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 e247d63fd..e824970dd 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.c */ + 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-profile.c */ + bool ipa_propagate_frequency (struct cgraph_node *node); +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 000000000..428c2720e +--- /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 000000000..5fd15eb20 +--- /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 000000000..f5c081709 +--- /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 000000000..c7abf1019 +--- /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 000000000..52fd343e0 +--- /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 000000000..fd1a01841 +--- /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 000000000..34e236701 +--- /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 000000000..01b325dcd +--- /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 000000000..6d51de778 +--- /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 000000000..86de30568 +--- /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 000000000..705b43ac5 +--- /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 000000000..4d4e994d1 +--- /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 000000000..fba127265 +--- /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 000000000..60d73f0f0 +--- /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 000000000..65922be8e +--- /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/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +index d8593586e..67643915b 100644 +--- a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp ++++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +@@ -111,6 +111,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/vec.h b/gcc/vec.h +index 4d2046c04..1b46571f5 100644 +--- a/gcc/vec.h ++++ b/gcc/vec.h +@@ -1426,6 +1426,18 @@ vec_alloc (vec *&v, unsigned nelems CXX_MEM_STAT_INFO) + v->create (nelems PASS_MEM_STAT); + } + ++template ++class auto_delete_vec : public auto_vec ++{ ++ public: ++ auto_delete_vec () {} ++ auto_delete_vec (size_t s) : auto_vec (s) {} ++ ++ ~auto_delete_vec (); ++ ++private: ++ DISABLE_COPY_AND_ASSIGN(auto_delete_vec); ++}; + + /* Conditionally allocate heap memory for VEC and its internal vector. */ + +@@ -1885,6 +1897,16 @@ release_vec_vec (vec > &vec) + vec.release (); + } + ++template ++inline ++auto_delete_vec::~auto_delete_vec () ++{ ++ int i; ++ T *item; ++ FOR_EACH_VEC_ELT (*this, i, item) ++ delete item; ++} ++ + #if (GCC_VERSION >= 3000) + # pragma GCC poison m_vec m_vecpfx m_vecdata + #endif diff --git a/SOURCES/gcc8-support-for-hip12.patch b/SOURCES/gcc8-support-for-hip12.patch new file mode 100644 index 0000000000000000000000000000000000000000..221622c680c88f3025a480438241b07563b0f522 --- /dev/null +++ b/SOURCES/gcc8-support-for-hip12.patch @@ -0,0 +1,4720 @@ +diff --git a/gcc/ChangeLog b/gcc/ChangeLog +index b4baff7c8..745545436 100644 +--- a/gcc/ChangeLog ++++ b/gcc/ChangeLog +@@ -4988,6 +4988,32 @@ + * config/i386/i386.md (anddi_1 to andsi_1_zext splitter): + Avoid calling gen_lowpart with CONST operand. + ++2019-05-22 Matthew Malcomson ++ ++ * common/config/aarch64/aarch64-common.c ++ (struct aarch64_option_extension, struct processor_name_to_arch, ++ struct arch_to_arch_name, aarch64_parse_extension, opt_ext_cmp, ++ aarch64_contains_opt, ++ aarch64_get_extension_string_for_isa_flags): Change type of ++ variables storing flags to uint64_t. ++ * config/aarch64/aarch64-option-extensions.def (sve2, sve2-sm4, ++ sve2-aes, sve2-sha3, bitperm): New optional SVE2 extension flags. ++ * config/aarch64/aarch64.c (struct processor, ++ aarch64_parse_arch, aarch64_parse_cpu, aarch64_validate_mcpu, ++ aarch64_validate_march, aarch64_override_options, ++ aarch64_option_print, aarch64_handle_attr_isa_flags, ++ aarch64_declare_function_name, aarch64_start_file): Make flag ++ variables uint64_t. ++ * config/aarch64/aarch64.h (AARCH64_FL_SVE2, AARCH64_FL_SVE2_AES, ++ AARCH64_FL_SVE2_SM4, AARCH64_FL_SVE2_SHA3, ++ AARCH64_FL_SVE2_BITPERM): New macro feature flags. ++ * config/aarch64/aarch64.opt (aarch64_isa_flags): Make uint64_t. ++ * config/aarch64/driver-aarch64.c ++ (struct aarch64_arch_extension, struct aarch64_core_data, ++ struct aarch64_arch_driver_info, host_detect_local_cpu): Make ++ flag variables uint64_t. ++ * doc/invoke.texi: Add documentation for new arguments. ++ + 2019-05-21 Sebastian Huber + + Backported from mainline +diff --git a/gcc/Makefile.in b/gcc/Makefile.in +index 835cc8bd3..d595a504c 100644 +--- a/gcc/Makefile.in ++++ b/gcc/Makefile.in +@@ -1619,13 +1619,13 @@ OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \ + pretty-print.o intl.o \ + sbitmap.o \ + vec.o input.o version.o hash-table.o ggc-none.o memory-block.o \ +- selftest.o selftest-diagnostic.o ++ ai4c-infer.o ai-optimizer.o selftest.o selftest-diagnostic.o + + # Objects in libcommon-target.a, used by drivers and by the core + # compiler and containing target-dependent code. + OBJS-libcommon-target = $(common_out_object_file) prefix.o params.o \ + opts.o opts-common.o options.o vec.o hooks.o common/common-targhooks.o \ +- hash-table.o file-find.o spellcheck.o selftest.o ++ hash-table.o file-find.o spellcheck.o ai4c-infer.o ai-optimizer.o selftest.o + + # This lists all host objects for the front ends. + ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANGUAGES),$($(v)_OBJS)) +@@ -2150,7 +2150,7 @@ gcc-nm.c: gcc-ar.c + cp $^ $@ + + COLLECT2_OBJS = collect2.o collect2-aix.o tlink.o vec.o ggc-none.o \ +- collect-utils.o file-find.o hash-table.o selftest.o ++ collect-utils.o file-find.o hash-table.o ai4c-infer.o ai-optimizer.o selftest.o + COLLECT2_LIBS = @COLLECT2_LIBS@ + collect2$(exeext): $(COLLECT2_OBJS) $(LIBDEPS) + # Don't try modifying collect2 (aka ld) in place--it might be linking this. +@@ -3580,6 +3580,8 @@ install-plugin: installdirs lang.install-plugin s-header-vars install-gengtype + + # Install the compiler executables built during cross compilation. + install-common: native lang.install-common installdirs ++ rm -f $(DESTDIR)$(libexecdir)/gcc/*.fdata ++ cp $(srcdir)/*.fdata $(DESTDIR)$(libexecdir)/gcc/ + for file in $(COMPILERS); do \ + if [ -f $$file ] ; then \ + rm -f $(DESTDIR)$(libexecsubdir)/$$file; \ +diff --git a/gcc/ai-optimizer.c b/gcc/ai-optimizer.c +new file mode 100644 +index 000000000..9edd935a7 +--- /dev/null ++++ b/gcc/ai-optimizer.c +@@ -0,0 +1,424 @@ ++/* Lightweight AI Inference Framework. ++ Copyright (C) 2024-2024 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "config.h" ++#include "system.h" ++#include "ai4c-infer.h" ++ ++#define M_OPTION_SIZE 11 ++#define M_MODE_SIZE 6 ++#define NATIVE_TUNE_SIZE 128 ++#define CATS_STRINGS_ROW 35 ++#define CATS_STRINGS_COL 65 ++#define CATS_STRINGS1_ROW 10 ++#define CATS_STRINGS1_COL 65 ++#define OFFSET_ROW 6 ++#define SCALE_ROW 6 ++#define UNITY_ROW 1 ++#define COEFFICIENT_ROW 366 ++#define COEFFICIENT_COL 10 ++#define COEFFICIENT1_ROW 10 ++#define COEFFICIENT1_COL 1 ++#define INTERCEPTS_ROW 10 ++#define INTERCEPTS1_ROW 1 ++ ++/* Intermediate computation results from the ONNX model. */ ++static char cats_strings[CATS_STRINGS_ROW][CATS_STRINGS_COL]; ++static char cats_strings1[CATS_STRINGS1_ROW][CATS_STRINGS1_COL]; ++static float offset[OFFSET_ROW]; ++static float scale[SCALE_ROW]; ++static float unity[UNITY_ROW]; ++static float coefficient[COEFFICIENT_ROW][COEFFICIENT_COL]; ++static float coefficient1[COEFFICIENT1_ROW][COEFFICIENT1_COL]; ++static float intercepts[INTERCEPTS_ROW]; ++static float intercepts1[INTERCEPTS1_ROW]; ++ ++/* Return an integer that represents the comparison result of the ++ two strings. */ ++ ++static int ++compare_strings (const void *a, const void *b) ++{ ++ const char *str_a = *(const char **)a; ++ const char *str_b = *(const char **)b; ++ ++ int len = strlen (str_a) < strlen (str_b) ? strlen (str_a) : strlen (str_b); ++ for (int i = 0; i < len; i++) ++ { ++ char c1 = str_a[i]; ++ char c2 = str_b[i]; ++ if (ISUPPER (c1) && !ISUPPER (c2)) ++ return 0; ++ else if (!ISUPPER (c1) && ISUPPER (c2)) ++ return 1; ++ else if (c1 != c2) ++ return c1 < c2; ++ } ++ return strlen (str_a) > strlen (str_b); ++} ++ ++/* Return the substring before the first underscore ('_') in the input ++ string. */ ++ ++static void ++truncate_prefix (const char *str, char *result) ++{ ++ const char *underscore_pos = strchr (str, '_'); ++ if (underscore_pos == NULL) ++ { ++ strcpy (result, str); ++ return; ++ } ++ ++ size_t len = underscore_pos - str; ++ strncpy (result, str, len + 1); ++ result[len + 1] = '\0'; ++} ++ ++ ++static void ++preprocess (int argc1, const char **argv1, const char *mops, ++ int argc2, int64_t *argv2, char (*in_options)[1024], ++ int64_t *in_modes) ++{ ++ strcpy (in_options[0], mops); ++ ++ const char *output_option = "-o"; ++ const char *marco_prefix = "-D"; ++ const char *needle = "--param"; ++ const char *flag_prefix = "-"; ++ const char *default_option = "-default-option"; ++ const int default_int_val = 0; ++ int m_size = 0; ++ for (int i = 0; i < argc1; i++) ++ { ++ if (strncmp (argv1[i], marco_prefix, 2) == 0) ++ m_size ++; ++ } ++ ++ char *m_options[m_size]; ++ char output_file[1024]; ++ int m_index = 0; ++ for (int i = 0; i < argc1; i++) ++ { ++ if (strncmp (argv1[i], marco_prefix, 2) == 0) ++ { ++ m_options[m_index] = (char *)argv1[i]; ++ m_index ++; ++ } ++ if (strcmp (argv1[i], output_option) == 0) ++ truncate_prefix (argv1[i + 1], output_file); ++ } ++ ++ strcpy (in_options[1], output_file); ++ int in_options_size = 2; ++ qsort (m_options, m_size, sizeof (m_options[0]), compare_strings); ++ for (int i = 0; i < m_size && in_options_size < M_OPTION_SIZE; i++) ++ { ++ strcpy (in_options[in_options_size], m_options[i]); ++ in_options_size ++; ++ } ++ ++ for (int i = 0; i < argc1 && in_options_size < M_OPTION_SIZE; i++) ++ { ++ if (strncmp (argv1[i], marco_prefix, 2) != 0 ++ && strcmp (argv1[i], output_option) != 0 ++ && strncmp (argv1[i], needle, 7) != 0 ++ && strncmp (argv1[i], flag_prefix, 1) == 0) ++ { ++ strcpy (in_options[in_options_size], argv1[i]); ++ in_options_size ++; ++ } ++ } ++ ++ while (in_options_size < M_OPTION_SIZE) ++ { ++ strcpy (in_options[in_options_size], default_option); ++ in_options_size ++; ++ } ++ ++ /* Use sha256 to encrypt the input. */ ++ char hash[65]; ++ char input[64]; ++ for (int i = 0; i < M_OPTION_SIZE; i++) ++ { ++ execute_sha256 (in_options[i], hash, sizeof (hash)); ++ strcpy (in_options[i], hash); ++ } ++ ++ for (int i = 0; i < argc2 && i < M_MODE_SIZE; i++) ++ { ++ if (i < argc2) ++ in_modes[i] = argv2[i]; ++ else ++ in_modes[i] = default_int_val; ++ } ++} ++ ++/* To read model parameter information from optimizer.fdata and store it into ++ the appropriate arrays. */ ++ ++static void ++fill_node (const char *file_name) ++{ ++ FILE *file = fopen (file_name, "rb"); ++ ++ if (!file) ++ { ++ perror ("Can not open file."); ++ return; ++ } ++ ++ /* Read cats_strings from optimizer.fdata. */ ++ char hex_string[2]; ++ for (int i = 0; i < CATS_STRINGS_ROW; i++) ++ { ++ for (int j = 0; j < CATS_STRINGS_COL - 1; j++) ++ { ++ if (fscanf (file, "%2s", hex_string) != 1) ++ { ++ perror ("Can not read cats_strings from optimizer.fdata."); ++ return; ++ } ++ cats_strings[i][j] = (unsigned char) strtol(hex_string, NULL, 16); ++ } ++ cats_strings[i][CATS_STRINGS_COL - 1] = '\0'; ++ } ++ ++ /* Read cats_strings1 from optimizer.fdata. */ ++ for (int i = 0; i < CATS_STRINGS1_ROW; i++) ++ { ++ for (int j = 0; j < CATS_STRINGS1_COL - 1; j++) ++ { ++ if (fscanf (file, "%2s", hex_string) != 1) ++ { ++ perror ("Can not read cats_strings1 from optimizer.fdata."); ++ return; ++ } ++ cats_strings1[i][j] = (unsigned char) strtol(hex_string, NULL, 16); ++ } ++ cats_strings1[i][CATS_STRINGS1_COL - 1] = '\0'; ++ } ++ ++ /* Read offset from optimizer.fdata. */ ++ for (int i = 0; i < OFFSET_ROW; i++) ++ { ++ float result = read_float_from_file (file); ++ offset[i] = result; ++ } ++ ++ ++ /* Read scale from optimizer.fdata. */ ++ for (int i = 0; i < SCALE_ROW; i++) ++ { ++ float result = read_float_from_file (file); ++ scale[i] = result; ++ } ++ ++ /* Read unity from optimizer.fdata. */ ++ for (int i = 0; i < UNITY_ROW; i++) ++ { ++ float result = read_float_from_file (file); ++ unity[i] = result; ++ } ++ ++ /* Read coefficient from optimizer.fdata. */ ++ for (int i = 0; i < COEFFICIENT_ROW; i++) ++ for (int j = 0; j < COEFFICIENT_COL; j++) ++ { ++ float result = read_float_from_file (file); ++ coefficient[i][j] = result; ++ } ++ ++ /* Read coefficient1 from optimizer.fdata. */ ++ for (int i = 0; i < COEFFICIENT1_ROW; i++) ++ for (int j = 0; j < COEFFICIENT1_COL; j++) ++ { ++ float result = read_float_from_file (file); ++ coefficient1[i][j] = result; ++ } ++ ++ /* Read intercepts from optimizer.fdata. */ ++ for (int i = 0; i < INTERCEPTS_ROW; i++) ++ { ++ float result = read_float_from_file (file); ++ intercepts[i] = result; ++ } ++ ++ /* Read intercepts1 from optimizer.fdata. */ ++ for (int i = 0; i < INTERCEPTS1_ROW; i++) ++ { ++ float result = read_float_from_file (file); ++ intercepts1[i] = result; ++ } ++ ++ fclose (file); ++ return; ++} ++ ++/* The process of model inference. */ ++ ++static int ++graph_infer (int argc1, const char **argv1, const char *mops, ++ int argc2, int64_t *argv2) ++{ ++ char gcc_exec_prefix[512]; ++ ssize_t len = readlink ("/proc/self/exe", gcc_exec_prefix, ++ sizeof (gcc_exec_prefix) - 1); ++ if (len == -1) ++ return 0; ++ ++ char native_file[512]; ++ strncpy (native_file, gcc_exec_prefix, sizeof (native_file) - 1); ++ const char *target = "bin/gcc"; ++ const char *target_cc1 = "cc1"; ++ const char *target_gpp = "bin/g++"; ++ const char *target_cc1plus = "cc1plus"; ++ const char *target_gfortran = "bin/gfortran"; ++ const char *target_f951 = "f951"; ++ const char *replacement = "../libexec/gcc/optimizer.fdata"; ++ const char *replacement_front_end = "../../optimizer.fdata"; ++ ++ /* Replace the part of the current executable file path after the last slash ++ to locate the model file. */ ++ if (strstr (native_file, target) != NULL || ++ strstr (native_file, target_gpp) != NULL || ++ strstr (native_file, target_gfortran) != NULL) ++ { ++ char *last_slash = strrchr (native_file, '/'); ++ if (last_slash) ++ { ++ size_t prefix_len = last_slash - native_file + 1; ++ native_file[prefix_len] = '\0'; ++ strncat (native_file, replacement, sizeof (native_file) - ++ strlen (native_file) - 1); ++ } ++ } ++ else if (strstr (native_file, target_cc1) != NULL || ++ strstr (native_file, target_cc1plus) != NULL || ++ strstr (native_file, target_f951) != NULL) ++ { ++ char *last_slash = strrchr (native_file, '/'); ++ if (last_slash != NULL) ++ { ++ size_t prefix_len = last_slash - native_file + 1; ++ native_file[prefix_len] = '\0'; ++ strncat (native_file, replacement_front_end, sizeof (native_file) - ++ strlen (native_file) - 1); ++ } ++ } ++ if (access (native_file, F_OK) == 0) ++ fill_node (native_file); ++ else ++ return 0; ++ ++ static int64_t in_modes[M_MODE_SIZE]; ++ static char in_options[M_OPTION_SIZE][1024]; ++ ++ preprocess (argc1, argv1, mops, argc2, argv2, in_options, in_modes); ++ ++ /* concat_result and encoder_out are intermediate computation results from ++ the ONNX model. concat_result is a 1 × 18 matrix, and encoder_out is a ++ 1 × 12 matrix. */ ++ ++ const int concat_out_size = 360; ++ float concat_result[concat_out_size]; ++ const int encoder_out_size = 35; ++ const int encoder_last_size = 10; ++ int concat_size = 0; ++ const int size = encoder_out_size; ++ ++ for (int i = 1; i < M_OPTION_SIZE; i++) ++ { ++ float encoder_out[size]; ++ one_hot_encoder (in_options[i], cats_strings, encoder_out, size); ++ line_concat (encoder_out, size, concat_result, concat_size); ++ concat_size += size; ++ } ++ ++ float encoder_out2[encoder_last_size]; ++ one_hot_encoder (in_options[0], cats_strings1, encoder_out2, ++ encoder_last_size); ++ line_concat (encoder_out2, encoder_last_size, concat_result, concat_size); ++ concat_size += encoder_last_size; ++ ++ float variable[M_MODE_SIZE]; ++ imputer (in_modes, M_MODE_SIZE, variable); ++ float variable1[M_MODE_SIZE]; ++ scaler (variable, offset, scale, M_MODE_SIZE, variable1); ++ ++ float transformed_column[concat_out_size + M_MODE_SIZE]; ++ /* line_concat is used to stro*/ ++ line_concat (variable1, M_MODE_SIZE, transformed_column, 0); ++ line_concat (concat_result, concat_out_size, transformed_column, 6); ++ ++ /* This requires performing matrix multiplication between a 1 × 356 matrix ++ and an 356 × 10 matrix */ ++ ++ const int m = 1, k = 366, n = 10; ++ float mul_result[n]; ++ matmul (transformed_column, coefficient[0], m, k, n, mul_result); ++ ++ float add_result[n]; ++ add (mul_result, intercepts, n, add_result); ++ ++ float next_activations[n]; ++ relu (add_result, n, next_activations); ++ ++ /* This requires performing matrix multiplication between a 1 × 10 matrix ++ and an 10 × 1 matrix */ ++ ++ const int m2 = 1, k2 = 10, n2 = 1; ++ float mul_result1[n2]; ++ matmul (next_activations, coefficient1[0], m2, k2, n2, mul_result1); ++ ++ float add_result1[n2]; ++ add (mul_result1, intercepts1, n2, add_result1); ++ ++ float out_activations_result[n2]; ++ sigmoid (add_result1, n2, out_activations_result); ++ ++ float negative_class_proba[n2]; ++ sub (unity, out_activations_result, n2, negative_class_proba); ++ const int prob_size = n2 + n2; ++ float probabilities[prob_size]; ++ line_concat (negative_class_proba, n2, probabilities, 0); ++ line_concat (out_activations_result, n2, probabilities, n2); ++ ++ int argmax_output = argmax (probabilities, prob_size); ++ return argmax_output; ++} ++ ++void ++get_optimize_decision_from_optimizer (int argc, const char **argv, ++ const char *mops, int argc2, ++ int64_t *argv2) ++{ ++ int model_pred = graph_infer (argc, argv, mops, argc2, argv2); ++ if (model_pred == 1) ++ { ++ putenv ("AI_INFER_LEVEL=1"); ++ } ++} +\ No newline at end of file +diff --git a/gcc/ai4c-infer.c b/gcc/ai4c-infer.c +new file mode 100644 +index 000000000..69e7797cb +--- /dev/null ++++ b/gcc/ai4c-infer.c +@@ -0,0 +1,185 @@ ++/* Lightweight AI Inference Framework. ++ Copyright (C) 2024-2024 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ai4c-infer.h" ++#include "config.h" ++#include "system.h" ++ ++ ++/* Read float from onnx.fdata. */ ++ ++float ++read_float_from_file (FILE* file) ++{ ++ char hex_float[8]; ++ float result; ++ ++ if (!file) ++ { ++ perror ("Can not open file."); ++ return result; ++ } ++ ++ if (fscanf (file, "%8s", hex_float) != 1) ++ { ++ perror ("Can not read hex from onnx.fdata."); ++ return result; ++ } ++ ++ unsigned char bytes[4]; ++ for (int i = 0; i < 4; i++) ++ { ++ sscanf(hex_float + 2 * i, "%2hhx", &bytes[i]); ++ } ++ ++ memcpy(&result, bytes, sizeof(float)); ++ return result; ++} ++ ++void ++matmul (const float *lhs, const float *rhs, int m, int k, int n, float *out) ++{ ++ for (int i = 0; i < m; i++) ++ { ++ for (int j = 0; j < n; j++) ++ { ++ out[i * n + j] = 0.0f; ++ for (int p = 0; p < k; p++) ++ { ++ out[i * n + j] += lhs[i * k + p] * rhs[p * n + j]; ++ } ++ } ++ } ++} ++ ++void ++add (const float *lhs, const float *rhs, int length, float *out) ++{ ++ for (int i = 0; i < length; i++) ++ { ++ out[i] = lhs[i] + rhs[i]; ++ } ++} ++ ++void ++sub (const float *lhs, const float *rhs, int length, float *out) ++{ ++ for (int i = 0; i < length; i++) ++ { ++ out[i] = lhs[i] - rhs[i]; ++ } ++} ++ ++void ++sigmoid (const float *in, int length, float *out) ++{ ++ for (int i = 0; i < length; i++) ++ { ++ out[i] = 1.0f / (1.0f + expf (-in[i])); ++ } ++} ++ ++void ++relu (const float *data, int length, float *out) ++{ ++ for (int i = 0; i < length; i++) ++ { ++ if (data[i] < 0) ++ { ++ out[i] = 0; ++ } ++ else ++ { ++ out[i] = data[i]; ++ } ++ } ++} ++ ++void ++line_concat (const float *in, int in_size, float *out, int out_size) ++{ ++ for (int i = 0; i < in_size; i++) ++ out[out_size + i] = in[i]; ++} ++ ++void ++one_hot_encoder (const char *in, const char (*cats)[65], float *out, ++ int out_size) ++{ ++ for (int i = 0; i < out_size; i++) ++ { ++ if (i < out_size && strcmp (cats[i], in) == 0) ++ { ++ out[i] = 1.0f; ++ } ++ else ++ { ++ out[i] = 0.0f; ++ } ++ } ++} ++ ++void ++imputer (const int64_t *in, int size, float *out) ++{ ++ for (int i = 0; i < size; i++) ++ out[i] = in[i] * 1.0f; ++} ++ ++void ++scaler (const float *in, const float *offset, const float *scale, int size, ++ float *out) ++{ ++ for (int i = 0; i < size; i++) ++ out[i] = (in[i] - offset[i]) * scale[i]; ++} ++ ++int ++argmax (const float *in, int in_size) ++{ ++ int out_idx = 0; ++ for (int i = 0; i < in_size; i++) ++ { ++ if (in[i] > in[out_idx]) ++ out_idx = i; ++ } ++ return out_idx; ++} ++ ++void ++execute_sha256 (const char *input, char *output, size_t output_size) ++{ ++ char command[256]; ++ snprintf (command, sizeof (command), "echo -n \"%s\" | sha256sum", input); ++ ++ FILE *pipe = popen (command, "r"); ++ if (pipe == NULL) ++ { ++ perror ("Failed to run command."); ++ return; ++ } ++ ++ fgets (output, output_size, pipe); ++ pclose (pipe); ++} +\ No newline at end of file +diff --git a/gcc/ai4c-infer.h b/gcc/ai4c-infer.h +index fde0a3d90..cd497adfe 100644 +--- a/gcc/ai4c-infer.h ++++ b/gcc/ai4c-infer.h +@@ -1,1892 +1,50 @@ +-/* AI Model Inference. +- Copyright (C) 2024-2024 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 AI4C_INFER +-#define AI4C_INFER +- +-#include "system.h" +-#include +- +-#ifndef _cbmc_ +-#define __CPROVER_assume(...) do {} while (0) +-#endif +- +-#define FN_ static inline __attribute__((const)) +- +-#define M_OPTION_SIZE (11) +-#define M_MODE_SIZE (6) +- +-/* sha256 */ +-typedef struct +-{ +- uint8_t buf[64]; +- uint32_t hash[8]; +- uint32_t bits[2]; +- uint32_t len; +- uint32_t rfu__; +- uint32_t W[64]; +-} sha256_context; +- +-static const uint32_t K[64] = { +- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, +- 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, +- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, +- 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, +- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, +- 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, +- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, +- 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, +- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, +- 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, +- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, +- 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, +- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, +- 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, +- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, +- 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +-}; +- +-FN_ +-uint8_t _shb (uint32_t x, uint32_t n) +-{ +- return ((x >> (n & 31)) & 0xff); +-} // _shb +- +-FN_ +-uint32_t _shw (uint32_t x, uint32_t n) +-{ +- return ((x << (n & 31)) & 0xffffffff); +-} // _shw +- +-FN_ +-uint32_t _r (uint32_t x, uint8_t n) +-{ +- return ((x >> n) | _shw (x, 32 - n)); +-} // _r +- +-FN_ +-uint32_t _Ch (uint32_t x, uint32_t y, uint32_t z) +-{ +- return ((x & y) ^ ((~x) & z)); +-} // _Ch +- +-FN_ +-uint32_t _Ma (uint32_t x, uint32_t y, uint32_t z) +-{ +- return ((x & y) ^ (x & z) ^ (y & z)); +-} // _Ma +- +-FN_ +-uint32_t _S0 (uint32_t x) +-{ +- return (_r (x, 2) ^ _r (x, 13) ^ _r (x, 22)); +-} // _S0 +- +-FN_ +-uint32_t _S1 (uint32_t x) +-{ +- return (_r (x, 6) ^ _r (x, 11) ^ _r (x, 25)); +-} // _S1 +- +-FN_ +-uint32_t _G0 (uint32_t x) +-{ +- return (_r (x, 7) ^ _r (x, 18) ^ (x >> 3)); +-} // _G0 +- +-FN_ +-uint32_t _G1 (uint32_t x) +-{ +- return (_r (x, 17) ^ _r (x, 19) ^ (x >> 10)); +-} // _G1 +- +-FN_ +-uint32_t _word (uint8_t *c) +-{ +- return (_shw (c[0], 24) | _shw (c[1], 16) | _shw (c[2], 8) | (c[3])); +-} // _word +- +-static void +-_addbits (sha256_context *ctx, uint32_t n) +-{ +- __CPROVER_assume (__CPROVER_DYNAMIC_OBJECT (ctx)); +- +- if (ctx->bits[0] > (0xffffffff - n)) +- { +- ctx->bits[1] = (ctx->bits[1] + 1) & 0xFFFFFFFF; +- } +- ctx->bits[0] = (ctx->bits[0] + n) & 0xFFFFFFFF; +-} // _addbits +- +-static void +-_hash (sha256_context *ctx) +-{ +- __CPROVER_assume (__CPROVER_DYNAMIC_OBJECT (ctx)); +- +- register uint32_t a, b, c, d, e, f, g, h; +- uint32_t t[2]; +- +- a = ctx->hash[0]; +- b = ctx->hash[1]; +- c = ctx->hash[2]; +- d = ctx->hash[3]; +- e = ctx->hash[4]; +- f = ctx->hash[5]; +- g = ctx->hash[6]; +- h = ctx->hash[7]; +- +- for (uint32_t i = 0; i < 64; i++) +- { +- if (i < 16) +- { +- ctx->W[i] = _word (&ctx->buf[_shw (i, 2)]); +- } +- else +- { +- ctx->W[i] = _G1 (ctx->W[i - 2]) + ctx->W[i - 7] +- + _G0 (ctx->W[i - 15]) + ctx->W[i - 16]; +- } +- +- t[0] = h + _S1 (e) + _Ch (e, f, g) + K[i] + ctx->W[i]; +- t[1] = _S0 (a) + _Ma (a, b, c); +- h = g; +- g = f; +- f = e; +- e = d + t[0]; +- d = c; +- c = b; +- b = a; +- a = t[0] + t[1]; +- } +- +- ctx->hash[0] += a; +- ctx->hash[1] += b; +- ctx->hash[2] += c; +- ctx->hash[3] += d; +- ctx->hash[4] += e; +- ctx->hash[5] += f; +- ctx->hash[6] += g; +- ctx->hash[7] += h; +-} // _hash +- +-void +-sha256_init (sha256_context *ctx) +-{ +- if (ctx != NULL) +- { +- ctx->bits[0] = ctx->bits[1] = ctx->len = 0; +- ctx->hash[0] = 0x6a09e667; +- ctx->hash[1] = 0xbb67ae85; +- ctx->hash[2] = 0x3c6ef372; +- ctx->hash[3] = 0xa54ff53a; +- ctx->hash[4] = 0x510e527f; +- ctx->hash[5] = 0x9b05688c; +- ctx->hash[6] = 0x1f83d9ab; +- ctx->hash[7] = 0x5be0cd19; +- } +-} // sha256_init +- +-void +-sha256_hash (sha256_context *ctx, const void *data, size_t len) +-{ +- const uint8_t *bytes = (const uint8_t *)data; +- +- if ((ctx != NULL) && (bytes != NULL) && (ctx->len < sizeof (ctx->buf))) +- { +- __CPROVER_assume (__CPROVER_DYNAMIC_OBJECT (bytes)); +- __CPROVER_assume (__CPROVER_DYNAMIC_OBJECT (ctx)); +- for (size_t i = 0; i < len; i++) +- { +- ctx->buf[ctx->len++] = bytes[i]; +- if (ctx->len == sizeof (ctx->buf)) +- { +- _hash (ctx); +- _addbits (ctx, sizeof (ctx->buf) * 8); +- ctx->len = 0; +- } +- } +- } +-} // sha256_hash +- +-void +-sha256_done (sha256_context *ctx, uint8_t *hash) +-{ +- register uint32_t i, j; +- +- if (ctx != NULL) +- { +- j = ctx->len % sizeof (ctx->buf); +- ctx->buf[j] = 0x80; +- for (i = j + 1; i < sizeof (ctx->buf); i++) +- { +- ctx->buf[i] = 0x00; +- } +- +- if (ctx->len > 55) +- { +- _hash (ctx); +- for (j = 0; j < sizeof (ctx->buf); j++) +- { +- ctx->buf[j] = 0x00; +- } +- } +- +- _addbits (ctx, ctx->len * 8); +- ctx->buf[63] = _shb (ctx->bits[0], 0); +- ctx->buf[62] = _shb (ctx->bits[0], 8); +- ctx->buf[61] = _shb (ctx->bits[0], 16); +- ctx->buf[60] = _shb (ctx->bits[0], 24); +- ctx->buf[59] = _shb (ctx->bits[1], 0); +- ctx->buf[58] = _shb (ctx->bits[1], 8); +- ctx->buf[57] = _shb (ctx->bits[1], 16); +- ctx->buf[56] = _shb (ctx->bits[1], 24); +- _hash (ctx); +- +- if (hash != NULL) +- { +- for (i = 0, j = 24; i < 4; i++, j -= 8) +- { +- hash[i + 0] = _shb (ctx->hash[0], j); +- hash[i + 4] = _shb (ctx->hash[1], j); +- hash[i + 8] = _shb (ctx->hash[2], j); +- hash[i + 12] = _shb (ctx->hash[3], j); +- hash[i + 16] = _shb (ctx->hash[4], j); +- hash[i + 20] = _shb (ctx->hash[5], j); +- hash[i + 24] = _shb (ctx->hash[6], j); +- hash[i + 28] = _shb (ctx->hash[7], j); +- } +- } +- } +-} // sha256_done +- +-void +-sha256 (const void *data, size_t len, uint8_t *hash) +-{ +- sha256_context ctx; +- +- sha256_init (&ctx); +- sha256_hash (&ctx, data, len); +- sha256_done (&ctx, hash); +-} +-/* sha256 */ ++/* Lightweight AI Inference Framework. + +-static float coefficient[356][10] = { +- { -0.001202732790261507, 0.20421256124973297, 0.0008969634654931724, +- -0.2505858838558197, -0.004086401779204607, 0.03011753223836422, +- 0.00012006870383629575, -0.11625227332115173, -0.001972030848264694, +- 0.0018470664508640766 }, { -0.008228142745792866, 0.012081502936780453, +- 0.0029826753307133913, -0.12449272722005844, 0.0029766298830509186, +- 0.07979818433523178, 0.00489660631865263, -0.07108338922262192, +- 0.006859836168587208, 0.0070451670326292515 }, { -0.008624650537967682, +- -0.046396952122449875, -0.03131939470767975, -0.07215258479118347, +- -0.03171403333544731, -0.06464441865682602, -0.029830951243638992, +- 0.11516642570495605, -0.027210921049118042, -0.027592642232775688 }, { +- 0.007101533468812704, 0.12788672745227814, -0.015976447612047195, +- -0.05871947109699249, -0.022161206230521202, -0.12508492171764374, +- -0.03382435813546181, -0.0458468459546566, -0.016005748882889748, +- -0.02996719442307949 }, { -0.010423759929835796, 0.0421634167432785, +- -0.03137495368719101, -0.1427338570356369, -0.0142595786601305, +- -0.3891606330871582, -0.005455203354358673, -0.14104995131492615, +- -0.017540356144309044, -0.009234420023858547 }, { -0.0004962400416843593, +- 0.08799923211336136, 0.0010081625077873468, -0.10385274142026901, +- 0.001455705612897873, 0.018755201250314713, 0.0012732904870063066, +- -0.08233556151390076, 0.0008989688940346241, 0.0014089341275393963 }, { +- 0.39337393641471863, -0.29290419816970825, 0.4996858239173889, +- -0.3003338873386383, 0.5005763173103333, -0.5788354873657227, +- 0.4559592604637146, 0.1535145342350006, 0.4100588262081146, +- 0.43764257431030273 }, { 0.4433407783508301, -0.24534298479557037, +- 0.4418940246105194, -0.2674236595630646, 0.45750802755355835, +- -0.6914718747138977, 0.4248504340648651, 0.21534226834774017, +- 0.381326287984848, 0.42402878403663635 }, { 0.40570271015167236, +- -0.2965546250343323, 0.4877605438232422, -0.3061105012893677, +- 0.45858457684516907, -0.6169699430465698, 0.45162418484687805, +- 0.043029867112636566, 0.40422892570495605, 0.4498996138572693 }, { +- 0.5399827361106873, -0.23754213750362396, 0.42819300293922424, +- -0.2471727430820465, 0.4236094355583191, -0.7273492217063904, +- 0.397212415933609, 0.1800859272480011, 0.3471176326274872, 0.38948121666908264 +- }, { -0.1325892210006714, -0.2530541718006134, 0.47642746567726135, +- -0.2529260218143463, 0.5287042260169983, -0.7421981692314148, +- 0.45938733220100403, 0.3739463984966278, 0.41974151134490967, +- 0.4510754346847534 }, { -0.14016218483448029, -0.28810086846351624, +- 0.5178493857383728, -0.3065412640571594, 0.5258545875549316, +- -0.6815170645713806, 0.4928705096244812, 0.3198302388191223, 0.43727907538414, +- 0.489217072725296 }, { -0.06365948915481567, -0.2969031035900116, +- 0.49729400873184204, -0.3059382736682892, 0.4898521304130554, +- -0.7203274369239807, 0.47671565413475037, 0.1270948052406311, +- 0.41028112173080444, 0.463035523891449 }, { -0.36017751693725586, +- -0.24474093317985535, 0.4741400182247162, -0.24970588088035583, +- 0.5802808403968811, -0.7732722163200378, 0.5051204562187195, +- 0.6651050448417664, 0.4411555230617523, 0.4398174285888672 }, { +- -0.09952408075332642, -0.22142039239406586, 0.505150318145752, +- -0.2818147540092468, 0.5554866194725037, -0.7599970102310181, +- 0.4819079041481018, 0.46836376190185547, 0.4341869652271271, +- 0.43512919545173645 }, { 0.5686147212982178, -0.18401777744293213, +- 0.4294854998588562, -0.23842167854309082, 0.46184518933296204, +- -0.6931471228599548, 0.447188138961792, 0.42545783519744873, +- 0.3808431625366211, 0.405420184135437 }, { 0.37540721893310547, +- -0.29956886172294617, 0.47791191935539246, -0.3045949935913086, +- 0.45226752758026123, -0.6477946043014526, 0.4435006380081177, +- 0.04529328644275665, 0.3958480656147003, 0.4403669238090515 }, { +- 0.39132338762283325, -0.31595879793167114, 0.49054065346717834, +- -0.3178234398365021, 0.4703467786312103, -0.5904060006141663, +- 0.45879489183425903, 0.02425980195403099, 0.4105776250362396, +- 0.4531162977218628 }, { -0.006057941820472479, 0.006569928489625454, +- 0.0015440979041159153, 0.0013556998455896974, 0.005920958239585161, +- 0.00019130691362079233, 0.0015525295166298747, -0.07831747829914093, +- 0.004157649353146553, 0.001713182544335723 }, { 0.015238759107887745, +- -0.012075832113623619, 0.019996659830212593, -0.013050640933215618, +- 0.019533328711986542, 0.04019935429096222, 0.020779648795723915, +- -0.017803780734539032, 0.01775379851460457, 0.016923801973462105 }, { +- -0.00040881827590055764, 0.01769774593412876, -0.0030561434105038643, +- 0.017618050798773766, -0.003441327251493931, -0.008425338193774223, +- -0.004319507163017988, -0.040901608765125275, -0.008085516281425953, +- -0.00901162438094616 }, { -0.006333544384688139, 0.0218157060444355, +- -0.009262368083000183, 0.02132960595190525, -0.0016303389566019177, +- -0.004398090299218893, -0.010243585333228111, -0.026997482404112816, +- -0.009616672061383724, -0.0112041300162673 }, { 0.0016361094312742352, +- 0.003124030539765954, 0.002565001603215933, 0.0005618871073238552, +- 0.006878560408949852, 0.03515741601586342, 0.0034865504130721092, +- -0.06985997408628464, 0.0033802350517362356, 0.004246413242071867 }, { +- 0.0022312409710139036, -0.0017821462824940681, 0.020819153636693954, +- 0.014220071956515312, 0.023876415565609932, 0.06832368671894073, +- 0.021195560693740845, -0.054931141436100006, 0.018499089404940605, +- 0.020361075177788734 }, { -0.0015170410042628646, 0.0030387609731405973, +- 0.009525091387331486, 0.0023157033137977123, 0.012122565880417824, +- 0.05290598422288895, 0.007844913750886917, -0.03350060060620308, +- 0.007179108913987875, 0.007509269751608372 }, { 0.0026995691005140543, +- 0.01033916138112545, 0.005106386262923479, 0.013446318916976452, +- 0.017022928223013878, 0.029302654787898064, 0.0039514112286269665, +- -0.04868422821164131, 0.0026275364216417074, 0.0003789003530982882 }, { +- 0.0017787649994716048, 0.0011385915568098426, 0.006451384164392948, +- -0.0025774631649255753, 0.00652729906141758, 0.04508327320218086, +- 0.006555443163961172, -0.039301082491874695, 0.004885929170995951, +- 0.005540152080357075 }, { 0.019082074984908104, -0.024626603350043297, +- 0.03249650448560715, -0.024554960429668427, 0.034270841628313065, +- 0.17475825548171997, 0.03194909542798996, -0.03408300504088402, +- 0.031355202198028564, 0.03307552635669708 }, { -0.002374011091887951, +- 0.011019104160368443, 0.00536889536306262, 0.009987344965338707, +- 0.005368947982788086, 0.020124142989516258, 0.00080679333768785, +- -0.03276708722114563, -0.0007575554773211479, -0.0007960750954225659 }, { +- -0.004796746652573347, 0.022617409005761147, -0.006781514268368483, +- 0.02012668177485466, -0.007941807620227337, -0.04676627740263939, +- -0.005258339922875166, -0.046246204525232315, -0.0071578314527869225, +- -0.009032237343490124 }, { 0.009770474396646023, -0.011141248978674412, +- 0.019269749522209167, -0.014210346154868603, 0.020542146638035774, +- 0.048880185931921005, 0.020035162568092346, -0.03952275589108467, +- 0.01856170780956745, 0.018174735829234123 }, { -0.001620269613340497, +- 0.011470676399767399, 0.004611546639353037, 0.006774203386157751, +- 0.002739626681432128, 0.02218065969645977, 0.0017491821199655533, +- -0.03775838017463684, -0.000319438346195966, -0.0004950406146235764 }, { +- 0.0012955022975802422, -0.014817631803452969, 0.026199808344244957, +- -0.016461731866002083, 0.026273394003510475, 0.01814219355583191, +- 0.025083784013986588, -0.052376847714185715, 0.023744210600852966, +- 0.02334190532565117 }, { -0.004868587013334036, 0.0016003461787477136, +- 0.007678245659917593, 0.010228456929326057, 0.007693978026509285, +- 0.03484954312443733, 0.006526602897793055, -0.04437534511089325, +- 0.005023591686040163, 0.006007996853441 }, { 0.005454160738736391, +- -0.0013410535175353289, 0.018168626353144646, -0.0020983892027288675, +- 0.015060464851558208, 0.03024686872959137, 0.015840236097574234, +- -0.04037412256002426, 0.011990564875304699, 0.013903414830565453 }, { +- 0.032678719609975815, 0.029509004205465317, -0.010374271310865879, +- 0.027847718447446823, -0.0037994948215782642, -0.005600559059530497, +- -0.011222416535019875, -0.04614663124084473, -0.011661309748888016, +- -0.01393105834722519 }, { -0.015608241781592369, 0.027740608900785446, +- -0.01684558019042015, 0.028051670640707016, -0.01144394464790821, +- -0.07844652235507965, -0.010136575438082218, 0.008260861039161682, +- -0.014186658896505833, -0.018748557195067406 }, { 0.002531855134293437, +- -0.0005372058367356658, 0.003925851080566645, -0.0011777161853387952, +- 0.010105716064572334, 0.02006799913942814, 0.006369675975292921, +- -0.035407859832048416, 0.004390612710267305, 0.003906560130417347 }, { +- 0.0004741567827295512, -0.005425834562629461, 0.013468168675899506, +- -0.007848411798477173, 0.014999646693468094, 0.05871981754899025, +- 0.01284038182348013, -0.0707777887582779, 0.01137702725827694, +- 0.011949912644922733 }, { 0.0034670683089643717, 0.015495876781642437, +- -0.0003862276498693973, 0.020269881933927536, 0.006256113760173321, +- -0.03999203070998192, -0.0016746723558753729, -0.016698412597179413, +- -0.004960658494383097, -0.00985676608979702 }, { 0.35677266120910645, +- -0.3067082464694977, 0.48919913172721863, -0.31237122416496277, +- 0.4738852381706238, -0.5941600799560547, 0.4615767002105713, +- 0.047763656824827194, 0.40800178050994873, 0.4500437378883362 }, { +- -0.25821566581726074, -0.22462493181228638, 0.4883313477039337, +- -0.24713778495788574, 0.5772050619125366, -0.7332009673118591, +- 0.5216001868247986, 0.5480482578277588, 0.43185901641845703, +- 0.4523636996746063 }, { 0.19879445433616638, -0.25711092352867126, +- 0.4803866744041443, -0.2656075656414032, 0.5224763751029968, +- -0.6769493222236633, 0.48308855295181274, 0.3640625476837158, +- 0.4015120565891266, 0.44193294644355774 }, { 0.17444361746311188, +- -0.28432539105415344, 0.5108065009117126, -0.29203298687934875, +- 0.4814567267894745, -0.6813467741012573, 0.47634705901145935, +- 0.17478255927562714, 0.4159505069255829, 0.471327543258667 }, { +- 0.5217663645744324, -0.17899653315544128, 0.39776644110679626, +- -0.20610567927360535, 0.42632627487182617, -0.7954551577568054, +- 0.42123308777809143, 0.4024495780467987, 0.33674800395965576, +- 0.3868448734283447 }, { 0.31139451265335083, -0.26311445236206055, +- 0.45686542987823486, -0.26005586981773376, 0.45263493061065674, +- -0.7277234792709351, 0.44414806365966797, 0.20275796949863434, +- 0.3741361200809479, 0.4068785309791565 }, { 0.47839173674583435, +- -0.326466828584671, 0.5236772894859314, -0.3327166438102722, +- 0.4952683448791504, -0.3960452973842621, 0.4910362958908081, +- 0.03845810517668724, 0.43991318345069885, 0.4891665577888489 }, { +- 0.5242507457733154, -0.24348242580890656, 0.4767012298107147, +- -0.27835455536842346, 0.5170917510986328, -0.5139406323432922, +- 0.48506686091423035, 0.5183406472206116, 0.39156001806259155, +- 0.416274756193161 }, { 0.5781201124191284, -0.1694248616695404, +- 0.39874783158302307, -0.2267722487449646, 0.45742788910865784, +- -0.756742537021637, 0.43179264664649963, 0.5569558143615723, +- 0.3354019820690155, 0.38277822732925415 }, { 0.44512343406677246, +- -0.28214940428733826, 0.45495280623435974, -0.2845173180103302, +- 0.43727442622184753, -0.6208862066268921, 0.42300325632095337, +- 0.05262891575694084, 0.37483906745910645, 0.4194048047065735 }, { +- 0.5658515095710754, -0.24622099101543427, 0.40973880887031555, +- -0.25131961703300476, 0.4089406132698059, -0.7447801828384399, +- 0.4004283547401428, 0.2017516791820526, 0.3478139638900757, +- 0.38178232312202454 }, { 0.4014036953449249, -0.3180599808692932, +- 0.4926551580429077, -0.3234487771987915, 0.4705946743488312, +- -0.5702056884765625, 0.4595737159252167, 0.0025559605564922094, +- 0.4122040569782257, 0.4547051191329956 }, { 0.00631254818290472, +- 0.004695649724453688, 0.001254465663805604, 0.005696732550859451, +- 0.0023733554407954216, 0.04329599067568779, 0.0016458479221910238, +- -0.051247015595436096, 0.0024118819274008274, 0.0006495030829682946 }, { +- -0.0032856527250260115, -0.018718909472227097, 0.030709968879818916, +- -0.012741449289023876, 0.02889016829431057, 0.0657300055027008, +- 0.027614576742053032, -0.035456832498311996, 0.0254605021327734, +- 0.026815468445420265 }, { -0.003770829876884818, -0.0016013112617656589, +- 0.010914111509919167, 0.0016578776994720101, 0.010670076124370098, +- 0.039462439715862274, 0.010168864391744137, -0.05946071818470955, +- 0.007895838469266891, 0.009147178381681442 }, { -0.006373136304318905, +- 0.008666799403727055, 0.001952896942384541, 0.00739066069945693, +- 0.002088198671117425, 0.037924543023109436, 0.0035334238782525063, +- -0.011865115724503994, 0.0002879906678572297, -0.001207929104566574 }, { +- -0.0007611983455717564, 0.0015425271121785045, 0.0051070027984678745, +- -0.0013066474348306656, 0.005959689617156982, 0.02223912440240383, +- 0.007067907135933638, -0.024973992258310318, 0.003854858921840787, +- 0.0060404338873922825 }, { 0.017561906948685646, -0.01144108735024929, +- 0.018111426383256912, -0.012923494912683964, 0.019758738577365875, +- 0.03190469369292259, 0.019775792956352234, -0.052073489874601364, +- 0.018106823787093163, 0.016977809369564056 }, { -0.0050155650824308395, +- 0.008771779015660286, 0.00379161792807281, 0.008968946523964405, +- 0.005812218878418207, 0.02727273479104042, 0.0007583085680380464, +- -0.0533866211771965, -0.002516883658245206, 0.00009238173515768722 }, { +- -0.00247786077670753, 0.0003207485133316368, 0.016416607424616814, +- -0.002901908941566944, 0.015632500872015953, 0.04922391474246979, +- 0.011707223020493984, -0.04018537700176239, 0.014558601193130016, +- 0.013372098095715046 }, { -0.0006833462393842638, -0.008516350761055946, +- 0.016746778041124344, -0.010095765814185143, 0.019341517239809036, +- 0.07184676826000214, 0.01662410981953144, -0.0477169044315815, +- 0.014878511428833008, 0.016003578901290894 }, { 0.006134988274425268, +- 0.0028561220970004797, 0.006001178175210953, 0.0025067164096981287, +- 0.0073916856199502945, -0.005343225318938494, 0.006808382924646139, +- -0.05429920181632042, 0.005704749375581741, 0.004888042341917753 }, { +- -0.008253844454884529, -0.012654522433876991, 0.030933868139982224, +- -0.017089130356907845, 0.030207190662622452, 0.05908428877592087, +- 0.02486512064933777, -0.05387717857956886, 0.027740055695176125, +- 0.027160899713635445 }, { -0.0030630494002252817, 0.03412308543920517, +- 0.025218475610017776, 0.05634024739265442, 0.0145757757127285, +- 0.03613355755805969, 0.017948228865861893, 0.0029273657128214836, +- 0.019232898950576782, 0.025804951786994934 }, { -0.0053084841929376125, +- 0.008004368282854557, 0.002490437123924494, 0.007242793682962656, +- 0.002230247249826789, -0.005027027800679207, 0.0013848483795300126, +- -0.05966925993561745, 0.0007122044917196035, 0.0005953755462542176 }, { +- -0.003552465233951807, 0.01371860597282648, 0.00029682100284844637, +- 0.014464018866419792, 0.006823183503001928, -0.0029436377808451653, +- 0.0036627105437219143, -0.026593642309308052, 0.003319727722555399, +- -0.004079866223037243 }, { -0.004950231406837702, 0.009796304628252983, +- -0.005045390222221613, 0.010425612330436707, -0.0023220933508127928, +- 0.0281475018709898, -0.00399128720164299, -0.059150245040655136, +- -0.0031713603530079126, -0.005205043125897646 }, { -0.009771433658897877, +- -0.0007495912723243237, 0.011543015018105507, 0.00681203231215477, +- 0.011193306185305119, 0.056541558355093, 0.009296410717070103, +- -0.048109039664268494, 0.009890548884868622, 0.01428703498095274 }, { +- 0.0004116710042580962, -0.00467436108738184, 0.010720730759203434, +- -0.0071477461606264114, 0.010865158401429653, 0.028416208922863007, +- 0.01043710671365261, -0.05642630532383919, 0.009662536904215813, +- 0.009986967779695988 }, { -0.00758307846263051, 0.0014912659535184503, +- 0.01690526492893696, 0.004506679717451334, 0.013007068075239658, +- 0.05443597212433815, 0.012869101949036121, -0.04848651587963104, +- 0.013623030856251717, 0.01024931576102972 }, { -0.0021359408274292946, +- 0.0019886293448507786, 0.0138174407184124, -0.0016140117077156901, +- 0.016112633049488068, 0.03569406270980835, 0.013866663910448551, +- -0.06101948767900467, 0.012550893239676952, 0.008590311743319035 }, { +- -0.00042973129893653095, -0.0015992613043636084, 0.00973537378013134, +- -0.004072064533829689, 0.013309834524989128, 0.02134225331246853, +- 0.01163671538233757, -0.04713080823421478, 0.01012404914945364, +- 0.009876932948827744 }, { -0.011610781773924828, 0.014853764325380325, +- 0.014632822014391422, 0.029135484248399734, 0.011313805356621742, +- 0.003662638133391738, 0.015793537721037865, -0.03962944820523262, +- 0.005145533941686153, 0.00009221830987371504 }, { 0.004347763489931822, +- 0.022476281970739365, -0.01422419585287571, 0.023311611264944077, +- -0.008525911718606949, -0.018679389730095863, -0.01441978570073843, +- -0.050741516053676605, -0.012619998306035995, -0.016100144013762474 }, { +- 0.26648804545402527, -0.24869973957538605, 0.5001082420349121, +- -0.26320508122444153, 0.5172261595726013, -0.600309431552887, +- 0.4873872697353363, 0.3840385675430298, 0.4301820397377014, +- 0.44938644766807556 }, { 0.19970428943634033, -0.24906688928604126, +- 0.49419620633125305, -0.2521631419658661, 0.591415286064148, +- -0.6174148321151733, 0.4951655864715576, 0.5356807708740234, +- 0.4415935277938843, 0.5007588267326355 }, { 0.45470723509788513, +- -0.21435604989528656, 0.4544687867164612, -0.24683958292007446, +- 0.4703499674797058, -0.7032299637794495, 0.42703747749328613, +- 0.3735293447971344, 0.3851105868816376, 0.4045441448688507 }, { +- 0.23523642122745514, -0.280335396528244, 0.5114400386810303, +- -0.28569453954696655, 0.519293487071991, -0.6316213607788086, +- 0.4714227318763733, 0.24102947115898132, 0.4118637144565582, +- 0.4599464237689972 }, { -0.06750105321407318, -0.23893500864505768, +- 0.5113561153411865, -0.25943517684936523, 0.5291240811347961, +- -0.8059202432632446, 0.4518407881259918, 0.559169590473175, +- 0.3906984329223633, 0.42972540855407715 }, { -0.04944419860839844, +- -0.3194381892681122, 0.5327072739601135, -0.33172181248664856, +- 0.5322365164756775, -0.6178959012031555, 0.4992518723011017, +- 0.10273844748735428, 0.4417611062526703, 0.4914925992488861 }, { +- 0.12261144071817398, -0.27961334586143494, 0.4897664785385132, +- -0.28379377722740173, 0.4701533615589142, -0.7701170444488525, +- 0.4573203921318054, 0.13482007384300232, 0.4046354293823242, +- 0.4424322843551636 }, { -0.12076657265424728, -0.26055288314819336, +- 0.5143585801124573, -0.2957977056503296, 0.5377769470214844, +- -0.7055689096450806, 0.5118506550788879, 0.5443096160888672, +- 0.42148810625076294, 0.49116748571395874 }, { 0.35388174653053284, +- -0.2781399190425873, 0.4809688925743103, -0.2846035659313202, +- 0.4714585840702057, -0.6599823236465454, 0.4498941898345947, +- 0.17147189378738403, 0.3920755982398987, 0.43900835514068604 }, { +- -0.0724145770072937, -0.298572301864624, 0.5292425155639648, +- -0.3123910129070282, 0.5187233686447144, -0.7006471157073975, +- 0.4884525239467621, 0.1735648512840271, 0.4334541857242584, +- 0.47951751947402954 }, { 0.49916213750839233, -0.22020630538463593, +- 0.44710540771484375, -0.2740632891654968, 0.48884180188179016, +- -0.6627715826034546, 0.44638872146606445, 0.46793827414512634, +- 0.3978542685508728, 0.4065893292427063 }, { -0.05901636928319931, +- -0.2896057069301605, 0.5240043997764587, -0.2961432933807373, +- 0.5208248496055603, -0.6868585348129272, 0.49868494272232056, +- 0.2699999511241913, 0.43201151490211487, 0.47376498579978943 }, { +- -0.002234352519735694, 0.016013534739613533, 0.002894036704674363, +- 0.013280924409627914, 0.003373500658199191, -0.0045653413981199265, +- 0.001402402762323618, -0.04343356192111969, 0.004746296443045139, +- -0.005772083532065153 }, { -0.003404085524380207, 0.019610367715358734, +- -0.006076980847865343, 0.02683728188276291, -0.004124600440263748, +- -0.042014770209789276, -0.0060096983797848225, -0.023686058819293976, +- -0.0037593937013298273, -0.011510312557220459 }, { 0.010586127638816833, +- -0.013853782787919044, 0.022643573582172394, -0.017112020403146744, +- 0.023843154311180115, 0.06436611711978912, 0.023400604724884033, +- -0.03352835774421692, 0.02105114795267582, 0.021704377606511116 }, { +- 0.012631593272089958, 0.01700655370950699, -0.010391277261078358, +- 0.016881456598639488, -0.00943238940089941, 0.025502588599920273, +- -0.011467267759144306, -0.048643022775650024, -0.011530925519764423, +- -0.01087180431932211 }, { 0.0008400416118092835, -0.012132988311350346, +- 0.02489103376865387, -0.014805002138018608, 0.024110503494739532, +- 0.08459749817848206, 0.02301730029284954, -0.0426340289413929, +- 0.021684782579541206, 0.022695453837513924 }, { -0.003004414727911353, +- 0.01831796206533909, -0.00488548818975687, 0.0236123688519001, +- -0.0037792918737977743, -0.044561997056007385, -0.008535650558769703, +- -0.032742831856012344, -0.001632258528843522, -0.009045031853020191 }, { +- 0.00023985032748896629, -0.0030061749275773764, 0.012408904731273651, +- -0.006272349040955305, 0.011873750016093254, 0.06629911810159683, +- 0.011471881531178951, -0.05111151188611984, 0.010345963761210442, +- 0.011247397400438786 }, { 0.0023007874842733145, 0.006500924937427044, +- 0.010920364409685135, 0.006234305445104837, 0.011485299095511436, +- 0.03967350348830223, 0.009669859893620014, -0.04745170846581459, +- 0.011817275546491146, 0.010059949941933155 }, { 0.001465094625018537, +- -0.00048672384582459927, 0.0163226667791605, -0.001292944303713739, +- 0.0151982381939888, 0.0606490895152092, 0.013637114316225052, +- -0.06261260062456131, 0.014055446721613407, 0.013576042838394642 }, { +- -0.0001873112632893026, 0.0015860183630138636, 0.003434687154367566, +- -0.00017216514970641583, 0.0029561116825789213, 0.018156403675675392, +- 0.0027261353097856045, -0.04237988591194153, 0.00225082878023386, +- 0.0024046837352216244 }, { -0.0026958626694977283, 0.009052198380231857, +- 0.005014043767005205, 0.01248753722757101, 0.0035849723499268293, +- 0.02170162834227085, 0.0008693770505487919, -0.06040492653846741, +- 0.002804276766255498, 0.0051020486280322075 }, { 0.00942535512149334, +- -0.004300141241401434, 0.008385482244193554, -0.004852784797549248, +- 0.011213487014174461, 0.018281737342476845, 0.011016284115612507, +- -0.038639508187770844, 0.008772789500653744, 0.007894097827374935 }, { +- 0.0010291871149092913, -0.00981814879924059, 0.024387352168560028, +- -0.010375672951340675, 0.01893046498298645, 0.06595788896083832, +- 0.01827145181596279, -0.0444631353020668, 0.016436178237199783, +- 0.017636051401495934 }, { -0.010090198367834091, 0.008834106847643852, +- 0.002184360520914197, 0.007961283437907696, 0.0010680037084966898, +- 0.028744075447320938, -0.00012055880506522954, -0.04150494188070297, +- 0.00022014508431311697, -0.00010875037696678191 }, { -0.002214360050857067, +- 0.002061660634353757, 0.003913296386599541, 0.0011758452747017145, +- 0.005529853515326977, 0.03328150138258934, 0.0036821728572249413, +- -0.06388077139854431, 0.003236079355701804, 0.0020536042284220457 }, { +- 0.005336817819625139, -0.003152756718918681, 0.010073942132294178, +- -0.0028491178527474403, 0.011316847987473011, 0.04968370124697685, +- 0.009736640378832817, -0.022268712520599365, 0.008456943556666374, +- 0.009223775006830692 }, { 0.003610957181081176, 0.0018024005694314837, +- 0.006994737312197685, 0.0003976915904786438, 0.006564057432115078, +- 0.028843484818935394, 0.006228307727724314, -0.04193565621972084, +- 0.004515984561294317, 0.0038089128211140633 }, { 0.0035793259739875793, +- 0.00024845683947205544, 0.011646122671663761, -0.0006574213039129972, +- 0.01175664085894823, 0.032858606427907944, 0.011057045310735703, +- -0.05352763459086418, 0.009176482446491718, 0.007683977019041777 }, { +- 0.017327478155493736, -0.021014131605625153, 0.03675592318177223, +- -0.023575730621814728, 0.03674892336130142, 0.17082227766513824, +- 0.03329358994960785, -0.06862113624811172, 0.035587064921855927, +- 0.03224540501832962 }, { -0.0003742593398783356, -0.0007492642616853118, +- 0.01074102334678173, 0.004762626253068447, 0.010435575619339943, +- 0.05701477453112602, 0.008857154287397861, -0.04856060445308685, +- 0.009517062455415726, 0.009449489414691925 }, { 0.021506890654563904, +- -0.004900256637483835, 0.013120364397764206, -0.004270331934094429, +- 0.013063513673841953, 0.036737289279699326, 0.01273847185075283, +- -0.030944833531975746, 0.011892692185938358, 0.012283744290471077 }, { +- -0.0005700172041542828, 0.012877707369625568, -0.0023861911613494158, +- 0.012297096662223339, 0.007443356327712536, 0.032988592982292175, +- -0.006841632537543774, -0.0522049181163311, -0.0029206429608166218, +- -0.005925927311182022 }, { 0.32971906661987305, -0.2569878399372101, +- 0.5150460004806519, -0.25439950823783875, 0.6113936901092529, +- -0.48353490233421326, 0.5259202718734741, 0.5645553469657898, +- 0.4593457877635956, 0.46227583289146423 }, { 0.37084445357322693, +- -0.20920789241790771, 0.45929017663002014, -0.260132759809494, +- 0.47654494643211365, -0.742158055305481, 0.43023625016212463, +- 0.4480195939540863, 0.38146743178367615, 0.41218599677085876 }, { +- -0.13799647986888885, -0.2610854506492615, 0.4780105650424957, +- -0.2924763262271881, 0.5766012668609619, -0.7103790640830994, +- 0.5072041153907776, 0.5746259093284607, 0.42655065655708313, +- 0.4908026456832886 }, { 0.2539712190628052, -0.273158997297287, +- 0.5076822638511658, -0.2911801040172577, 0.5195984840393066, +- -0.5903722643852234, 0.49708008766174316, 0.25440511107444763, +- 0.42723336815834045, 0.4804675579071045 }, { 0.4277496337890625, +- -0.2303372174501419, 0.4371229112148285, -0.22747065126895905, +- 0.41435521841049194, -0.8005786538124084, 0.40467798709869385, +- 0.22788158059120178, 0.34313827753067017, 0.3844327926635742 }, { +- -0.05193758383393288, -0.32405146956443787, 0.5210202932357788, +- -0.3333767056465149, 0.5091855525970459, -0.6626431345939636, +- 0.5018999576568604, 0.12005914747714996, 0.4389205276966095, +- 0.4834160804748535 }, { 0.3554810881614685, -0.27852216362953186, +- 0.45156434178352356, -0.28202542662620544, 0.4282422959804535, +- -0.7150483131408691, 0.4183313548564911, 0.038397300988435745, +- 0.3712427616119385, 0.4142305850982666 }, { 0.41701021790504456, +- -0.2574079632759094, 0.44315722584724426, -0.26499179005622864, +- 0.4175316393375397, -0.6946894526481628, 0.407196044921875, +- 0.11480291187763214, 0.36187565326690674, 0.40444374084472656 }, { +- 0.49864259362220764, -0.24022161960601807, 0.4428102970123291, +- -0.26831868290901184, 0.49580544233322144, -0.6362045407295227, +- 0.41864120960235596, 0.3392195403575897, 0.39018359780311584, +- 0.4225161671638489 }, { 0.4728687107563019, -0.288887083530426, +- 0.4623958170413971, -0.29381319880485535, 0.4627613127231598, +- -0.6314435005187988, 0.446813702583313, 0.157890185713768, 0.3954184353351593, +- 0.436819463968277 }, { 0.7190647721290588, -0.20700779557228088, +- 0.4109399616718292, -0.20049646496772766, 0.4552929699420929, +- -0.6912363767623901, 0.3867221176624298, 0.39670324325561523, +- 0.36270585656166077, 0.36164742708206177 }, { 0.39509230852127075, +- -0.3203185796737671, 0.49347588419914246, -0.3236173093318939, +- 0.47046801447868347, -0.587643027305603, 0.4603486657142639, +- 0.019273223355412483, 0.41295307874679565, 0.45615461468696594 }, { +- 0.0015107422368600965, -0.008111530914902687, 0.01403808407485485, +- -0.007661266718059778, 0.018967866897583008, 0.05385633558034897, +- 0.01691502332687378, -0.05261307954788208, 0.017988597974181175, +- 0.014358200132846832 }, { -0.003521962324157357, -0.0030430376064032316, +- 0.01573706604540348, -0.004934822674840689, 0.015962257981300354, +- 0.03217754885554314, 0.015031723305583, -0.04776114970445633, +- 0.013775642961263657, 0.014163395389914513 }, { -0.00162152957636863, +- 0.01031531672924757, 0.0025367580819875, 0.010885938070714474, +- 0.0012719847727566957, 0.007090716157108545, 0.00349213695153594, +- -0.048895012587308884, 0.00009081598545890301, 0.0007498999475501478 }, { +- -0.005247785244137049, 0.013717358000576496, 0.0017076137010008097, +- 0.011759471148252487, 0.006176077760756016, 0.03477432206273079, +- -0.00039399511297233403, -0.04536423832178116, -0.0024714532773941755, +- 0.0065734791569411755 }, { -0.00886576622724533, 0.0015375255607068539, +- 0.013791244477033615, -0.001750514260493219, 0.01292826421558857, +- 0.05824657902121544, 0.007982349023222923, -0.03780524805188179, +- 0.010814276523888111, 0.010736916214227676 }, { -0.009352477267384529, +- 0.0023313374258577824, 0.004659546539187431, 0.0012798323296010494, +- 0.009132716804742813, 0.03420717641711235, 0.00446666544303298, +- -0.04006955772638321, 0.005003183614462614, 0.003841248108074069 }, { +- 0.01427305769175291, -0.015364369377493858, 0.026737773790955544, +- -0.016058186069130898, 0.03137701749801636, 0.13593988120555878, +- 0.02650587633252144, -0.03361409902572632, 0.026142967864871025, +- 0.026552049443125725 }, { -0.00013387583021540195, -0.01002851128578186, +- 0.018820548430085182, -0.011509539559483528, 0.020002681761980057, +- 0.06615198403596878, 0.01913979835808277, -0.04728790372610092, +- 0.017776139080524445, 0.017894001677632332 }, { -0.0019412607653066516, +- -0.0018155045108869672, 0.00907590240240097, 0.00012895507097709924, +- 0.008858051151037216, 0.04029446840286255, 0.010800599120557308, +- -0.050823770463466644, 0.009680962190032005, 0.006776850204914808 }, { +- 0.004218680318444967, -0.0006653969758190215, 0.011195164173841476, +- 0.0028215600177645683, 0.011957314796745777, 0.027502169832587242, +- 0.009083712473511696, -0.04110126942396164, 0.010082284919917583, +- 0.009515357203781605 }, { -0.010092077776789665, 0.0004374016134534031, +- 0.00759620126336813, 0.0005942480638623238, 0.00704161124303937, +- 0.06316915899515152, 0.005854485090821981, -0.052614759653806686, +- 0.00454446068033576, 0.005376110319048166 }, { -0.009543361142277718, +- 0.004790019243955612, 0.002699700416997075, 0.002880487125366926, +- 0.0036820336245000362, 0.05957769602537155, 0.0025948784314095974, +- -0.03791522979736328, 0.004117514006793499, 0.0023839971981942654 }, { +- -0.003232921240851283, 0.00028455432038754225, 0.003735104575753212, +- 0.0003304227429907769, 0.004987048450857401, 0.020830029621720314, +- 0.004268736578524113, -0.03267630934715271, 0.0032730563543736935, +- 0.0030928240157663822 }, { -0.007887831889092922, 0.01775304786860943, +- -0.010209411382675171, 0.01583942398428917, -0.00694592110812664, +- -0.04492638260126114, -0.009174861013889313, -0.04145486652851105, +- -0.00873994454741478, -0.011859103105962276 }, { -0.011508720926940441, +- -0.0008797270711511374, 0.012334436178207397, -0.002126268809661269, +- 0.011760641820728779, 0.06561554968357086, 0.010725924745202065, +- -0.05713455379009247, 0.009644977748394012, 0.010311487130820751 }, { +- -0.0014114591758698225, -0.0013780308654531837, 0.009845906868577003, +- 0.00019869966490659863, 0.012768428772687912, 0.015545024536550045, +- 0.00971787516027689, -0.0343363881111145, 0.01124667376279831, +- 0.007428389508277178 }, { -0.0019165310077369213, 0.015990756452083588, +- -0.003961917478591204, 0.01446043886244297, -0.0020961558911949396, +- -0.024069203063845634, -0.0009506303467787802, -0.026605797931551933, +- -0.004948863293975592, -0.005360433831810951 }, { -0.0009331670007668436, +- 0.010439150966703892, 0.002033381722867489, 0.005976949818432331, +- 0.0030609718523919582, -0.0152490409091115, 0.0022052470594644547, +- -0.02463957481086254, 0.0018451027572154999, 0.0016752344090491533 }, { +- -0.02421066164970398, 0.02548786997795105, -0.014983361586928368, +- 0.02521836943924427, -0.01682799868285656, -0.07953552156686783, +- -0.018269795924425125, -0.015474652871489525, -0.018383121117949486, +- -0.01861703395843506 }, { -0.0022197780199348927, 0.0054593454115092754, +- 0.012789066880941391, 0.0043558888137340546, 0.012382447719573975, +- 0.033836692571640015, 0.008521873503923416, -0.05198455974459648, +- 0.00327088451012969, 0.00403466634452343 }, { 0.0012469867942854762, +- 0.000039742051740176976, 0.012675073929131031, 0.0071757473051548, +- 0.010641961358487606, 0.03840821236371994, 0.008852503262460232, +- -0.0515020452439785, 0.00727095315232873, 0.007266306318342686 }, { +- 0.0239836648106575, 0.022883733734488487, -0.009106436744332314, +- 0.024731729179620743, -0.010678094811737537, -0.007750748656690121, +- -0.017125196754932404, -0.047901980578899384, -0.01314406655728817, +- -0.0152578828856349 }, { 0.2024650275707245, -0.2556355893611908, +- 0.49706771969795227, -0.26546382904052734, 0.5170111656188965, +- -0.7027327418327332, 0.4737210273742676, 0.5158984661102295, +- 0.4176698923110962, 0.45604848861694336 }, { 0.2511301040649414, +- -0.25669336318969727, 0.44285476207733154, -0.2640312612056732, +- 0.43021368980407715, -0.7673763632774353, 0.41621434688568115, +- 0.14666114747524261, 0.360414981842041, 0.4032975435256958 }, { +- -0.035878200083971024, -0.29932811856269836, 0.5200004577636719, +- -0.30275869369506836, 0.5039951801300049, -0.7101736664772034, +- 0.4889015555381775, 0.15757746994495392, 0.43065378069877625, +- 0.47586190700531006 }, { 0.42416346073150635, -0.284225195646286, +- 0.4706224203109741, -0.28746193647384644, 0.44657576084136963, +- -0.6410810351371765, 0.43831899762153625, 0.10210704803466797, +- 0.3875702917575836, 0.4324491322040558 }, { 0.3799944519996643, +- -0.23468098044395447, 0.43374767899513245, -0.24029609560966492, +- 0.4265262484550476, -0.8125537037849426, 0.40513134002685547, +- 0.23688483238220215, 0.35393282771110535, 0.39388179779052734 }, { +- 0.5151740908622742, -0.23919852077960968, 0.454291433095932, +- -0.25200510025024414, 0.48922625184059143, -0.5916802883148193, +- 0.46632474660873413, 0.33609893918037415, 0.3940261900424957, +- 0.4241180121898651 }, { 0.31598159670829773, -0.2846437096595764, +- 0.489607036113739, -0.2929103970527649, 0.4900318384170532, +- -0.5738467574119568, 0.4853067100048065, 0.20726218819618225, +- 0.4131965935230255, 0.4621387720108032 }, { 0.17968030273914337, +- -0.3021254539489746, 0.5211050510406494, -0.30443263053894043, +- 0.4841987192630768, -0.6259934902191162, 0.47785505652427673, +- 0.09802400320768356, 0.42168256640434265, 0.47705498337745667 }, { +- -0.072822205722332, -0.26418083906173706, 0.5029717683792114, +- -0.273860901594162, 0.5238067507743835, -0.7305569648742676, +- 0.47113296389579773, 0.3515421152114868, 0.404433935880661, 0.4439637362957001 +- }, { 0.49909141659736633, -0.25807374715805054, 0.44910740852355957, +- -0.2690280079841614, 0.45146873593330383, -0.6549240946769714, +- 0.44270578026771545, 0.14907193183898926, 0.3812515139579773, +- 0.404631644487381 }, { 0.9451454281806946, -0.12172175943851471, +- 0.35375872254371643, -0.12911345064640045, 0.4211767911911011, +- -0.7854087352752686, 0.4128362834453583, 0.7100168466567993, +- 0.275394082069397, 0.2850882112979889 }, { 0.32620251178741455, +- -0.2837602496147156, 0.5272522568702698, -0.28859567642211914, +- 0.5113046765327454, -0.5401381850242615, 0.48731696605682373, +- 0.1767905354499817, 0.429871529340744, 0.4830949306488037 }, { +- 0.0001034512824844569, 0.0014297087909653783, 0.007380022667348385, +- -0.0013273477088660002, 0.008180003613233566, 0.02884218655526638, +- 0.006742201279848814, -0.04736163094639778, 0.0064711500890553, +- 0.005683882161974907 }, { -0.0012720412341877818, 0.0017057316144928336, +- 0.012861287221312523, 0.0008145716856233776, 0.012058421038091183, +- 0.05043844133615494, 0.012121297419071198, -0.03895298019051552, +- 0.010499618947505951, 0.00921549927443266 }, { -0.003398544155061245, +- 0.007194585166871548, 0.006937784608453512, 0.007087428588420153, +- 0.014601846225559711, 0.004313499666750431, 0.010342626832425594, +- -0.04732348024845123, 0.007367708720266819, 0.00476636691018939 }, { +- -0.00038893934106454253, -0.0054539781995117664, 0.017097998410463333, +- -0.0077361674048006535, 0.01611400954425335, 0.06385892629623413, +- 0.01621103473007679, -0.05143913999199867, 0.014218196272850037, +- 0.015829984098672867 }, { -0.0015569461975246668, -0.0004716807743534446, +- 0.01386708952486515, -0.001851478940807283, 0.014144710265100002, +- 0.058693777769804, 0.007606356870383024, -0.05040718987584114, +- 0.0086852191016078, 0.006490609608590603 }, { -0.0008776353788562119, +- 0.0025141597725450993, 0.00858901347965002, 0.00003631951767602004, +- 0.007644773926585913, 0.030429380014538765, 0.007567201275378466, +- -0.04120470583438873, 0.006361702457070351, 0.007417372427880764 }, { +- -0.007474453654140234, 0.008329227566719055, -0.0014144275337457657, +- 0.009105396457016468, -0.0017333802534267306, 0.03594202548265457, +- -0.00170602195430547, -0.0650184378027916, -0.0035165611188858747, +- -0.0012938230065628886 }, { -0.005699983797967434, 0.004849062766879797, +- 0.008374503813683987, 0.0041782306507229805, 0.005268950015306473, +- 0.003478845115751028, 0.005731129553169012, -0.03419240936636925, +- 0.003018561750650406, 0.0041783456690609455 }, { 0.02009463682770729, +- 0.02764892764389515, -0.017680699005723, 0.026912381872534752, +- -0.01412166841328144, -0.0250224769115448, -0.02268531359732151, +- -0.05545519292354584, -0.015660272911190987, -0.021383706480264664 }, { +- -0.005010075867176056, -0.002268025651574135, 0.011254868470132351, +- -0.0009375481167808175, 0.0089499456807971, 0.04841604456305504, +- 0.009131637401878834, -0.044481053948402405, 0.00793182011693716, +- 0.009038356132805347 }, { -0.00508113531395793, 0.002514268271625042, +- 0.002993303118273616, 0.0018126214854419231, 0.008111991919577122, +- 0.01890823058784008, 0.005373830441385508, -0.0427052341401577, +- 0.004490814171731472, 0.002286323579028249 }, { -0.0007105849799700081, +- -0.0006577741005457938, 0.012885059230029583, -0.005983484908938408, +- 0.01568477228283882, 0.041648004204034805, 0.013795964419841766, +- -0.04489719495177269, 0.013163908384740353, 0.010458777658641338 }, { +- 0.003718580584973097, -0.004785879980772734, 0.017198089510202408, +- -0.0068361032754182816, 0.018540117889642715, 0.059733398258686066, +- 0.018065281212329865, -0.034459616988897324, 0.013666990213096142, +- 0.015126876533031464 }, { -0.0022624428384006023, 0.008888696320354939, +- 0.007892398163676262, 0.012993923388421535, 0.003014610381796956, +- 0.00876035075634718, -0.0007005254738032818, -0.05129927396774292, +- 0.0011976168025285006, -0.00349798914976418 }, { -0.0020581118296831846, +- 0.007179563399404287, 0.002437642542645335, 0.0070954617112874985, +- -0.00042830390157178044, 0.06703010201454163, 0.0006539201131090522, +- -0.05018097907304764, -0.0021458633709698915, -0.0021123839542269707 }, { +- -0.015272526070475578, 0.00923843588680029, 0.0026710068341344595, +- 0.007502542342990637, 0.002589230891317129, 0.024879610165953636, +- 0.0009149846737273037, -0.029469823464751244, 0.0011784625239670277, +- -0.0012270213337615132 }, { 0.0033333704341202974, -0.013977563008666039, +- 0.019253617152571678, -0.012292261235415936, 0.019836261868476868, +- 0.04821594059467316, 0.01946161314845085, -0.04534419998526573, +- 0.01916733756661415, 0.019060125574469566 }, { 0.01414389070123434, +- -0.019738802686333656, 0.043368540704250336, -0.017700836062431335, +- 0.037637174129486084, 0.1972360759973526, 0.036332279443740845, +- -0.03329158574342728, 0.04156399145722389, 0.039473552256822586 }, { +- -0.0024562107864767313, 0.005195387173444033, 0.005882758181542158, +- 0.006527344696223736, 0.006949870381504297, 0.002454560250043869, +- 0.00637483038008213, -0.05690411105751991, 0.002228259574621916, +- 0.002057186560705304 }, { -0.002114897361025214, -0.004048052243888378, +- 0.020294521003961563, -0.006519239861518145, 0.01701320894062519, +- 0.047927163541316986, 0.015208413824439049, -0.039080459624528885, +- 0.01596655696630478, 0.012692058458924294 }, { -0.0031728586181998253, +- -0.006308139767497778, 0.014073040336370468, -0.0076532019302248955, +- 0.014445527456700802, 0.060555350035429, 0.012900394387543201, +- -0.04208141565322876, 0.011637561023235321, 0.012679635547101498 }, { +- -0.003142057452350855, -0.0034169128630310297, 0.009995733387768269, +- 0.0040106694214046, 0.011664467863738537, 0.04288806766271591, +- 0.012626497074961662, -0.035296183079481125, 0.008529572747647762, +- 0.00935597624629736 }, { 0.4980035126209259, -0.2097456455230713, +- 0.4284243881702423, -0.19634218513965607, 0.4643947184085846, +- -0.7277891635894775, 0.42098382115364075, 0.4525802433490753, +- 0.34272322058677673, 0.36339765787124634 }, { 0.37171104550361633, +- -0.2734377086162567, 0.4560849070549011, -0.2769210636615753, +- 0.4302436411380768, -0.7394245266914368, 0.4212239980697632, +- 0.06778233498334885, 0.3737483620643616, 0.41849958896636963 }, { +- 0.14315171539783478, -0.33280885219573975, 0.5282275676727295, +- -0.33608922362327576, 0.5097172260284424, -0.5935012698173523, +- 0.49451935291290283, 0.027227789163589478, 0.4386059045791626, +- 0.4863540828227997 }, { 0.39705657958984375, -0.3159700930118561, +- 0.489080011844635, -0.3196788430213928, 0.4660041332244873, +- -0.5700941681861877, 0.456408828496933, 0.00902116671204567, +- 0.40913039445877075, 0.4516433775424957 }, { -0.05363082513213158, +- -0.2923521101474762, 0.527229368686676, -0.31503698229789734, +- 0.5346335768699646, -0.6469199657440186, 0.5115681290626526, +- 0.22393152117729187, 0.45344555377960205, 0.4775262773036957 }, { +- -0.2058877944946289, -0.20928259193897247, 0.48974913358688354, +- -0.2633398175239563, 0.5812196135520935, -0.7458516359329224, +- 0.5018584132194519, 0.8229286074638367, 0.40985649824142456, +- 0.47616511583328247 }, { -0.03551383689045906, -0.294028639793396, +- 0.5143598914146423, -0.2975674271583557, 0.5005382299423218, +- -0.7268581986427307, 0.4848017990589142, 0.16768774390220642, +- 0.4265562891960144, 0.4712880551815033 }, { 0.07675863057374954, +- -0.2376459538936615, 0.5038663744926453, -0.21692903339862823, +- 0.5103217959403992, -0.7743855714797974, 0.4897463023662567, +- 0.5830397009849548, 0.41292136907577515, 0.4403582215309143 }, { +- 0.38198336958885193, -0.30988505482673645, 0.49205297231674194, +- -0.3137170970439911, 0.4636269509792328, -0.5859404802322388, +- 0.45566508173942566, 0.034200482070446014, 0.4076629877090454, +- 0.45405641198158264 }, { 0.6943585872650146, -0.2162187397480011, +- 0.415563702583313, -0.21747009456157684, 0.4811369776725769, +- -0.6374079585075378, 0.45303553342819214, 0.5088182687759399, +- 0.3500761091709137, 0.38261595368385315 }, { 0.4866848886013031, +- -0.19974316656589508, 0.43205076456069946, -0.2500990033149719, +- 0.5082396864891052, -0.6846417784690857, 0.4276694357395172, +- 0.4790182113647461, 0.39519038796424866, 0.41071170568466187 }, { +- -0.13120195269584656, -0.28756099939346313, 0.506804883480072, +- -0.29273855686187744, 0.5201780796051025, -0.7256585359573364, +- 0.4998188614845276, 0.23331651091575623, 0.420910507440567, +- 0.46347561478614807 }, { 0.020263617858290672, -0.02398751862347126, +- 0.031117049977183342, -0.026444517076015472, 0.033532917499542236, +- 0.15016087889671326, 0.031977180391550064, -0.022426113486289978, +- 0.032213244587183, 0.031215893104672432 }, { 0.00016303094162140042, +- -0.0005079557886347175, 0.008175727911293507, -0.003915916197001934, +- 0.008960898965597153, 0.027809739112854004, 0.0070376042276620865, +- -0.032052360475063324, 0.006963090039789677, 0.006496068090200424 }, { +- 0.0015253173187375069, 0.014777729287743568, -0.0019750716164708138, +- 0.013338723219931126, 0.0002951306232716888, -0.00804287288337946, +- -0.0028200107626616955, -0.05147448182106018, -0.003235999494791031, +- -0.006361926905810833 }, { -0.00026068519218824804, 0.007341846823692322, +- 0.00494029326364398, 0.007645924109965563, 0.006241479888558388, +- 0.012532778084278107, 0.0023908554576337337, -0.047997087240219116, +- 0.0019561871886253357, 0.003923457581549883 }, { 0.0055681257508695126, +- -0.0065895807929337025, 0.019353939220309258, -0.0072127473540604115, +- 0.02609376609325409, -0.007900835946202278, 0.019326237961649895, +- -0.06654947251081467, 0.016001546755433083, 0.016774775460362434 }, { +- -0.004201805684715509, 0.002335949568077922, 0.004686316009610891, +- 0.000633189280051738, 0.007542524952441454, 0.05224112421274185, +- 0.006588107440620661, -0.04902205243706703, 0.006768723484128714, +- 0.003934974316507578 }, { -0.005735934246331453, -0.002975957468152046, +- 0.014403949491679668, -0.0026752986013889313, 0.01753624528646469, +- 0.06800984591245651, 0.01723332889378071, -0.03963148593902588, +- 0.015854639932513237, 0.012425081804394722 }, { -0.004707885440438986, +- 0.011655726470053196, -0.000299384439131245, 0.015042067505419254, +- -0.0007629760657437146, -0.022438373416662216, -0.0022332409862428904, +- -0.051876358687877655, -0.0005419617518782616, -0.005716115701943636 }, { +- 0.004690894391387701, 0.004863144364207983, 0.00782408844679594, +- 0.007873046211898327, 0.008985517546534538, 0.01980200596153736, +- 0.007053704001009464, -0.01914944313466549, 0.005738165695220232, +- 0.0047444868832826614 }, { 0.0014098609099164605, -0.0003986054507549852, +- 0.007087371312081814, -0.00028210325399413705, 0.009423747658729553, +- 0.029143216088414192, 0.008709214627742767, -0.036258161067962646, +- 0.005485150497406721, 0.006132291629910469 }, { 0.008434508927166462, +- 0.005169567186385393, 0.006516270339488983, 0.002090160036459565, +- 0.0064424509182572365, 0.033114708960056305, 0.004466255195438862, +- -0.03761342167854309, 0.0021806969307363033, 0.0026210618671029806 }, { +- -0.0008251544204540551, 0.005243905819952488, 0.0063179307617247105, +- 0.010176618583500385, 0.006117727607488632, 0.059992752969264984, +- 0.004373328760266304, -0.045534078031778336, 0.0033736361656337976, +- 0.0038741719909012318 }, { -0.0018498452845960855, 0.0011218012077733874, +- 0.0035896305926144123, 0.00786408968269825, 0.010716152377426624, +- 0.01770993135869503, 0.004966005217283964, -0.03396230936050415, +- 0.00418362021446228, 0.0032204908784478903 }, { 0.005331010557711124, +- -0.007274183910340071, 0.019492829218506813, -0.007708560675382614, +- 0.018325237557291985, 0.021840400993824005, 0.01857992447912693, +- -0.05069852992892265, 0.015546310693025589, 0.017652282491326332 }, { +- 0.0061551243998110294, -0.0067198751494288445, 0.01217375136911869, +- -0.0038776465225964785, 0.015730099752545357, 0.04559868574142456, +- 0.013174815103411674, -0.04056819900870323, 0.012913263402879238, +- 0.011467943899333477 }, { -0.005125180818140507, 0.00437471829354763, +- 0.011322085745632648, -0.0009740858804434538, 0.010905669070780277, +- 0.042331743985414505, 0.007754409685730934, -0.03777961805462837, +- 0.008502025157213211, 0.008063827641308308 }, { 0.00009201361535815522, +- -0.00939862709492445, 0.01751229353249073, -0.00897576566785574, +- 0.016598859801888466, 0.04656074941158295, 0.017772328108549118, +- -0.04154404252767563, 0.015240632928907871, 0.014678829349577427 }, { +- 0.0034175082109868526, -0.006245067343115807, 0.01799560897052288, +- -0.007359115872532129, 0.017865080386400223, 0.09320395439863205, +- 0.01875576376914978, -0.05601831153035164, 0.014776765368878841, +- 0.017442749813199043 }, { 0.0344444215297699, 0.015497531741857529, +- -0.004650880116969347, 0.012920305132865906, -0.0033617683220654726, +- 0.009689759463071823, -0.0060478332452476025, -0.054280441254377365, +- -0.005980691406875849, -0.006819257978349924 }, { -0.0016789792571216822, +- 0.017763644456863403, -0.011233877390623093, 0.01623489335179329, +- -0.004851062782108784, -0.033496346324682236, -0.005862857215106487, +- -0.020943569019436836, -0.01264400128275156, -0.012615418061614037 }, { +- 0.0032434293534606695, -0.002535657724365592, 0.016603417694568634, +- -0.0011759568005800247, 0.016816364601254463, 0.05420137941837311, +- 0.011708744801580906, -0.03238052502274513, 0.010250696912407875, +- 0.01224898174405098 }, { -0.010468143969774246, 0.006247559562325478, +- 0.000442272488726303, 0.005198853090405464, 0.001934064901433885, +- 0.06291567534208298, 0.00007925542013254017, -0.049085527658462524, +- 0.00014641298912465572, -0.000234912644373253 }, { 0.05748433992266655, +- -0.3004145622253418, 0.5059400796890259, -0.3048456907272339, +- 0.48982006311416626, -0.7122836709022522, 0.4675930142402649, +- 0.10337714105844498, 0.4189482033252716, 0.4583773612976074 }, { +- 0.2314625382423401, -0.28387755155563354, 0.48159700632095337, +- -0.2916921079158783, 0.49806106090545654, -0.6411739587783813, +- 0.4746934473514557, 0.21576014161109924, 0.43059784173965454, +- 0.4659345746040344 }, { 0.7322643399238586, -0.17596033215522766, +- 0.40273377299308777, -0.21373124420642853, 0.4535852074623108, +- -0.6836487054824829, 0.40143248438835144, 0.46859216690063477, +- 0.3652418851852417, 0.3740067481994629 }, { 0.5762629508972168, +- -0.21222488582134247, 0.4420599937438965, -0.2340168058872223, +- 0.46325451135635376, -0.6806350946426392, 0.40162724256515503, +- 0.35366183519363403, 0.3584599196910858, 0.38596633076667786 }, { +- 0.4814559817314148, -0.284950852394104, 0.4941795766353607, +- -0.2858513593673706, 0.4752497971057892, -0.5404026508331299, +- 0.4468853771686554, 0.13097383081912994, 0.4004047214984894, +- 0.4461156129837036 }, { 0.46920904517173767, -0.22464995086193085, +- 0.41227978467941284, -0.22737666964530945, 0.4354296326637268, +- -0.7572371363639832, 0.40751829743385315, 0.26966050267219543, +- 0.3621707558631897, 0.37370315194129944 }, { 0.4001244306564331, +- -0.3015642464160919, 0.4823078513145447, -0.30654510855674744, +- 0.4531826376914978, -0.6341028213500977, 0.4467841386795044, +- 0.07889309525489807, 0.4005136489868164, 0.44535648822784424 }, { +- 0.08536136895418167, -0.2894590497016907, 0.5369172096252441, +- -0.2975267767906189, 0.5423058271408081, -0.6058066487312317, +- 0.5260575413703918, 0.10728677362203598, 0.4588721990585327, +- 0.4774160385131836 }, { -0.156595841050148, -0.3079659342765808, +- 0.5159251093864441, -0.30142104625701904, 0.5428873300552368, +- -0.6842026114463806, 0.48538678884506226, 0.27556174993515015, +- 0.4482533633708954, 0.48419129848480225 }, { -0.16680443286895752, +- -0.2451428771018982, 0.48644188046455383, -0.26209893822669983, +- 0.5361882448196411, -0.7496296763420105, 0.4787711501121521, +- 0.4982735216617584, 0.4359911382198334, 0.44098228216171265 }, { +- -0.0635901391506195, -0.2188900113105774, 0.47698721289634705, +- -0.2903880774974823, 0.5613070130348206, -0.7348792552947998, +- 0.4638911187648773, 0.6031451225280762, 0.46169912815093994, +- 0.48178136348724365 }, { -0.10117660462856293, -0.24561303853988647, +- 0.5169081687927246, -0.2897927761077881, 0.5870382189750671, +- -0.6910725831985474, 0.5089510679244995, 0.7973889708518982, +- 0.4971103370189667, 0.48363053798675537 }, { -0.0006422133301384747, +- 0.00699690543115139, 0.014983654953539371, 0.0058754812926054, +- 0.013354708440601826, 0.06260710954666138, 0.01104094460606575, +- -0.023850157856941223, 0.008996900171041489, 0.006454320624470711 }, { +- 0.002674293937161565, 0.009830446913838387, 0.015991022810339928, +- 0.01222702581435442, 0.015490354970097542, 0.023672504350543022, +- 0.008879203349351883, -0.07357189059257507, 0.011916869319975376, +- 0.008848906494677067 }, { -0.00005529536429094151, -0.009714844636619091, +- 0.019512873142957687, -0.01219360064715147, 0.01873214729130268, +- 0.08420958369970322, 0.017481638118624687, -0.04416817054152489, +- 0.015729544684290886, 0.018113337457180023 }, { -0.0029804545920342207, +- -0.004589577671140432, 0.011460436508059502, -0.006201551295816898, +- 0.01042337715625763, 0.06430846452713013, 0.010064974427223206, +- -0.05466027185320854, 0.008933083154261112, 0.010309524834156036 }, { +- 0.004409444518387318, -0.011415109038352966, 0.01625971682369709, +- -0.009612435474991798, 0.01705070026218891, 0.03511267527937889, +- 0.015850914642214775, -0.03921475261449814, 0.015634892508387566, +- 0.015604815445840359 }, { 0.002096987795084715, 0.0020931647159159184, +- 0.007141382899135351, -0.001340491813607514, 0.009163721464574337, +- 0.02456776425242424, 0.0063947574235498905, -0.03222661837935448, +- 0.006288853473961353, 0.0050973747856915 }, { -0.0034607239067554474, +- 0.0031061512418091297, 0.007416469044983387, 0.0017525834264233708, +- 0.008035082370042801, 0.01825927197933197, 0.0045385961420834064, +- -0.04628309607505798, 0.0050338017754256725, 0.0031098295003175735 }, { +- 0.013295140117406845, -0.003324859542772174, 0.025390343740582466, +- -0.003924305085092783, 0.024210220202803612, 0.12190306931734085, +- 0.0170985609292984, -0.03308294713497162, 0.02225038781762123, +- 0.018125852569937706 }, { -0.0016678980318829417, -0.009337554685771465, +- 0.019568176940083504, -0.012291750870645046, 0.0189657062292099, +- 0.07632237672805786, 0.017948711290955544, -0.037182725965976715, +- 0.017455358058214188, 0.017933927476406097 }, { 0.00018495075346436352, +- -0.0010653747012838721, 0.022742940112948418, -0.0031456167344003916, +- 0.02321663126349449, 0.028897395357489586, 0.02606729231774807, +- -0.07048308104276657, 0.024645140394568443, 0.016696888953447342 }, { +- -0.001072198385372758, -0.0007396471919491887, 0.005770928226411343, +- 0.0016552715096622705, 0.012147405184805393, 0.01228863000869751, +- 0.008740259334445, -0.03436771035194397, 0.010297732427716255, +- 0.00726251071318984 }, { 0.0010710692731663585, 0.0055290148593485355, +- 0.006929269526153803, 0.005556853488087654, 0.008267863653600216, +- 0.01496707834303379, 0.0055410549975931644, -0.0410819835960865, +- 0.004156499169766903, 0.005045691505074501 }, { -0.0021238874178379774, +- 0.002472770633175969, 0.006283507216721773, 0.0013875107979401946, +- 0.007113832980394363, 0.049724988639354706, 0.006209261249750853, +- -0.020033443346619606, 0.004309189040213823, 0.006459884811192751 }, { +- -0.0007124415715225041, -0.0051717753522098064, 0.019124837592244148, +- -0.008177341893315315, 0.016783403232693672, 0.04223710671067238, +- 0.016251731663942337, -0.027300149202346802, 0.016289258375763893, +- 0.016962839290499687 }, { 0.0002419629308860749, 0.005653316155076027, +- 0.004878369625657797, 0.004544268362224102, 0.004747139755636454, +- 0.03066272847354412, 0.0033675977028906345, -0.05165494233369827, +- 0.004021954722702503, 0.0050241099670529366 }, { -0.002074574586004019, +- -0.006678820587694645, 0.013416794128715992, -0.008097994141280651, +- 0.012509034015238285, 0.056994061917066574, 0.01235214900225401, +- -0.025026755407452583, 0.010967383161187172, 0.012180999852716923 }, { +- -0.00021187332458794117, 0.009081171825528145, 0.007899428717792034, +- 0.010133710689842701, 0.012805720791220665, 0.023471936583518982, +- 0.007391876075416803, -0.03661924600601196, 0.01448797807097435, +- 0.004828270059078932 }, { 0.001572385081090033, -0.025635521858930588, +- 0.03550581634044647, -0.026116903871297836, 0.037323515862226486, +- 0.08619633316993713, 0.034845370799303055, -0.04712314531207085, +- 0.03374631330370903, 0.03381635621190071 }, { -0.0030969076324254274, +- 0.0004503595409914851, 0.003994464408606291, 0.003491645911708474, +- 0.004688626155257225, 0.013733210042119026, 0.0038224176969379187, +- -0.03556334972381592, 0.0028728488832712173, 0.0029469686560332775 }, { +- 0.0016221802216023207, -0.003193127689883113, 0.011608647182583809, +- -0.005559929180890322, 0.011751323938369751, 0.03608258068561554, +- 0.01324139628559351, -0.06177749112248421, 0.010341217741370201, +- 0.007959919981658459 }, { -0.0056099239736795425, 0.00918140634894371, +- -0.00039480862324126065, 0.0061620320193469524, 0.0016795031260699034, +- 0.03773985803127289, -0.0009877195116132498, -0.03919967636466026, +- -0.0007461411296389997, -0.0016115568578243256 }, { -0.004253915976732969, +- 0.009847749955952168, 0.0019563166424632072, 0.008787315338850021, +- 0.0008667649817653, 0.0035813958384096622, -0.0017758094472810626, +- -0.06278850138187408, 0.00138835737016052, -0.0003129424585495144 }, { +- -0.17939598858356476, -0.27844536304473877, 0.5212129950523376, +- -0.2902967631816864, 0.5039370656013489, -0.7545657753944397, +- 0.4726392924785614, 0.2642815411090851, 0.4239436089992523, +- 0.46291080117225647 }, { 0.48849835991859436, -0.25973615050315857, +- 0.4468657374382019, -0.2647654116153717, 0.41828009486198425, +- -0.7162700891494751, 0.41093844175338745, 0.10013245046138763, +- 0.36440014839172363, 0.40974563360214233 }, { -0.10971822589635849, +- -0.2990286350250244, 0.5397084355354309, -0.3039056360721588, +- 0.5164146423339844, -0.6814507842063904, 0.4951390027999878, +- 0.18886838853359222, 0.4410306215286255, 0.48860231041908264 }, { +- -0.09862486273050308, -0.24673829972743988, 0.5097599029541016, +- -0.2972269356250763, 0.5496587753295898, -0.6951537728309631, +- 0.5087418556213379, 0.4584609568119049, 0.444987952709198, 0.4812290668487549 +- }, { -0.04779630899429321, -0.1991073191165924, 0.5410584211349487, +- -0.27704566717147827, 0.5572314262390137, -0.7326679229736328, +- 0.47875553369522095, 0.6717023253440857, 0.42449116706848145, +- 0.4990275800228119 }, { 0.23070257902145386, -0.2908032536506653, +- 0.5089505910873413, -0.31453976035118103, 0.5173734426498413, +- -0.569523274898529, 0.4894070327281952, 0.16679716110229492, +- 0.4274459779262543, 0.4572615623474121 }, { -0.11214735358953476, +- -0.2869555652141571, 0.48671823740005493, -0.292818158864975, +- 0.47493404150009155, -0.7705704569816589, 0.45869484543800354, +- 0.16960425674915314, 0.40393802523612976, 0.45179077982902527 }, { +- -0.22548624873161316, -0.2387329339981079, 0.5436649918556213, +- -0.30128592252731323, 0.5553328990936279, -0.7291233539581299, +- 0.4743258059024811, 0.3924575448036194, 0.46738868951797485, +- 0.4971018433570862 }, { -0.14675763249397278, -0.2945316433906555, +- 0.5314000248908997, -0.29357045888900757, 0.5400947332382202, +- -0.6586063504219055, 0.49865207076072693, 0.25670352578163147, +- 0.43785858154296875, 0.5069266557693481 }, { 0.4656718373298645, +- -0.2801032066345215, 0.47092965245246887, -0.28259119391441345, +- 0.43876153230667114, -0.6427084803581238, 0.4324922561645508, +- 0.12260568886995316, 0.3862890601158142, 0.4358588755130768 }, { +- -0.2243649661540985, -0.2728715240955353, 0.5265766382217407, +- -0.3175083100795746, 0.5416776537895203, -0.6470391750335693, +- 0.5048414468765259, 0.3777300715446472, 0.4433270990848541, 0.5132767558097839 +- }, { 0.42592954635620117, -0.2556743621826172, 0.4496026337146759, +- -0.2635961174964905, 0.4339139461517334, -0.6852652430534363, +- 0.4187034070491791, 0.1403844654560089, 0.37034907937049866, +- 0.40996524691581726 }, { 0.006349429953843355, 0.008758490905165672, +- 0.0024025600869208574, 0.0074418410658836365, 0.0068122586235404015, +- 0.004567309282720089, 0.0041856649331748486, -0.03593219816684723, +- 0.005340238101780415, 0.007625584956258535 }, { 0.00992648396641016, +- 0.027865136042237282, -0.013052484951913357, 0.03475460037589073, +- -0.009106453508138657, -0.059478554874658585, -0.012555109336972237, +- -0.04875533655285835, -0.013867026194930077, -0.016776222735643387 }, { +- -0.00919875968247652, 0.0077290842309594154, 0.0029145595617592335, +- 0.006386174354702234, 0.0050420681945979595, 0.035884469747543335, +- 0.0023514979984611273, -0.026723366230726242, 0.0033713094890117645, +- 0.0022858516313135624 }, { 0.003402372822165489, -0.0050055356696248055, +- 0.010922269895672798, -0.004661293234676123, 0.010839125141501427, +- 0.07475539296865463, 0.010319668799638748, -0.049569301307201385, +- 0.008867098018527031, 0.010002100840210915 }, { 0.0034308244939893484, +- -0.00010209958418272436, 0.009724345058202744, -0.0040523274801671505, +- 0.009339124895632267, 0.0765046626329422, 0.008690908551216125, +- -0.05033581331372261, 0.007238353602588177, 0.008703971281647682 }, { +- 0.00016939315537456423, 0.0036689804401248693, 0.005852220114320517, +- 0.003966270945966244, 0.007253644056618214, 0.012695081532001495, +- 0.004334104247391224, -0.0486932173371315, 0.005126115400344133, +- 0.001781611004844308 }, { 0.00617396691814065, 0.01146733295172453, +- -0.005833564791828394, 0.011826511472463608, -0.00216634850949049, +- -0.02159707434475422, -0.0027919351123273373, -0.06520309299230576, +- -0.0035587691236287355, -0.007957630790770054 }, { 0.0008262136834673584, +- 0.004355533979833126, 0.0018761655082926154, 0.005150128155946732, +- 0.002988312393426895, 0.01281831320375204, 0.0016794330440461636, +- -0.0390508808195591, 0.0013916873140260577, 0.0001941779482876882 }, { +- -0.00004851054472965188, -0.01201767660677433, 0.024829553440213203, +- -0.008297210559248924, 0.026100557297468185, 0.043741077184677124, +- 0.02266228385269642, -0.048086125403642654, 0.02307322807610035, +- 0.02412465587258339 }, { -0.0011748506221920252, 0.0014962955610826612, +- 0.006020717788487673, 0.0034769370686262846, 0.010606036521494389, +- 0.031417302787303925, 0.005139397922903299, -0.04918951168656349, +- 0.006018136627972126, 0.00755356065928936 }, { 0.00022752655786462128, +- -0.0024958455469459295, 0.0104898102581501, -0.0035057987552136183, +- 0.01983889751136303, 0.06993480771780014, 0.01040184311568737, +- -0.054011836647987366, 0.00848767627030611, 0.009434795938432217 }, { +- -0.01137248333543539, 0.019169019535183907, -0.005699540488421917, +- 0.020259737968444824, -0.009858974255621433, -0.05176264047622681, +- -0.012346201576292515, -0.0015207919059321284, -0.010896929539740086, +- -0.013632716611027718 }, { 0.019687844440340996, -0.016997911036014557, +- 0.04152873158454895, -0.01771809533238411, 0.04080713540315628, +- 0.198679119348526, 0.04447152093052864, -0.05112702026963234, +- 0.03946048393845558, 0.03607768565416336 }, { 0.00005108291588840075, +- -0.007710771635174751, 0.01803247258067131, -0.0069360146299004555, +- 0.019745295867323875, 0.056591834872961044, 0.014080281369388103, +- -0.05748939514160156, 0.015569544397294521, 0.015056119300425053 }, { +- 0.0063628265634179115, -0.005078371148556471, 0.020405638962984085, +- -0.00865379348397255, 0.02197723090648651, 0.06078439950942993, +- 0.022219959646463394, -0.05925067141652107, 0.01677227020263672, +- 0.017973273992538452 }, { 0.002265959745272994, -0.0022392929531633854, +- 0.013171348720788956, -0.0039203274063766, 0.014817837625741959, +- 0.02926689386367798, 0.011932063847780228, -0.047607481479644775, +- 0.010706663131713867, 0.011735614389181137 }, { 0.0014648373471572995, +- 0.0036698412150144577, 0.007382333278656006, 0.004113280214369297, +- 0.008649623952805996, 0.045254651457071304, 0.0027535564731806517, +- -0.06927894800901413, 0.006338451988995075, 0.007019430864602327 }, { +- 0.00044056089245714247, -0.010283137671649456, 0.01905573159456253, +- -0.013132072985172272, 0.01779758930206299, 0.06923307478427887, +- 0.0174750704318285, -0.04245254769921303, 0.01608343794941902, +- 0.01723497360944748 }, { -0.00034060439793393016, 0.0032564448192715645, +- 0.011709870770573616, -0.0013793004909530282, 0.012723761610686779, +- 0.04533017426729202, 0.010787508450448513, -0.05386055260896683, +- 0.009837791323661804, 0.007716013118624687 }, { 0.006440089549869299, +- -0.004935078322887421, 0.016215354204177856, -0.005261401180177927, +- 0.013468231074512005, 0.04748852923512459, 0.01386487577110529, +- -0.023055989295244217, 0.01248460728675127, 0.012813093140721321 }, { +- 0.0024100805167108774, 0.003949601203203201, 0.013426079414784908, +- 0.003418722888454795, 0.009464995004236698, 0.00673643359914422, +- 0.007718656212091446, -0.0340745709836483, 0.005984948016703129, +- 0.004742426332086325 }, { -0.0006950328242965043, -0.009912993758916855, +- 0.01731671206653118, -0.009677963331341743, 0.017950914800167084, +- 0.04394759237766266, 0.015702784061431885, -0.039462897926568985, +- 0.014503180049359798, 0.015180781483650208 }, { -0.12449537962675095, +- -0.2796618640422821, 0.5373491644859314, -0.3040497601032257, +- 0.548844039440155, -0.6404352188110352, 0.5001888275146484, +- 0.3734420835971832, 0.45425084233283997, 0.5000465512275696 }, { +- 0.4505344033241272, -0.2586415410041809, 0.44793030619621277, +- -0.2633204162120819, 0.43474504351615906, -0.7237627506256104, +- 0.4105754792690277, 0.1324867159128189, 0.3643965423107147, 0.407732218503952 +- }, { 0.3970632255077362, -0.3023785352706909, 0.48028749227523804, +- -0.30594387650489807, 0.45442909002304077, -0.5986360907554626, +- 0.44578656554222107, 0.05206897482275963, 0.39808422327041626, +- 0.4438410699367523 }, { 0.45537206530570984, -0.27713721990585327, +- 0.47015684843063354, -0.2807360291481018, 0.44025346636772156, +- -0.6490985155105591, 0.4320252239704132, 0.09027732908725739, +- 0.3839702606201172, 0.429051011800766 }, { 0.3831164836883545, +- -0.2999071180820465, 0.47874411940574646, -0.3052879571914673, +- 0.45563340187072754, -0.6477287411689758, 0.4443303644657135, +- 0.05708439648151398, 0.3995322585105896, 0.44064900279045105 }, { +- -0.16495628654956818, -0.2562197148799896, 0.514265775680542, +- -0.27941155433654785, 0.5780549645423889, -0.7235912680625916, +- 0.49422237277030945, 0.699141800403595, 0.41885998845100403, +- 0.45819440484046936 }, { -0.24704083800315857, -0.22255827486515045, +- 0.5046676993370056, -0.31884849071502686, 0.5316600799560547, +- -0.7154296636581421, 0.5253787040710449, 0.5879004001617432, +- 0.42596113681793213, 0.4891957640647888 }, { 0.44627609848976135, +- -0.2649291753768921, 0.47296538949012756, -0.2700652480125427, +- 0.4447115361690521, -0.6644209623336792, 0.43864351511001587, +- 0.13057145476341248, 0.38153448700904846, 0.43287578225135803 }, { +- 0.44761377573013306, -0.27964404225349426, 0.47144728899002075, +- -0.2866366505622864, 0.45033419132232666, -0.6497937440872192, +- 0.4380679428577423, 0.1150546595454216, 0.391343355178833, 0.4289560616016388 +- }, { 0.4290846884250641, -0.25031933188438416, 0.42947667837142944, +- -0.2567079961299896, 0.43037256598472595, -0.7650051712989807, +- 0.4066895842552185, 0.20941932499408722, 0.3541245460510254, +- 0.40041694045066833 }, { 0.5199558138847351, -0.21980324387550354, +- 0.4631670415401459, -0.27651867270469666, 0.48013609647750854, +- -0.6284698843955994, 0.4315768778324127, 0.29421037435531616, +- 0.37770697474479675, 0.414880633354187 }, { 0.35179853439331055, +- -0.2159298062324524, 0.4537796080112457, -0.26939600706100464, +- 0.5043035745620728, -0.6542515754699707, 0.4901948571205139, +- 0.5454661846160889, 0.4187948405742645, 0.42518454790115356 }, { +- -0.010106919333338737, 0.004475359804928303, 0.011831885203719139, +- 0.0023347740061581135, 0.01412924937903881, 0.04035996273159981, +- 0.01116417907178402, -0.05056631565093994, 0.012054730206727982, +- 0.006604544352740049 }, { 0.017707109451293945, 0.013513759709894657, +- -0.005782492458820343, 0.010859273374080658, -0.003893932094797492, +- 0.03987651318311691, -0.005789508577436209, -0.056447528302669525, +- -0.005746860057115555, -0.006472072098404169 }, { 0.003789577865973115, +- -0.005335954949259758, 0.012654170393943787, 0.0063898926600813866, +- 0.014640971086919308, 0.04621101915836334, 0.013488966971635818, +- -0.039636291563510895, 0.012730089947581291, 0.010913509875535965 }, { +- -0.0015369183383882046, 0.005471350625157356, 0.0061502521857619286, +- 0.0034285960718989372, 0.007057072129100561, 0.027003156021237373, +- 0.0068636247888207436, -0.03954155370593071, 0.004650274757295847, +- 0.0037534344010055065 }, { -0.0015425966121256351, -0.003100242465734482, +- 0.009736061096191406, 0.0009204444359056652, 0.010626270435750484, +- 0.04256312549114227, 0.008730685338377953, -0.043148912489414215, +- 0.007481274660676718, 0.007902776822447777 }, { -0.006916466634720564, +- 0.010189691558480263, 0.015274337492883205, 0.022279955446720123, +- 0.01549245323985815, 0.05858184024691582, 0.006524320226162672, +- -0.03847585245966911, 0.004579894710332155, 0.01218155212700367 }, { +- -0.0059191943146288395, 0.011880015023052692, -0.0015437973197549582, +- 0.00954253040254116, -0.0016970221186056733, -0.022822292521595955, +- -0.00408344529569149, -0.07529190927743912, -0.0034426965285092592, +- -0.004027289338409901 }, { 0.002155002672225237, -0.006135761737823486, +- 0.015392973087728024, -0.00586795574054122, 0.014701003208756447, +- 0.05463358014822006, 0.013828868046402931, -0.03820426017045975, +- 0.012749378569424152, 0.014512996189296246 }, { 0.007013010326772928, +- -0.018321361392736435, 0.027655690908432007, -0.02128089964389801, +- 0.027056558057665825, 0.08393879979848862, 0.026643965393304825, +- -0.031438592821359634, 0.025179145857691765, 0.026807432994246483 }, { +- -0.009060818701982498, -0.014637550339102745, 0.0245140939950943, +- -0.014633459970355034, 0.026211651042103767, 0.07277548313140869, +- 0.023219900205731392, -0.06044448912143707, 0.02219962514936924, +- 0.02255772426724434 }, { 0.0009890473447740078, 0.0025689988397061825, +- 0.0029933019541203976, 0.0026871562004089355, 0.004153409041464329, +- -0.014231826178729534, 0.0028683433774858713, -0.05255261808633804, +- 0.0018601162591949105, 0.0037203743122518063 }, { 0.014783969148993492, +- -0.030348604544997215, 0.048568908125162125, -0.03418368101119995, +- 0.045904822647571564, 0.21774326264858246, 0.0468745194375515, +- -0.025057200342416763, 0.04436303302645683, 0.04256070405244827 }, { +- -0.001338872709311545, -0.001099711051210761, 0.011798196472227573, +- -0.0010271434439346194, 0.011257215403020382, 0.055040646344423294, +- 0.006571569014340639, -0.029846491292119026, 0.009083450771868229, +- 0.008606307208538055 }, { -0.0025206380523741245, 0.03125658258795738, +- -0.017243361100554466, 0.03216329589486122, -0.010740770027041435, +- -0.05781843513250351, -0.017374781891703606, -0.04399864003062248, +- -0.016367197036743164, -0.02207805961370468 }, { -0.008935511112213135, +- 0.01588786020874977, -0.005458052270114422, 0.015447932295501232, +- 0.0002479049435351044, 0.016621612012386322, -0.0034153328742831945, +- -0.05701618641614914, -0.001652541453950107, -0.006199870724231005 }, { +- 0.0018021129071712494, -0.004010784439742565, 0.010857501067221165, +- -0.006167122162878513, 0.01268286444246769, 0.04609864205121994, +- 0.01222320832312107, -0.03699537366628647, 0.011207222007215023, +- 0.009706465527415276 }, { -0.003358280286192894, 0.0069209677167236805, +- 0.003744186367839575, 0.000022598034775000997, 0.007120033726096153, +- 0.02481597475707531, 0.0039831786416471004, -0.056816235184669495, +- 0.003359674010425806, 0.004114761017262936 }, { -0.030529499053955078, +- 0.034363728016614914, -0.015255279839038849, 0.031067367643117905, +- -0.017316114157438278, -0.08005756884813309, -0.020900147035717964, +- -0.017726922407746315, -0.021875783801078796, -0.02447391115128994 }, { +- 0.00013344670878723264, -0.004767694044858217, 0.019656620919704437, +- -0.004764625336974859, 0.021222280338406563, 0.09208859503269196, +- 0.02049538679420948, -0.022420845925807953, 0.01786002889275551, +- 0.01997714303433895 }, { 0.00014631978410761803, 0.008409316651523113, +- 0.0012459810823202133, 0.0057202852331101894, 0.005349485669285059, +- 0.0013218697858974338, 0.001785722910426557, -0.04473695531487465, +- 0.005110645201057196, 0.001506762346252799 }, { -0.002060048282146454, +- -0.0010022168280556798, 0.007520562037825584, -0.0009703372488729656, +- 0.006972051225602627, 0.06452539563179016, 0.0065904115326702595, +- -0.028183946385979652, 0.005169377662241459, 0.0067565166391432285 }, { +- -0.005371135659515858, -0.015999047085642815, 0.029263461008667946, +- -0.019694972783327103, 0.029128270223736763, 0.04808508977293968, +- 0.028227513656020164, -0.04525603726506233, 0.026467733085155487, +- 0.02688291110098362 }, { 0.3956967294216156, -0.30071401596069336, +- 0.4752607047557831, -0.3055664598941803, 0.45281344652175903, +- -0.6206023097038269, 0.44189783930778503, 0.06217087060213089, +- 0.39544960856437683, 0.43739572167396545 }, { -0.21993833780288696, +- -0.21602937579154968, 0.5010220408439636, -0.24192959070205688, +- 0.5627127885818481, -0.7517757415771484, 0.47989609837532043, +- 0.5445677042007446, 0.4359857141971588, 0.4769592881202698 }, { +- -0.293827623128891, -0.22659969329833984, 0.5350731611251831, +- -0.24201250076293945, 0.5874162912368774, -0.6911905407905579, +- 0.4974977374076843, 0.5962449312210083, 0.4659028947353363, 0.4858441948890686 +- }, { -0.13362209498882294, -0.20075586438179016, 0.5073968172073364, +- -0.21574439108371735, 0.5423585176467896, -0.8226967453956604, +- 0.4961773157119751, 0.5814898014068604, 0.42381197214126587, +- 0.40555694699287415 }, { 0.3945615291595459, -0.30794617533683777, +- 0.4858875870704651, -0.3100610077381134, 0.45987027883529663, +- -0.5704949498176575, 0.45145004987716675, 0.03575756400823593, +- 0.4044911563396454, 0.4486754536628723 }, { 0.3881177306175232, +- -0.2975883185863495, 0.48459023237228394, -0.30436834692955017, +- 0.4621533453464508, -0.634148120880127, 0.44884735345840454, +- 0.06066117435693741, 0.39942702651023865, 0.442918062210083 }, { +- 0.4598709046840668, -0.2672586739063263, 0.48327597975730896, +- -0.2713889479637146, 0.4720040559768677, -0.6245943903923035, +- 0.4516040086746216, 0.23443907499313354, 0.3861783742904663, +- 0.42826351523399353 }, { 0.48248040676116943, -0.23114001750946045, +- 0.4572550654411316, -0.25944364070892334, 0.4942852854728699, +- -0.6639610528945923, 0.43984267115592957, 0.3424535095691681, +- 0.4004361927509308, 0.41089051961898804 }, { 0.38496607542037964, +- -0.3036200702190399, 0.47444915771484375, -0.3070754408836365, +- 0.45291975140571594, -0.6247242093086243, 0.4432004690170288, +- 0.04626859724521637, 0.39626365900039673, 0.4387483298778534 }, { +- 0.16463077068328857, -0.23566898703575134, 0.449471652507782, +- -0.2461680769920349, 0.5223954319953918, -0.7245633006095886, +- 0.4709729552268982, 0.6249322295188904, 0.38184911012649536, +- 0.4182289242744446 }, { -0.20520466566085815, -0.21954932808876038, +- 0.5382333397865295, -0.2711770236492157, 0.5803769826889038, +- -0.6825782060623169, 0.5150359272956848, 0.5594297647476196, +- 0.4470074474811554, 0.4280882477760315 }, { -0.2052452564239502, +- -0.2987375557422638, 0.5244084000587463, -0.30783239006996155, +- 0.5058938264846802, -0.7263019680976868, 0.4874771237373352, +- 0.22786666452884674, 0.4228363335132599, 0.4780672490596771 }, { +- 0.006423501297831535, -0.00790311861783266, 0.012774823233485222, +- -0.009273654781281948, 0.012359986081719398, 0.05843383073806763, +- 0.011961334384977818, -0.03883614391088486, 0.011078624986112118, +- 0.01173930149525404 }, { 0.0014385083923116326, -0.0044732727110385895, +- 0.015440189279615879, -0.0061906492337584496, 0.014422492124140263, +- 0.06662198901176453, 0.014233109541237354, -0.05597100034356117, +- 0.0131152905523777, 0.013865616172552109 }, { 0.009637736715376377, +- 0.030058089643716812, -0.007750398945063353, 0.031803008168935776, +- -0.00718939583748579, -0.07411515712738037, -0.008559634909033775, +- -0.019085107371211052, -0.011364089325070381, -0.013287690468132496 }, { +- 0.004903716500848532, -0.002202908741310239, 0.0075493259355425835, +- -0.002605787478387356, 0.009029371663928032, 0.043410398066043854, +- 0.007343624718487263, -0.03680622950196266, 0.007066784892231226, +- 0.007688944693654776 }, { 0.009564908221364021, 0.05481602996587753, +- -0.016788091510534286, 0.05179797485470772, -0.026091543957591057, +- -0.13783486187458038, -0.027153389528393745, -0.01847674511373043, +- -0.026059303432703018, -0.030188512057065964 }, { 0.0016651751939207315, +- -0.005048097111284733, 0.01250064093619585, -0.005707516800612211, +- 0.012914878316223621, 0.05107787251472473, 0.012088499963283539, +- -0.04519118368625641, 0.011483707465231419, 0.011719024740159512 }, { +- 0.0006001264555379748, 0.0013837899314239621, 0.006818774621933699, +- 0.0033755304757505655, 0.006554333493113518, -0.00003665871554403566, +- 0.005578219890594482, -0.04432947188615799, 0.005049873609095812, +- 0.004962805658578873 }, { 0.004503821488469839, -0.003093608422204852, +- 0.014094601385295391, 0.0041489084251224995, 0.012278278358280659, +- 0.06518924981355667, 0.011578143574297428, -0.04368652030825615, +- 0.010912473313510418, 0.0120044881477952 }, { -0.0037537268362939358, +- -0.0002755214227363467, 0.00618172250688076, -0.002148724626749754, +- 0.007708885241299868, 0.029431177303195, 0.005798679776489735, +- -0.05353694036602974, 0.006208398379385471, 0.005658458918333054 }, { +- 0.0019448136445134878, -0.0026976277586072683, 0.00876267533749342, +- -0.0000970091568888165, 0.012022394686937332, 0.05288229137659073, +- 0.009998875670135021, -0.05675194412469864, 0.011053116992115974, +- 0.008077314123511314 }, { 0.003996753599494696, 0.00024642248172312975, +- 0.014732210896909237, 0.0005698638851754367, 0.012556754052639008, +- 0.054665401577949524, 0.010451117530465126, -0.06310147047042847, +- 0.006954568438231945, 0.009528204798698425 }, { 0.0058280099183321, +- -0.00258102104999125, 0.010313651524484158, -0.0038629730697721243, +- 0.012018734589219093, 0.04469588026404381, 0.011624212376773357, +- -0.032834507524967194, 0.009230989962816238, 0.00894859153777361 }, { +- -0.003170835319906473, 0.029248306527733803, 0.012852590531110764, +- 0.024345608428120613, 0.009399308823049068, 0.02461123652756214, +- 0.007300689350813627, -0.04759199172258377, 0.008626566268503666, +- -0.0007201972766779363 }, { 0.02862769365310669, 0.04148479178547859, +- 0.0012835917295888066, 0.06216878443956375, 0.014748645946383476, +- -0.01936311088502407, 0.011281806044280529, -0.011410423554480076, +- -0.00031697427039034665, -0.0014610219513997436 }, { 0.0190869253128767, +- -0.013382898643612862, 0.03353014215826988, -0.014998090453445911, +- 0.035997822880744934, 0.14594082534313202, 0.03475242480635643, +- -0.029255840927362442, 0.033868107944726944, 0.02671659365296364 }, { +- -0.001192377065308392, -0.0027634012512862682, 0.015599570237100124, +- -0.004747576545923948, 0.01570502482354641, 0.04308212921023369, +- 0.013200322166085243, -0.035211093723773956, 0.012427289970219135, +- 0.009146512486040592 }, { 0.0033591173123568296, 0.01675717532634735, +- 0.0016154601471498609, 0.01687380112707615, 0.0033041187562048435, +- -0.01991080492734909, -0.0004345179768279195, -0.03294739872217178, +- -0.0019367106724530458, -0.004603420849889517 }, { 0.0035828121472150087, +- -0.005195597652345896, 0.013006294146180153, -0.005307102110236883, +- 0.012347385287284851, 0.055735498666763306, 0.011431930586695671, +- -0.03440462052822113, 0.010198966600000858, 0.010713182389736176 }, { +- -0.0025030390825122595, 0.013939392752945423, -0.005901286844164133, +- 0.01259881816804409, -0.005060490220785141, 0.022167406976222992, +- -0.004270714707672596, -0.050579238682985306, -0.006613144651055336, +- -0.008481571450829506 }, { 0.004870634060353041, -0.0026161016430705786, +- 0.01613251492381096, -0.0059494515880942345, 0.019811052829027176, +- 0.08013129979372025, 0.015556958504021168, -0.05195259302854538, +- 0.015604502521455288, 0.014967973344027996 }, { 0.04881030321121216, +- 0.021579794585704803, -0.014629039913415909, 0.020981132984161377, +- -0.014194115065038204, -0.02374698780477047, -0.014806374907493591, +- -0.02924659475684166, -0.016147935763001442, -0.017149971798062325 }, { +- -0.0021160561591386795, -0.004959597252309322, 0.01772688701748848, +- -0.0046059261076152325, 0.015790479257702827, 0.06731697916984558, +- 0.015668079257011414, -0.03320039063692093, 0.013252184726297855, +- 0.013839546591043472 }, { 0.941993772983551, -1.5817229747772217, +- 2.0343267917633057, -1.6535866260528564, 2.232057809829712, +- -1.278427004814148, 2.059631109237671, -2.335737705230713, 1.9325844049453735, +- 1.9331965446472168 }, { 0.9436568021774292, -1.5791032314300537, +- 2.0879499912261963, -1.4951168298721313, 2.2531628608703613, +- -1.2294822931289673, 2.0839295387268066, -2.4484803676605225, +- 1.9551223516464233, 1.9740538597106934 }, { -0.22980402410030365, +- 0.9199262261390686, -1.3931411504745483, 1.0378587245941162, +- -1.7050687074661255, 4.863880964656175e-10, -1.5698068141937256, +- 0.9223605394363403, -1.335978627204895, -1.3027349710464478 }, { +- -0.3382071256637573, 0.9860741496086121, -1.6543004512786865, +- 1.0869961977005005, -1.7495276927947998, 0.1413516104221344, +- -1.6589244604110718, 0.9568116068840027, -1.2587254047393799, +- -1.316787838935852 }, { -0.08973576128482819, 1.0692839622497559, +- -1.7261924743652344, 1.1258662939071655, -2.048819065093994, +- 8.524442957069667e-11, -1.707221269607544, 0.9763411283493042, +- -1.3384802341461182, -1.5099725723266602 }, { -0.23119568824768066, +- 1.1025586128234863, -1.7083741426467896, 1.1952905654907227, +- -2.0976247787475586, 4.2183725262878724e-11, -1.6553181409835815, +- 1.043709397315979, -1.353306770324707, -1.5630202293395996 }, { +- -0.2149028778076172, 0.9034178853034973, -1.4059020280838013, +- 1.0857110023498535, -1.7785764932632446, 6.785310091572683e-10, +- -1.5982911586761475, 0.9385444521903992, -1.102957844734192, +- -1.2764172554016113 }, { -0.3442114591598511, 0.7093265652656555, +- -1.3323750495910645, 0.7892476320266724, -1.4307119846343994, +- 1.2957205308694597e-9, -1.2221505641937256, 0.7406548261642456, +- -1.0390063524246216, -1.0610798597335815 }, { -0.3801349401473999, +- 0.8477363586425781, -1.5809834003448486, 0.9098098874092102, +- -1.6288162469863892, 1.2674961080705316e-9, -1.4910979270935059, +- 0.8300457000732422, -1.1994421482086182, -1.3756306171417236 }, { +- -0.4026913046836853, 0.9909816980361938, -1.6537928581237793, +- 1.0298457145690918, -1.8706167936325073, 6.557077797397781e-11, +- -1.4875438213348389, 0.9636037945747375, -1.3140361309051514, +- -1.321130633354187 } +-}; +- +-static float intercepts[10] = { +- -0.9423781037330627, +- 2.3425614833831787, +- -1.880435824394226, +- 2.760684013366699, +- -2.1556267738342285, +- 1.9289884567260742, +- -1.943122386932373, +- 1.8524644374847412, +- -1.8369754552841187, +- -1.778485655784607 +-}; +- +-static float intercepts1[1] = { +- -0.9438958168029785 +-}; +- +-static float coefficient1[10] = { +- 4.518650531768799, +- -3.7139227390289307, +- 5.691276550292969, +- -3.7503113746643066, +- 5.436307430267334, +- -6.737155914306641, +- 5.318873405456543, +- -4.013023376464844, +- 4.771977424621582, +- 5.26296329498291 +-}; +- +-static float offset[6] = { +- 205.08018493652344, +- 10.790425300598145, +- 10.719943046569824, +- 183.248779296875, +- 2656.49658203125, +- 12.877049446105957 +-}; +- +-static float scale[6] = { +- 0.006835445761680603, +- 0.08650556206703186, +- 0.08758283406496048, +- 0.005654274486005306, +- 0.00008135510142892599, +- 0.10635864734649658 +-}; +- +-static float unity[1] = { +- 1.0 +-}; +- +-static char cats_strings[34][128] = { +- "ea75dbfdc4cd33ab059a923fd2b87d67c250a61e8ae64ff03001eb26b4a3c5f1", +- "232d26a984e4c87bf91264837eedc6b5a4a1444a93349be0ac371de77d0d8426", +- "db8c6ac43cd10145ceeb8463a1a3920697e85e7b554bd9f13c58b0b549db7af0", +- "327fa6f438c4bf9d4e01ce8230bd8c36d3f07b1d9d1b1f93ec38c8ebb0747a71", +- "5ed9c9e5e1107cde3924375a33c1250a951395b59d8472f08ac7978b6fc7ce82", +- "32a989965f70a73aa993e76f15344ec10a73ef65c32f47e16825465b246d0730", +- "6c6627b9bede7234a142d117e88d34ad04c9672a27b8fccd122a9442ad218e15", +- "d049e0300c22bf3f4bac34ad19e9b2ef00d433b0717b094bc6657ac94670f3c0", +- "f321a0cb393597da1f2e24598aa3adcb545b24a5fbaac9df87015f30aabded6e", +- "c0fabdb9eea8a2353102665eaa3211e95d3c5826fcc44bb67fc8c5fcd3503d35", +- "24801cbd57aaf8f01a2c5782d8ebeb26e382228eebdd904c9b78562defdad953", +- "6b4b7a9314efb703ba53a8316d02cd0e3408aad93760cc88b0d9b9bdd5891a1a", +- "e34b6f1266cf79b5e6ed49b1038dbe26f8da6d09bb556143ca2e4fb0a4ad065c", +- "ab3e680b0f70d8bb2df694b55c6d78e11dc82973ad20d5f376f30ee8d4a11a0a", +- "cfcaf0d89b776cab25e88e3f034a4993b16d78be249bc23af7bbb8751c7e350f", +- "cc912327aa7f363442b317507d27e4b8133989dc49e9f06c049b794a3bc0dc37", +- "819901d92b3deeb7adfd5b03b3620883abfd4cbe63a34fded376047842b78cda", +- "1763e99cd0ad99710d516d21f150cdd96da78d4a5d735d4061bb54b136f3517c", +- "26a65d430b9e68fae421cac7a9d5de333f79185b3c1db4e9d59cd501f0fd6a51", +- "0c3603e13e24a40b4bf215e3795a9a40d60a8456fb7b63c2d11e81701a231e85", +- "afd4b5db81abc1dd56d271732ed0245f8691bdc5dad3476d6f6356077b1aa1d6", +- "34a97c4109b59b73547f940c1dc8d21c0f187388f2d3a8eecfee7fd867902fd4", +- "ac31ba1c9fdf51baa2a437bfc092a1f732e2a8f066763742b0770a6e26e32f2b", +- "e5e47a373094f3f9b4188c843d5c4bea5a2a4ac4bbbdf7fac7ce39b206257377", +- "15410f23db97054b6eb270efccfb7cb4176f8268142cea16e28b21dc9598700e", +- "f1db8e9ad44e7e1841b14b125ccf35a17e8cffa8f06823bd46cf856e4af263b8", +- "aaf968ba850b4d9affbec493fa0fe0a833e9e463e87a2cce0c8c08a13ccca4a2", +- "a4169b3e9a4c13fc94c7a67ab099ce92cc68ea47abaf1688ab8a17202f8da051", +- "3c5359d2750623f244164c9f45384ef06e75360a76d4dbd04f063f608cccc780", +- "f1f1286515914da97052674ad09e256fc598dccd4d385dad588b6dd2a03e6194", +- "ca98d874b31b6b0b1e2533bc1408d0d6a8bac0a161a691221748163ddcb12103", +- "b1a11736cb8ef5565bff055f3163253813c9c4161304ea3fce55ad805eec174f", +- "8d79ca67d207ac46fde3b25b6e1d51c4daeae308e2f3aa89fc9a24b7bb8d0656", +- "29272c47115b5a93be87e3a9d8739fb2e8a1fea6aa31852af190389734c51946" +-}; +- +-static char cats_strings2[10][128] = { +- "7a917c2fad1f46426a21f8ecb64a3afbb7a763f80b2190cae4c021e8bd6c71d2", +- "a18ffc9586713699b9fcb936777d28cf9d7a4214a134c52ab6346348ff9ff3e4", +- "deeabd69f5ef24e4e8a7bb8d07fdc8fbdd4ffe702cb1b2e9e45d1010a8b5b610", +- "b2019c9be6a0087ab26f42c5de10595515176eb576277946bf04434c05bde5b7", +- "b15e2f190ad03a8869c3939ab684966a16a06ed55c9afeca4104f3544edc5378", +- "f0cac05641df3d87852f8c07eb49b9070a383c2a89287d5a32593f251002fd6a", +- "d511711f8439e1ba7592ae8330e1d2d8eaf43962b6bd2f27ed9d49c35ee47381", +- "7fc38db11d3600963a728c164a01991a1d627d2b51b7a3d957060c64aa7ec88b", +- "1cea197e5be5bb4fc161c0853cb955a50cb4d63bcd8ef9a2322773c2098ef6dc", +- "c4911d13876783e2a86ab62427e976ad8edc439a011f0d01a4e8dd2c485e4886" +-}; +- +-static void +-matmul (const float *lhs, const float *rhs, int m, int k, int n, float *out) +-{ +- for (int i = 0; i < m; i++) +- { +- for (int j = 0; j < n; j++) +- { +- out[i * n + j] = 0.0f; +- for (int p = 0; p < k; p++) +- { +- out[i * n + j] += lhs[i * k + p] * rhs[p * n + j]; +- } +- } +- } +-} +- +-static void +-add (const float *lhs, const float *rhs, int length, float *out) +-{ +- for (int i = 0; i < length; i++) +- { +- out[i] = lhs[i] + rhs[i]; +- } +-} +- +-static void +-sub (const float *lhs, const float *rhs, int length, float *out) +-{ +- for (int i = 0; i < length; i++) +- { +- out[i] = lhs[i] - rhs[i]; +- } +-} +- +-static void +-sigmoid (const float *in, int length, float *out) +-{ +- for (int i = 0; i < length; i++) +- { +- out[i] = 1.0f / (1.0f + expf (-in[i])); +- } +-} +- +-static void +-relu (const float *data, int length, float *out) +-{ +- for (int i = 0; i < length; i++) +- { +- if (data[i] < 0) +- { +- out[i] = 0; +- } +- else +- { +- out[i] = data[i]; +- } +- } +-} +- +-static void +-line_concat (const float *in, int in_size, float *out, int out_size) +-{ +- for (int i = 0; i < in_size; i++) +- { +- out[out_size + i] = in[i]; +- } +-} +- +-static void +-one_hot_encoder (const char *in, const char (*cats)[128], float *out, +- int out_size) +-{ +- for (int i = 0; i < out_size; i++) +- { +- if (i < out_size && strcmp (cats[i], in) == 0) +- { +- out[i] = 1.0f; +- } +- else +- { +- out[i] = 0.0f; +- } +- } +-} +- +-static void +-imputer (const int64_t *in, int size, float *out) +-{ +- for (int i = 0; i < size; i++) +- { +- out[i] = in[i] * 1.0f; +- } +-} +- +-static void +-scaler (const float *in, const float *offset, const float *scale, int size, +- float *out) +-{ +- for (int i = 0; i < size; i++) +- { +- out[i] = (in[i] - offset[i]) * scale[i]; +- } +-} +- +-static int +-argmax (const float *in, int in_size) +-{ +- int out_idx = 0; +- for (int i = 0; i < in_size; i++) +- { +- if (in[i] > in[out_idx]) +- { +- out_idx = i; +- } +- } +- return out_idx; +-} +- +-static int +-compare_strings (const void *a, const void *b) +-{ +- const char *str_a = *(const char **)a; +- const char *str_b = *(const char **)b; +- +- int len = strlen (str_a); +- for (int i = 0; i < len; i++) +- { +- char c1 = str_a[i]; +- char c2 = str_b[i]; +- if (ISUPPER (c1) && !ISUPPER (c2)) +- { +- return 0; +- } +- else if (!ISUPPER (c1) && ISUPPER (c2)) +- { +- return 1; +- } +- else if (c1 != c2) +- { +- return c1 < c2; +- } +- } +- return strlen (str_a) > strlen (str_b); +-} +- +-static void +-truncate_prefix (const char *str, char *result) +-{ +- const char *underscore_pos = strchr (str, '_'); +- if (underscore_pos == NULL) +- { +- strcpy (result, str); +- return; +- } +- +- size_t len = underscore_pos - str; +- strncpy (result, str, len + 1); +- result[len + 1] = '\0'; +-} +- +-static void +-preprocess (int argc1, const char **argv1, const char *mops, +- int argc2, int64_t *argv2, char (*in_options)[1024], +- int64_t * in_modes) +-{ +- strcpy (in_options[0], mops); +- +- const char *output_option = "-o"; +- const char *macro_prefix = "-D"; +- const char *needle = "--param"; +- const char *flag_prefix = "-"; +- const char *default_option = "-fdefault-option"; +- const int default_int_val = 0; +- int m_size = 0; +- for (int i = 0; i < argc1; i++) +- { +- if (strncmp (argv1[i], macro_prefix, 2) == 0) +- { +- m_size++; +- } +- } +- char *m_options[m_size]; +- char output_file[1024]; +- int m_index = 0; +- for (int i = 0; i < argc1; i++) +- { +- if (strncmp (argv1[i], macro_prefix, 2) == 0) +- { +- m_options[m_index] = (char*)argv1[i]; +- m_index++; +- } +- if (strcmp (argv1[i], output_option) == 0) +- { +- truncate_prefix (argv1[i + 1], output_file); +- } +- } +- strcpy (in_options[1], output_file); +- int in_options_size = 2; +- qsort (m_options, m_size, sizeof (m_options[0]), compare_strings); +- for (int i = 0; i < m_size && in_options_size < M_OPTION_SIZE; i++) +- { +- strcpy (in_options[in_options_size], m_options[i]); +- in_options_size++; +- } +- +- for (int i = 0; i < argc1 && in_options_size < M_OPTION_SIZE; i++) +- { +- if (strncmp (argv1[i], macro_prefix, 2) != 0 +- && strcmp (argv1[i], output_option) != 0 +- && strncmp (argv1[i], needle, 7) != 0 +- && strncmp (argv1[i], flag_prefix, 1) == 0) +- { +- strcpy (in_options[in_options_size], argv1[i]); +- in_options_size++; +- } +- } +- while (in_options_size < M_OPTION_SIZE) +- { +- strcpy (in_options[in_options_size], default_option); +- in_options_size++; +- } +- +- uint8_t hash[32]; +- for (int i = 0; i < M_OPTION_SIZE; i++) +- { +- sha256 (in_options[i], strlen (in_options[i]), hash); +- char hash_str[65]; +- for (int j = 0; j < 32; j++) +- { +- sprintf (hash_str + (j * 2), "%02x", hash[j]); +- } +- strcpy (in_options[i], hash_str); +- } +- +- for (int i = 0; i < argc2 && i < M_MODE_SIZE; i++) +- { +- if (i < argc2) +- { +- in_modes[i] = argv2[i]; +- } +- else +- { +- in_modes[i] = default_int_val; +- } +- } +-} +- +-static int +-graph_infer (int argc1, const char **argv1, const char *mops, +- int argc2, int64_t *argv2) +-{ +- static char in_options[M_OPTION_SIZE][1024]; +- static int64_t in_modes[M_MODE_SIZE]; +- +- preprocess (argc1, argv1, mops, argc2, argv2, in_options, in_modes); +- +- const int concat_out_size = 350; +- float concat_result[concat_out_size]; +- const int encoder_out_size = 34; +- const int encoder_last_size = 10; +- int concat_size = 0; +- const int size = encoder_out_size; +- for (int i = 1; i < M_OPTION_SIZE; i++) +- { +- float encoder_out[size]; +- one_hot_encoder (in_options[i], cats_strings, encoder_out, size); +- line_concat (encoder_out, size, concat_result, concat_size); +- concat_size += size; +- } +- float encoder_out2[encoder_last_size]; +- one_hot_encoder (in_options[0], cats_strings2, encoder_out2, +- encoder_last_size); +- line_concat (encoder_out2, encoder_last_size, concat_result, concat_size); +- concat_size += encoder_last_size; +- +- float variable[M_MODE_SIZE]; +- imputer (in_modes, M_MODE_SIZE, variable); +- float variable1[M_MODE_SIZE]; +- scaler (variable, offset, scale, M_MODE_SIZE, variable1); +- +- float transformed_column[concat_out_size + M_MODE_SIZE]; +- line_concat (variable1, M_MODE_SIZE, transformed_column, 0); +- line_concat (concat_result, concat_out_size, transformed_column, 6); +- +- const int m = 1, k = 356, n = 10; +- float mul_result[n]; +- matmul (transformed_column, coefficient[0], m, k, n, mul_result); +- +- float add_result[n]; +- add (mul_result, intercepts, n, add_result); +- float next_activations[n]; +- relu (add_result, n, next_activations); +- +- const int m2 = 1, k2 = 10, n2 = 1; +- float mul_result1[n2]; +- matmul (next_activations, coefficient1, m2, k2, n2, mul_result1); +- float add_result1[n2]; +- add (mul_result1, intercepts1, n2, add_result1); +- +- float out_activations_result[n2]; +- sigmoid (add_result1, n2, out_activations_result); +- +- float negative_class_proba[n2]; +- sub (unity, out_activations_result, n2, negative_class_proba); +- const int prob_size = n2 + n2; +- float probabilities[prob_size]; +- line_concat (negative_class_proba, n2, probabilities, 0); +- line_concat (out_activations_result, n2, probabilities, n2); +- +- int argmax_output = argmax (probabilities, prob_size); +- +- return argmax_output; +-} ++ Copyright (C) 2024-2024 Free Software Foundation, Inc. + +-#endif // AI4C_INFER ++ 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 AI4C_INFER_H ++#define AI4C_INFER_H ++ ++#define PARAM_VALUE_SIMULTANEOUS_PREFETCHES 3 ++#define PARAM_VALUE_L1_CACHE_SIZE 64 ++#define PARAM_VALUE_L1_CACHE_LINE_SIZE 32 ++#define PARAM_VALUE_L2_CACHE_SIZE 512 ++#define PARAM_VALUE_PREFETCH_LATENCY 200 ++#define PARAM_VALUE_PREFETCH_MIN_INSN_TO_MEM_RATIO 3 ++ ++extern void matmul (const float *, const float *, int, int, int, float *); ++extern void add (const float *, const float *, int, float *); ++extern void sub (const float *, const float *, int, float *); ++extern void sigmoid (const float *, int, float *); ++extern void relu (const float *, int, float *); ++extern void line_concat (const float *, int, float *, int); ++extern void one_hot_encoder (const char *, const char (*)[65], float *, int); ++extern void imputer (const int64_t *, int, float *); ++extern void scaler (const float *, const float *, const float *, int, float *); ++extern int argmax (const float *, int); ++ ++extern void ++execute_sha256 (const char *, char *, size_t); ++extern float read_float_from_file (FILE*); ++ ++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/config/aarch64/aarch64-common.c b/gcc/common/config/aarch64/aarch64-common.c +index 21e7820cd..f797f661d 100644 +--- a/gcc/common/config/aarch64/aarch64-common.c ++++ b/gcc/common/config/aarch64/aarch64-common.c +@@ -126,9 +126,9 @@ aarch64_handle_option (struct gcc_options *opts, + struct aarch64_option_extension + { + const char *const name; +- const unsigned long flag_canonical; +- const unsigned long flags_on; +- const unsigned long flags_off; ++ const uint64_t flag_canonical; ++ const uint64_t flags_on; ++ const uint64_t flags_off; + const bool is_synthetic; + }; + +@@ -157,14 +157,14 @@ struct processor_name_to_arch + { + const std::string processor_name; + const enum aarch64_arch arch; +- const unsigned long flags; ++ const uint64_t flags; + }; + + struct arch_to_arch_name + { + const enum aarch64_arch arch; + const std::string arch_name; +- const unsigned long flags; ++ const uint64_t flags; + }; + + /* Map processor names to the architecture revision they implement and +@@ -192,7 +192,7 @@ static const struct arch_to_arch_name all_architectures[] = + aarch64_parse_opt_result describing the result. */ + + enum aarch64_parse_opt_result +-aarch64_parse_extension (const char *str, unsigned long *isa_flags) ++aarch64_parse_extension (const char *str, uint64_t *isa_flags) + { + /* The extension string is parsed left to right. */ + const struct aarch64_option_extension *opt = NULL; +@@ -267,18 +267,21 @@ int opt_ext_cmp (const void* a, const void* b) + turns on as a dependency. As an example +dotprod turns on FL_DOTPROD and + FL_SIMD. As such the set of bits represented by this option is + {FL_DOTPROD, FL_SIMD}. */ +- unsigned long total_flags_a = opt_a->flag_canonical & opt_a->flags_on; +- unsigned long total_flags_b = opt_b->flag_canonical & opt_b->flags_on; ++ uint64_t total_flags_a = opt_a->flag_canonical & opt_a->flags_on; ++ uint64_t total_flags_b = opt_b->flag_canonical & opt_b->flags_on; + int popcnt_a = popcount_hwi ((HOST_WIDE_INT)total_flags_a); + int popcnt_b = popcount_hwi ((HOST_WIDE_INT)total_flags_b); + int order = popcnt_b - popcnt_a; + + /* If they have the same amount of bits set, give it a more + deterministic ordering by using the value of the bits themselves. */ +- if (order == 0) +- return total_flags_b - total_flags_a; ++ if (order != 0) ++ return order; + +- return order; ++ if (total_flags_a != total_flags_b) ++ return total_flags_a < total_flags_b ? 1 : -1; ++ ++ return 0; + } + + /* Implement TARGET_OPTION_INIT_STRUCT. */ +@@ -314,9 +317,9 @@ aarch64_option_init_struct (struct gcc_options *opts ATTRIBUTE_UNUSED) + */ + + static bool +-aarch64_contains_opt (unsigned long isa_flag_bits, opt_ext *opt) ++aarch64_contains_opt (uint64_t isa_flag_bits, opt_ext *opt) + { +- unsigned long flags_check ++ uint64_t flags_check + = opt->is_synthetic ? opt->flags_on : opt->flag_canonical; + + return (isa_flag_bits & flags_check) == flags_check; +@@ -329,13 +332,13 @@ aarch64_contains_opt (unsigned long isa_flag_bits, opt_ext *opt) + that all the "+" flags come before the "+no" flags. */ + + std::string +-aarch64_get_extension_string_for_isa_flags (unsigned long isa_flags, +- unsigned long default_arch_flags) ++aarch64_get_extension_string_for_isa_flags (uint64_t isa_flags, ++ uint64_t default_arch_flags) + { + const struct aarch64_option_extension *opt = NULL; + std::string outstr = ""; + +- unsigned long isa_flag_bits = isa_flags; ++ uint64_t isa_flag_bits = isa_flags; + + /* Pass one: Minimize the search space by reducing the set of options + to the smallest set that still turns on the same features as before in +diff --git a/gcc/config/aarch64/aarch64-arches.def b/gcc/config/aarch64/aarch64-arches.def +index 7d05cd604..f3aeb1eda 100644 +--- a/gcc/config/aarch64/aarch64-arches.def ++++ b/gcc/config/aarch64/aarch64-arches.def +@@ -36,5 +36,10 @@ AARCH64_ARCH("armv8.2-a", generic, 8_2A, 8, AARCH64_FL_FOR_ARCH8_2) + AARCH64_ARCH("armv8.3-a", generic, 8_3A, 8, AARCH64_FL_FOR_ARCH8_3) + AARCH64_ARCH("armv8.4-a", generic, 8_4A, 8, AARCH64_FL_FOR_ARCH8_4) + AARCH64_ARCH("armv8.5-a", generic, 8_5A, 8, AARCH64_FL_FOR_ARCH8_5) ++AARCH64_ARCH("armv8.6-a", generic, 8_6A, 8, AARCH64_FL_FOR_ARCH8_6) ++AARCH64_ARCH("armv8.7-a", generic, 8_7A, 8, AARCH64_FL_FOR_ARCH8_7) ++AARCH64_ARCH("armv9-a", generic, 9A , 9, AARCH64_FL_FOR_ARCH9) ++AARCH64_ARCH("armv9.1-a", generic, 9_1A , 9, AARCH64_FL_FOR_ARCH9_1) ++AARCH64_ARCH("armv9.2-a", generic, 9_2A , 9, AARCH64_FL_FOR_ARCH9_2) + + #undef AARCH64_ARCH +diff --git a/gcc/config/aarch64/aarch64-cores.def b/gcc/config/aarch64/aarch64-cores.def +index dc51967d4..3ff899ebd 100644 +--- a/gcc/config/aarch64/aarch64-cores.def ++++ b/gcc/config/aarch64/aarch64-cores.def +@@ -117,4 +117,6 @@ AARCH64_CORE("cortex-a73.cortex-a53", cortexa73cortexa53, cortexa53, 8A, AARCH + + AARCH64_CORE("cortex-a75.cortex-a55", cortexa75cortexa55, cortexa53, 8_2A, AARCH64_FL_FOR_ARCH8_2 | AARCH64_FL_F16 | AARCH64_FL_RCPC | AARCH64_FL_DOTPROD, cortexa73, 0x41, AARCH64_BIG_LITTLE (0xd0a, 0xd05), -1) + ++AARCH64_CORE("hip12", hip12, hip12, 9_2A, AARCH64_FL_FOR_ARCH9_2 | AARCH64_FL_SVE | AARCH64_FL_SVE2 | AARCH64_FL_SVE2_BITPERM | AARCH64_FL_SVE2_AES | AARCH64_FL_SVE2_SM4 | AARCH64_FL_SVE2_SHA3 | AARCH64_FL_F16 | AARCH64_FL_RCPC | AARCH64_FL_BF16 | AARCH64_FL_DOTPROD | AARCH64_FL_LSE | AARCH64_FL_SIMD | AARCH64_FL_PAUTH | AARCH64_FL_RDMA | AARCH64_FL_LS64, hip12, 0x48, 0xd06, -1) ++ + #undef AARCH64_CORE +diff --git a/gcc/config/aarch64/aarch64-cost-tables.h b/gcc/config/aarch64/aarch64-cost-tables.h +index 8a8ae7799..52a6dd06d 100644 +--- a/gcc/config/aarch64/aarch64-cost-tables.h ++++ b/gcc/config/aarch64/aarch64-cost-tables.h +@@ -334,6 +334,110 @@ const struct cpu_cost_table thunderx2t99_extra_costs = + } + }; + ++const struct cpu_cost_table hip12_extra_costs = ++{ ++ /* ALU */ ++ { ++ 0, /* arith. */ ++ 0, /* logical. */ ++ 0, /* shift. */ ++ 0, /* shift_reg. */ ++ 0, /* arith_shift. */ ++ 0, /* arith_shift_reg. */ ++ COSTS_N_INSNS (1), /* log_shift. */ ++ COSTS_N_INSNS (1), /* log_shift_reg. */ ++ COSTS_N_INSNS (1), /* extend. */ ++ 0, /* extend_arith. */ ++ 0, /* bfi. */ ++ 0, /* bfx. */ ++ 0, /* clz. */ ++ 0, /* rev. */ ++ 0, /* non_exec. */ ++ true /* non_exec_costs_exec. */ ++ }, ++ ++ { ++ /* MULT SImode */ ++ { ++ COSTS_N_INSNS (2), /* simple. */ ++ 0, /* flag_setting. */ ++ COSTS_N_INSNS (2), /* extend. */ ++ COSTS_N_INSNS (2), /* add. */ ++ COSTS_N_INSNS (2), /* extend_add. */ ++ COSTS_N_INSNS (5) /* idiv. */ ++ }, ++ /* MULT DImode */ ++ { ++ COSTS_N_INSNS (3), /* simple. */ ++ 0, /* flag_setting (N/A). */ ++ COSTS_N_INSNS (3), /* extend. */ ++ COSTS_N_INSNS (3), /* add. */ ++ COSTS_N_INSNS (3), /* extend_add. */ ++ COSTS_N_INSNS (7) /* idiv. */ ++ } ++ }, ++ /* LD/ST */ ++ { ++ COSTS_N_INSNS (3), /* load. */ ++ COSTS_N_INSNS (4), /* load_sign_extend. */ ++ COSTS_N_INSNS (3), /* ldrd. */ ++ COSTS_N_INSNS (3), /* ldm_1st. */ ++ 1, /* ldm_regs_per_insn_1st. */ ++ 2, /* ldm_regs_per_insn_subsequent. */ ++ COSTS_N_INSNS (5), /* loadf. */ ++ COSTS_N_INSNS (5), /* loadd. */ ++ COSTS_N_INSNS (4), /* load_unaligned. */ ++ 0, /* store. */ ++ 0, /* strd. */ ++ 0, /* stm_1st. */ ++ 1, /* stm_regs_per_insn_1st. */ ++ 2, /* stm_regs_per_insn_subsequent. */ ++ 0, /* storef. */ ++ 0, /* stored. */ ++ COSTS_N_INSNS (1), /* store_unaligned. */ ++ COSTS_N_INSNS (5), /* loadv. */ ++ COSTS_N_INSNS (2) /* storev. */ ++ }, ++ { ++ /* FP SFmode */ ++ { ++ COSTS_N_INSNS (5), /* div. */ ++ COSTS_N_INSNS (2), /* mult. */ ++ COSTS_N_INSNS (4), /* mult_addsub. */ ++ COSTS_N_INSNS (3), /* fma. */ ++ COSTS_N_INSNS (1), /* addsub. */ ++ COSTS_N_INSNS (1), /* fpconst. */ ++ 0, /* neg. */ ++ COSTS_N_INSNS (1), /* compare. */ ++ COSTS_N_INSNS (2), /* widen. */ ++ COSTS_N_INSNS (2), /* narrow. */ ++ COSTS_N_INSNS (2), /* toint. */ ++ COSTS_N_INSNS (3), /* fromint. */ ++ COSTS_N_INSNS (2) /* roundint. */ ++ }, ++ /* FP DFmode */ ++ { ++ COSTS_N_INSNS (7), /* div. */ ++ COSTS_N_INSNS (2), /* mult. */ ++ COSTS_N_INSNS (4), /* mult_addsub. */ ++ COSTS_N_INSNS (3), /* fma. */ ++ COSTS_N_INSNS (1), /* addsub. */ ++ COSTS_N_INSNS (1), /* fpconst. */ ++ 0, /* neg. */ ++ COSTS_N_INSNS (1), /* compare. */ ++ COSTS_N_INSNS (2), /* widen. */ ++ COSTS_N_INSNS (2), /* narrow. */ ++ COSTS_N_INSNS (2), /* toint. */ ++ COSTS_N_INSNS (3), /* fromint. */ ++ COSTS_N_INSNS (2) /* roundint. */ ++ } ++ }, ++ /* Vector */ ++ { ++ COSTS_N_INSNS (1), /* alu. */ ++ } ++}; ++ + const struct cpu_cost_table hip09_extra_costs = + { + /* ALU */ +diff --git a/gcc/config/aarch64/aarch64-option-extensions.def b/gcc/config/aarch64/aarch64-option-extensions.def +index 8246c612b..b07cd7cff 100644 +--- a/gcc/config/aarch64/aarch64-option-extensions.def ++++ b/gcc/config/aarch64/aarch64-option-extensions.def +@@ -57,17 +57,20 @@ + + /* Enabling "fp" just enables "fp". + Disabling "fp" also disables "simd", "crypto", "fp16", "aes", "sha2", +- "sha3", sm3/sm4 and "sve". */ +-AARCH64_OPT_EXTENSION("fp", AARCH64_FL_FP, 0, AARCH64_FL_SIMD | AARCH64_FL_CRYPTO | AARCH64_FL_F16 | AARCH64_FL_AES | AARCH64_FL_SHA2 | AARCH64_FL_SHA3 | AARCH64_FL_SM4 | AARCH64_FL_SVE, false, "fp") ++ "sha3", sm3/sm4, "sve", "sve2", "sve2-aes", "sve2-sha3", "sve2-sm4", and ++ "bitperm". */ ++AARCH64_OPT_EXTENSION("fp", AARCH64_FL_FP, 0, AARCH64_FL_SIMD | AARCH64_FL_CRYPTO | AARCH64_FL_F16 | AARCH64_FL_AES | AARCH64_FL_SHA2 | AARCH64_FL_SHA3 | AARCH64_FL_SM4 | AARCH64_FL_SVE | AARCH64_FL_SVE2 | AARCH64_FL_SVE2_AES | AARCH64_FL_SVE2_SHA3 | AARCH64_FL_SVE2_SM4 | AARCH64_FL_SVE2_BITPERM, false, "fp") + + /* Enabling "simd" also enables "fp". + Disabling "simd" also disables "crypto", "dotprod", "aes", "sha2", "sha3", +- "sm3/sm4" and "sve". */ +-AARCH64_OPT_EXTENSION("simd", AARCH64_FL_SIMD, AARCH64_FL_FP, AARCH64_FL_CRYPTO | AARCH64_FL_DOTPROD | AARCH64_FL_AES | AARCH64_FL_SHA2 | AARCH64_FL_SHA3 | AARCH64_FL_SM4 | AARCH64_FL_SVE, false, "asimd") ++ "sm3/sm4", "sve", "sve2", "sve2-aes", "sve2-sha3", "sve2-sm4", and "bitperm". ++ */ ++AARCH64_OPT_EXTENSION("simd", AARCH64_FL_SIMD, AARCH64_FL_FP, AARCH64_FL_CRYPTO | AARCH64_FL_DOTPROD | AARCH64_FL_AES | AARCH64_FL_SHA2 | AARCH64_FL_SHA3 | AARCH64_FL_SM4 | AARCH64_FL_SVE | AARCH64_FL_SVE2 | AARCH64_FL_SVE2_AES | AARCH64_FL_SVE2_SHA3 | AARCH64_FL_SVE2_SM4 | AARCH64_FL_SVE2_BITPERM, false, "asimd") + + /* Enabling "crypto" also enables "fp", "simd", "aes" and "sha2". +- Disabling "crypto" disables "crypto", "aes", "sha2", "sha3" and "sm3/sm4". */ +-AARCH64_OPT_EXTENSION("crypto", AARCH64_FL_CRYPTO, AARCH64_FL_FP | AARCH64_FL_SIMD | AARCH64_FL_AES | AARCH64_FL_SHA2, AARCH64_FL_AES | AARCH64_FL_SHA2 |AARCH64_FL_SHA3 | AARCH64_FL_SM4, true, "aes pmull sha1 sha2") ++ Disabling "crypto" disables "crypto", "aes", "sha2", "sha3" and "sm3/sm4", ++ "sve2-aes", "sve2-sha3", "sve2-sm4". */ ++AARCH64_OPT_EXTENSION("crypto", AARCH64_FL_CRYPTO, AARCH64_FL_FP | AARCH64_FL_SIMD | AARCH64_FL_AES | AARCH64_FL_SHA2, AARCH64_FL_AES | AARCH64_FL_SHA2 | AARCH64_FL_SHA3 | AARCH64_FL_SM4 | AARCH64_FL_SVE2_AES | AARCH64_FL_SVE2_SHA3 | AARCH64_FL_SVE2_SM4, true, "aes pmull sha1 sha2") + + /* Enabling or disabling "crc" only changes "crc". */ + AARCH64_OPT_EXTENSION("crc", AARCH64_FL_CRC, 0, 0, false, "crc32") +@@ -76,8 +79,9 @@ AARCH64_OPT_EXTENSION("crc", AARCH64_FL_CRC, 0, 0, false, "crc32") + AARCH64_OPT_EXTENSION("lse", AARCH64_FL_LSE, 0, 0, false, "atomics") + + /* Enabling "fp16" also enables "fp". +- Disabling "fp16" disables "fp16", "fp16fml" and "sve". */ +-AARCH64_OPT_EXTENSION("fp16", AARCH64_FL_F16, AARCH64_FL_FP, AARCH64_FL_F16FML | AARCH64_FL_SVE, false, "fphp asimdhp") ++ Disabling "fp16" disables "fp16", "fp16fml", "sve", "sve2", "sve2-aes", ++ "sve2-sha3", "sve2-sm4", and "bitperm". */ ++AARCH64_OPT_EXTENSION("fp16", AARCH64_FL_F16, AARCH64_FL_FP, AARCH64_FL_F16FML | AARCH64_FL_SVE | AARCH64_FL_SVE2 | AARCH64_FL_SVE2_AES | AARCH64_FL_SVE2_SHA3 | AARCH64_FL_SVE2_SM4 | AARCH64_FL_SVE2_BITPERM, false, "fphp asimdhp") + + /* Enabling or disabling "rcpc" only changes "rcpc". */ + AARCH64_OPT_EXTENSION("rcpc", AARCH64_FL_RCPC, 0, 0, false, "lrcpc") +@@ -91,28 +95,29 @@ AARCH64_OPT_EXTENSION("rdma", AARCH64_FL_RDMA, AARCH64_FL_FP | AARCH64_FL_SIMD, + AARCH64_OPT_EXTENSION("dotprod", AARCH64_FL_DOTPROD, AARCH64_FL_SIMD, 0, false, "asimddp") + + /* Enabling "aes" also enables "simd". +- Disabling "aes" just disables "aes". */ +-AARCH64_OPT_EXTENSION("aes", AARCH64_FL_AES, AARCH64_FL_SIMD, 0, false, "aes") ++ Disabling "aes" disables "aes" and "sve2-aes'. */ ++AARCH64_OPT_EXTENSION("aes", AARCH64_FL_AES, AARCH64_FL_SIMD, AARCH64_FL_SVE2_AES, false, "aes") + + /* Enabling "sha2" also enables "simd". + Disabling "sha2" just disables "sha2". */ + AARCH64_OPT_EXTENSION("sha2", AARCH64_FL_SHA2, AARCH64_FL_SIMD, 0, false, "sha1 sha2") + + /* Enabling "sha3" enables "simd" and "sha2". +- Disabling "sha3" just disables "sha3". */ +-AARCH64_OPT_EXTENSION("sha3", AARCH64_FL_SHA3, AARCH64_FL_SIMD | AARCH64_FL_SHA2, 0, false, "sha3 sha512") ++ Disabling "sha3" disables "sha3" and "sve2-sha3". */ ++AARCH64_OPT_EXTENSION("sha3", AARCH64_FL_SHA3, AARCH64_FL_SIMD | AARCH64_FL_SHA2, AARCH64_FL_SVE2_SHA3, false, "sha3 sha512") + + /* Enabling "sm4" also enables "simd". +- Disabling "sm4" just disables "sm4". */ +-AARCH64_OPT_EXTENSION("sm4", AARCH64_FL_SM4, AARCH64_FL_SIMD, 0, false, "sm3 sm4") ++ Disabling "sm4" disables "sm4" and "sve2-sm4". */ ++AARCH64_OPT_EXTENSION("sm4", AARCH64_FL_SM4, AARCH64_FL_SIMD, AARCH64_FL_SVE2_SM4, false, "sm3 sm4") + + /* Enabling "fp16fml" also enables "fp" and "fp16". + Disabling "fp16fml" just disables "fp16fml". */ + AARCH64_OPT_EXTENSION("fp16fml", AARCH64_FL_F16FML, AARCH64_FL_FP | AARCH64_FL_F16, 0, false, "asimdfml") + + /* Enabling "sve" also enables "fp16", "fp" and "simd". +- Disabling "sve" just disables "sve". */ +-AARCH64_OPT_EXTENSION("sve", AARCH64_FL_SVE, AARCH64_FL_FP | AARCH64_FL_SIMD | AARCH64_FL_F16, 0, false, "sve") ++ Disabling "sve" disables "sve", "sve2", "sve2-aes", "sve2-sha3", "sve2-sm4" ++ and "bitperm". */ ++AARCH64_OPT_EXTENSION("sve", AARCH64_FL_SVE, AARCH64_FL_FP | AARCH64_FL_SIMD | AARCH64_FL_F16, AARCH64_FL_SVE2 | AARCH64_FL_SVE2_AES | AARCH64_FL_SVE2_SHA3 | AARCH64_FL_SVE2_SM4 | AARCH64_FL_SVE2_BITPERM, false, "sve") + + /* Enabling/Disabling "rng" only changes "rng". */ + AARCH64_OPT_EXTENSION("rng", AARCH64_FL_RNG, 0, 0, false, "rng") +@@ -129,4 +134,25 @@ AARCH64_OPT_EXTENSION("ssbs", AARCH64_FL_SSBS, 0, 0, false, "ssbs") + /* Enabling/Disabling "predres" only changes "predres". */ + AARCH64_OPT_EXTENSION("predres", AARCH64_FL_PREDRES, 0, 0, false, "") + ++/* Enabling "sve2" also enables "sve", "fp16", "fp", and "simd". ++ Disabling "sve2" disables "sve2", "sve2-aes", "sve2-sha3", "sve2-sm4", and ++ "bitperm". */ ++AARCH64_OPT_EXTENSION("sve2", AARCH64_FL_SVE2, AARCH64_FL_SVE | AARCH64_FL_FP | AARCH64_FL_SIMD | AARCH64_FL_F16, AARCH64_FL_SVE2_AES | AARCH64_FL_SVE2_SHA3 | AARCH64_FL_SVE2_SM4 | AARCH64_FL_SVE2_BITPERM, false, "") ++ ++/* Enabling "sve2-sm4" also enables "sm4", "simd", "fp16", "fp", "sve", and ++ "sve2". Disabling "sve2-sm4" just disables "sve2-sm4". */ ++AARCH64_OPT_EXTENSION("sve2-sm4", AARCH64_FL_SVE2_SM4, AARCH64_FL_SM4 | AARCH64_FL_SIMD | AARCH64_FL_F16 | AARCH64_FL_FP | AARCH64_FL_SVE | AARCH64_FL_SVE2, 0, false, "") ++ ++/* Enabling "sve2-aes" also enables "aes", "simd", "fp16", "fp", "sve", and ++ "sve2". Disabling "sve2-aes" just disables "sve2-aes". */ ++AARCH64_OPT_EXTENSION("sve2-aes", AARCH64_FL_SVE2_AES, AARCH64_FL_AES | AARCH64_FL_SIMD | AARCH64_FL_F16 | AARCH64_FL_FP | AARCH64_FL_SVE | AARCH64_FL_SVE2, 0, false, "") ++ ++/* Enabling "sve2-sha3" also enables "sha3", "simd", "fp16", "fp", "sve", and ++ "sve2". Disabling "sve2-sha3" just disables "sve2-sha3". */ ++AARCH64_OPT_EXTENSION("sve2-sha3", AARCH64_FL_SVE2_SHA3, AARCH64_FL_SHA3 | AARCH64_FL_SIMD | AARCH64_FL_F16 | AARCH64_FL_FP | AARCH64_FL_SVE | AARCH64_FL_SVE2, 0, false, "") ++ ++/* Enabling "bitperm" also enables "simd", "fp16", "fp", "sve", and "sve2". ++ Disabling "bitperm" just disables "bitperm". */ ++AARCH64_OPT_EXTENSION("bitperm", AARCH64_FL_SVE2_BITPERM, AARCH64_FL_SIMD | AARCH64_FL_F16 | AARCH64_FL_FP | AARCH64_FL_SVE | AARCH64_FL_SVE2, 0, false, "") ++ + #undef AARCH64_OPT_EXTENSION +diff --git a/gcc/config/aarch64/aarch64-tune.md b/gcc/config/aarch64/aarch64-tune.md +index b7b063ad4..9c13a3ea3 100644 +--- a/gcc/config/aarch64/aarch64-tune.md ++++ b/gcc/config/aarch64/aarch64-tune.md +@@ -1,5 +1,5 @@ + ;; -*- buffer-read-only: t -*- + ;; Generated automatically by gentune.sh from aarch64-cores.def + (define_attr "tune" +- "cortexa35,cortexa53,cortexa57,cortexa72,cortexa73,thunderx,thunderxt88p1,thunderxt88,thunderxt81,thunderxt83,xgene1,falkor,qdf24xx,exynosm1,thunderx2t99p1,vulcan,thunderx2t99,cortexa55,cortexa75,cortexa76,ares,neoversen1,hip09,saphira,zeus,neoversev1,neoverse512tvb,neoversen2,cortexa57cortexa53,cortexa72cortexa53,cortexa73cortexa35,cortexa73cortexa53,cortexa75cortexa55" ++ "cortexa35,cortexa53,cortexa57,cortexa72,cortexa73,thunderx,thunderxt88p1,thunderxt88,thunderxt81,thunderxt83,xgene1,falkor,qdf24xx,exynosm1,thunderx2t99p1,vulcan,thunderx2t99,cortexa55,cortexa75,cortexa76,ares,neoversen1,hip09,saphira,zeus,neoversev1,neoverse512tvb,neoversen2,cortexa57cortexa53,cortexa72cortexa53,cortexa73cortexa35,cortexa73cortexa53,cortexa75cortexa55,hip12" + (const (symbol_ref "((enum attr_tune) aarch64_tune)"))) +diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c +index 5bbb7619e..e70ecaf9c 100644 +--- a/gcc/config/aarch64/aarch64.c ++++ b/gcc/config/aarch64/aarch64.c +@@ -215,7 +215,7 @@ unsigned aarch64_architecture_version; + enum aarch64_processor aarch64_tune = cortexa53; + + /* Mask to specify which instruction scheduling options should be used. */ +-unsigned long aarch64_tune_flags = 0; ++uint64_t aarch64_tune_flags = 0; + + /* Global flag for PC relative loads. */ + bool aarch64_pcrelative_literal_loads; +@@ -330,6 +330,22 @@ static const struct cpu_addrcost_table hip09_addrcost_table = + 0, /* imm_offset */ + }; + ++static const struct cpu_addrcost_table hip12_addrcost_table = ++{ ++ { ++ 1, /* hi */ ++ 0, /* si */ ++ 0, /* di */ ++ 1, /* ti */ ++ }, ++ 0, /* pre_modify */ ++ 0, /* post_modify */ ++ 0, /* register_offset */ ++ 0, /* register_sextend */ ++ 0, /* register_zextend */ ++ 0, /* imm_offset */ ++}; ++ + static const struct cpu_addrcost_table qdf24xx_addrcost_table = + { + { +@@ -432,6 +448,16 @@ static const struct cpu_regmove_cost hip09_regmove_cost = + 2 /* FP2FP */ + }; + ++static const struct cpu_regmove_cost hip12_regmove_cost = ++{ ++ 1, /* GP2GP */ ++ /* Avoid the use of slow int<->fp moves for spilling by setting ++ their cost higher than memmov_cost. */ ++ 6, /* GP2FP */ ++ 2, /* FP2GP */ ++ 2 /* FP2FP */ ++}; ++ + /* Generic costs for vector insn classes. */ + static const struct cpu_vector_cost generic_vector_cost = + { +@@ -511,6 +537,25 @@ static const struct cpu_vector_cost hip09_vector_cost = + 1 /* cond_not_taken_branch_cost */ + }; + ++static const struct cpu_vector_cost hip12_vector_cost = ++{ ++ 1, /* scalar_int_stmt_cost */ ++ 2, /* scalar_fp_stmt_cost */ ++ 4, /* scalar_load_cost */ ++ 1, /* scalar_store_cost */ ++ 2, /* vec_int_stmt_cost */ ++ 2, /* vec_fp_stmt_cost */ ++ 2, /* vec_permute_cost */ ++ 2, /* vec_to_scalar_cost */ ++ 5, /* scalar_to_vec_cost */ ++ 8, /* vec_align_load_cost */ ++ 8, /* vec_unalign_load_cost */ ++ 1, /* vec_unalign_store_cost */ ++ 1, /* vec_store_cost */ ++ 1, /* cond_taken_branch_cost */ ++ 1, /* cond_not_taken_branch_cost */ ++}; ++ + /* Generic costs for vector insn classes. */ + static const struct cpu_vector_cost cortexa57_vector_cost = + { +@@ -685,6 +730,15 @@ static const cpu_prefetch_tune hip09_prefetch_tune = + -1 /* default_opt_level */ + }; + ++static const cpu_prefetch_tune hip12_prefetch_tune = ++{ ++ 0, /* num_slots */ ++ 128, /* l1_cache_size */ ++ 64, /* l1_cache_line_size */ ++ 1024, /* l2_cache_size */ ++ -1 /* default_opt_level */ ++}; ++ + static const struct tune_params generic_tunings = + { + &cortexa57_extra_costs, +@@ -1101,6 +1155,33 @@ static const struct tune_params hip09_tunings = + &hip09_prefetch_tune + }; + ++static const struct tune_params hip12_tunings = ++{ ++ &hip12_extra_costs, ++ &hip12_addrcost_table, ++ &hip12_regmove_cost, ++ &hip12_vector_cost, ++ &generic_branch_cost, ++ &generic_approx_modes, ++ 4, /* memmov_cost */ ++ 16, /* issue_rate */ ++ (AARCH64_FUSE_AES_AESMC | AARCH64_FUSE_ALU_BRANCH ++ | AARCH64_FUSE_ALU_CBZ), /* fusible_ops */ ++ 16, /* function_align. */ ++ 4, /* jump_align. */ ++ 8, /* loop_align. */ ++ 4, /* int_reassoc_width. */ ++ 4, /* fp_reassoc_width. */ ++ 4, /* vec_reassoc_width. */ ++ 2, /* min_div_recip_mul_sf. */ ++ 2, /* min_div_recip_mul_df. */ ++ 0, /* max_case_values. */ ++ tune_params::AUTOPREFETCHER_WEAK, /* autoprefetcher_model. */ ++ (AARCH64_EXTRA_TUNE_NONE), /* tune_flags. */ ++ &hip12_prefetch_tune ++}; ++ ++ + /* Support for fine-grained override of the tuning structures. */ + struct aarch64_tuning_override_function + { +@@ -1127,7 +1208,7 @@ struct processor + enum aarch64_processor sched_core; + enum aarch64_arch arch; + unsigned architecture_version; +- const unsigned long flags; ++ const uint64_t flags; + const struct tune_params *const tune; + }; + +@@ -7606,10 +7687,7 @@ override_CPP_optimize_options (struct gcc_options *opts) + { + opts->x_flag_omit_frame_pointer = 1; + opts->x_flag_sized_deallocation = 0; +- maybe_set_param_value (PARAM_EARLY_INLINING_INSNS, +- 256, +- opts->x_param_values, +- global_options_set.x_param_values); ++ + maybe_set_param_value (PARAM_MAX_INLINE_INSNS_AUTO, + 128, + opts->x_param_values, +@@ -7659,26 +7737,26 @@ override_Fortran_optimize_options (struct gcc_options *opts) + static void + reset_machine_option (struct gcc_options *opts) + { +- if (!(opts->x_optimize_maximum) +- || opts->x_aarch64_cpu_string == NULL +- || strstr (opts->x_aarch64_cpu_string, "hip09") == NULL) +- { +- return; +- } +- + const char *ai_infer_level = getenv ("AI_INFER_LEVEL"); + if (ai_infer_level) + { ++ char *collect_gcc = getenv("COLLECT_GCC"); ++ const char* gcc_exec = basename(ASTRDUP(collect_gcc)); ++ ++ if (gcc_exec == NULL) ++ { ++ return; ++ } + override_optimize_options_1 (opts); +- if (lang_c_p ()) ++ if (strstr(gcc_exec, "gcc") != NULL) + { + override_C_optimize_options (opts); + } +- else if (lang_cpp_p ()) ++ else if (strstr(gcc_exec, "g++") != NULL) + { + override_CPP_optimize_options (opts); + } +- else if (lang_GNU_Fortran ()) ++ else if (strstr(gcc_exec, "gfortran") != NULL) + { + override_Fortran_optimize_options (opts); + } +@@ -10818,7 +10896,7 @@ static void initialize_aarch64_code_model (struct gcc_options *); + + static enum aarch64_parse_opt_result + aarch64_parse_arch (const char *to_parse, const struct processor **res, +- unsigned long *isa_flags) ++ uint64_t *isa_flags) + { + char *ext; + const struct processor *arch; +@@ -10843,7 +10921,7 @@ aarch64_parse_arch (const char *to_parse, const struct processor **res, + { + if (strlen (arch->name) == len && strncmp (arch->name, str, len) == 0) + { +- unsigned long isa_temp = arch->flags; ++ uint64_t isa_temp = arch->flags; + + if (ext != NULL) + { +@@ -10873,7 +10951,7 @@ aarch64_parse_arch (const char *to_parse, const struct processor **res, + + static enum aarch64_parse_opt_result + aarch64_parse_cpu (const char *to_parse, const struct processor **res, +- unsigned long *isa_flags) ++ uint64_t *isa_flags) + { + char *ext; + const struct processor *cpu; +@@ -10898,7 +10976,7 @@ aarch64_parse_cpu (const char *to_parse, const struct processor **res, + { + if (strlen (cpu->name) == len && strncmp (cpu->name, str, len) == 0) + { +- unsigned long isa_temp = cpu->flags; ++ uint64_t isa_temp = cpu->flags; + + + if (ext != NULL) +@@ -11357,7 +11435,7 @@ aarch64_print_hint_for_arch (const char *str) + + static bool + aarch64_validate_mcpu (const char *str, const struct processor **res, +- unsigned long *isa_flags) ++ uint64_t *isa_flags) + { + enum aarch64_parse_opt_result parse_res + = aarch64_parse_cpu (str, res, isa_flags); +@@ -11391,7 +11469,7 @@ aarch64_validate_mcpu (const char *str, const struct processor **res, + + static bool + aarch64_validate_march (const char *str, const struct processor **res, +- unsigned long *isa_flags) ++ uint64_t *isa_flags) + { + enum aarch64_parse_opt_result parse_res + = aarch64_parse_arch (str, res, isa_flags); +@@ -11503,8 +11581,8 @@ aarch64_convert_sve_vector_bits (aarch64_sve_vector_bits_enum value) + static void + aarch64_override_options (void) + { +- unsigned long cpu_isa = 0; +- unsigned long arch_isa = 0; ++ uint64_t cpu_isa = 0; ++ uint64_t arch_isa = 0; + aarch64_isa_flags = 0; + + bool valid_cpu = true; +@@ -11723,7 +11801,7 @@ aarch64_option_print (FILE *file, int indent, struct cl_target_option *ptr) + { + const struct processor *cpu + = aarch64_get_tune_cpu (ptr->x_explicit_tune_core); +- unsigned long isa_flags = ptr->x_aarch64_isa_flags; ++ uint64_t isa_flags = ptr->x_aarch64_isa_flags; + const struct processor *arch = aarch64_get_arch (ptr->x_explicit_arch); + std::string extension + = aarch64_get_extension_string_for_isa_flags (isa_flags, arch->flags); +@@ -11939,7 +12017,7 @@ static bool + aarch64_handle_attr_isa_flags (char *str) + { + enum aarch64_parse_opt_result parse_res; +- unsigned long isa_flags = aarch64_isa_flags; ++ uint64_t isa_flags = aarch64_isa_flags; + + /* We allow "+nothing" in the beginning to clear out all architectural + features if the user wants to handpick specific features. */ +@@ -14741,7 +14819,7 @@ aarch64_declare_function_name (FILE *stream, const char* name, + const struct processor *this_arch + = aarch64_get_arch (targ_options->x_explicit_arch); + +- unsigned long isa_flags = targ_options->x_aarch64_isa_flags; ++ uint64_t isa_flags = targ_options->x_aarch64_isa_flags; + std::string extension + = aarch64_get_extension_string_for_isa_flags (isa_flags, + this_arch->flags); +@@ -14782,7 +14860,7 @@ aarch64_start_file (void) + + const struct processor *default_arch + = aarch64_get_arch (default_options->x_explicit_arch); +- unsigned long default_isa_flags = default_options->x_aarch64_isa_flags; ++ uint64_t default_isa_flags = default_options->x_aarch64_isa_flags; + std::string extension + = aarch64_get_extension_string_for_isa_flags (default_isa_flags, + default_arch->flags); +diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h +index 4e15c1398..17fe8e5c5 100644 +--- a/gcc/config/aarch64/aarch64.h ++++ b/gcc/config/aarch64/aarch64.h +@@ -185,6 +185,12 @@ extern unsigned aarch64_architecture_version; + #define AARCH64_FL_SVE2_SHA3 (1ULL << 31) + #define AARCH64_FL_SVE2_BITPERM (1ULL << 32) + ++/* Transactional Memory Extension. */ ++#define AARCH64_FL_TME (1ULL << 33) /* Has TME instructions. */ ++ ++/* Armv8.6-A architecture extensions. */ ++#define AARCH64_FL_V8_6 (1ULL << 34) ++ + /* 8-bit Integer Matrix Multiply (I8MM) extensions. */ + #define AARCH64_FL_I8MM (1ULL << 35) + +@@ -197,6 +203,34 @@ extern unsigned aarch64_architecture_version; + /* 64-bit Floating-point Matrix Multiply (F64MM) extensions. */ + #define AARCH64_FL_F64MM (1ULL << 38) + ++/* Flag Manipulation Instructions (FLAGM) extension. */ ++#define AARCH64_FL_FLAGM (1ULL << 39) ++ ++/* Pointer Authentication (PAUTH) extension. */ ++#define AARCH64_FL_PAUTH (1ULL << 40) ++ ++/* Armv9.0-A. */ ++#define AARCH64_FL_V9 (1ULL << 41) /* Armv9.0-A Architecture. */ ++ ++/* 64-byte atomic load/store extensions. */ ++#define AARCH64_FL_LS64 (1ULL << 42) ++ ++/* Armv8.7-a architecture extensions. */ ++#define AARCH64_FL_V8_7 (1ULL << 43) ++ ++/* Hardware memory operation instructions. */ ++#define AARCH64_FL_MOPS (1ULL << 44) ++ ++/* Armv8.8-a architecture extensions. */ ++#define AARCH64_FL_V8_8 (1ULL << 45) ++ ++/* SVE2 instruction supported. */ ++#define AARCH64_FL_SVE2 (1 << 28) ++#define AARCH64_FL_SVE2_AES (1 << 29) ++#define AARCH64_FL_SVE2_SM4 (1 << 30) ++#define AARCH64_FL_SVE2_SHA3 (1ULL << 31) ++#define AARCH64_FL_SVE2_BITPERM (1ULL << 32) ++ + /* Has FP and SIMD. */ + #define AARCH64_FL_FPSIMD (AARCH64_FL_FP | AARCH64_FL_SIMD) + +@@ -218,6 +252,18 @@ extern unsigned aarch64_architecture_version; + #define AARCH64_FL_FOR_ARCH8_5 \ + (AARCH64_FL_FOR_ARCH8_4 | AARCH64_FL_V8_5 \ + | AARCH64_FL_SB | AARCH64_FL_SSBS | AARCH64_FL_PREDRES) ++#define AARCH64_FL_FOR_ARCH8_6 \ ++ (AARCH64_FL_FOR_ARCH8_5 | AARCH64_FL_V8_6 | AARCH64_FL_FPSIMD \ ++ | AARCH64_FL_I8MM | AARCH64_FL_BF16) ++#define AARCH64_FL_FOR_ARCH8_7 \ ++ (AARCH64_FL_FOR_ARCH8_6 | AARCH64_FL_V8_7 | AARCH64_FL_LS64) ++#define AARCH64_FL_FOR_ARCH9 \ ++ (AARCH64_FL_FOR_ARCH8_5 | AARCH64_FL_SVE | AARCH64_FL_SVE2 | AARCH64_FL_V9 \ ++ | AARCH64_FL_F16) ++#define AARCH64_FL_FOR_ARCH9_1 \ ++ (AARCH64_FL_FOR_ARCH8_6 | AARCH64_FL_FOR_ARCH9) ++#define AARCH64_FL_FOR_ARCH9_2 \ ++ (AARCH64_FL_FOR_ARCH8_7 | AARCH64_FL_FOR_ARCH9_1) + + /* Macros to test ISA flags. */ + +diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md +index e93dabf4d..ff2fc5c5d 100644 +--- a/gcc/config/aarch64/aarch64.md ++++ b/gcc/config/aarch64/aarch64.md +@@ -306,6 +306,7 @@ + (include "../arm/xgene1.md") + (include "thunderx2t99.md") + (include "hip09.md") ++(include "hip12.md") + + ;; ------------------------------------------------------------------- + ;; Jumps and other miscellaneous insns +diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt +index a2c830066..d5a7af581 100644 +--- a/gcc/config/aarch64/aarch64.opt ++++ b/gcc/config/aarch64/aarch64.opt +@@ -31,7 +31,7 @@ TargetSave + const char *x_aarch64_override_tune_string + + TargetVariable +-unsigned long aarch64_isa_flags = 0 ++uint64_t aarch64_isa_flags = 0 + + TargetVariable + unsigned aarch64_enable_bti = 2 +diff --git a/gcc/config/aarch64/driver-aarch64.c b/gcc/config/aarch64/driver-aarch64.c +index 4c1e42edf..317802d6e 100644 +--- a/gcc/config/aarch64/driver-aarch64.c ++++ b/gcc/config/aarch64/driver-aarch64.c +@@ -33,7 +33,7 @@ std::string aarch64_get_extension_string_for_isa_flags (unsigned long, + struct aarch64_arch_extension + { + const char *ext; +- unsigned int flag; ++ uint64_t flag; + const char *feat_string; + }; + +@@ -53,7 +53,7 @@ struct aarch64_core_data + unsigned char implementer_id; /* Exactly 8 bits */ + unsigned int part_no; /* 12 bits + 12 bits */ + unsigned variant; +- const unsigned long flags; ++ const uint64_t flags; + }; + + #define AARCH64_BIG_LITTLE(BIG, LITTLE) \ +@@ -76,7 +76,7 @@ struct aarch64_arch_driver_info + { + const char* id; + const char* name; +- const unsigned long flags; ++ const uint64_t flags; + }; + + #define AARCH64_ARCH(NAME, CORE, ARCH_IDENT, ARCH_REV, FLAGS) \ +@@ -261,8 +261,8 @@ host_detect_local_cpu (int argc, const char **argv) + unsigned int variants[2] = { ALL_VARIANTS, ALL_VARIANTS }; + unsigned int n_variants = 0; + bool processed_exts = false; +- unsigned long extension_flags = 0; +- unsigned long default_flags = 0; ++ uint64_t extension_flags = 0; ++ uint64_t default_flags = 0; + std::string buf; + size_t sep_pos = -1; + char *fcpu_info; +diff --git a/gcc/config/aarch64/hip12.md b/gcc/config/aarch64/hip12.md +new file mode 100644 +index 000000000..859a87b97 +--- /dev/null ++++ b/gcc/config/aarch64/hip12.md +@@ -0,0 +1,891 @@ ++;; hip12 pipeline description ++;; Copyright (C) 2023 Free Software Foundation, Inc. ++;; ++;;Contributed by liyunfei ++;; ++;; 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 ++;; . ++ ++(define_automaton "hip12") ++(define_automaton "hip12_ldst") ++(define_automaton "hip12_v") ++ ++; The hip12 core is modelled as issues pipeline that has ++; the following functional units. ++; 1. 4 pipelines for single cycle integer micro operations: ALU0, ALU1, ALU3, ALU4 ++ ++(define_cpu_unit "hip12_alu0" "hip12") ++(define_cpu_unit "hip12_alu1" "hip12") ++(define_cpu_unit "hip12_alu3" "hip12") ++(define_cpu_unit "hip12_alu4" "hip12") ++ ++ (define_reservation "hip12_alu0134" "hip12_alu0|hip12_alu1|hip12_alu3|hip12_alu4") ++(define_reservation "hip12_alu14" "hip12_alu1|hip12_alu4") ++ ++; 2. 2 pipelines for multi cycles integer micro operations: ALU2, ALU5 ++ ++(define_cpu_unit "hip12_alu2" "hip12") ++(define_cpu_unit "hip12_alu5" "hip12") ++ ++(define_reservation "hip12_alu25" "hip12_alu2|hip12_alu5") ++(define_reservation "hip12_alu1425" "hip12_alu1|hip12_alu4|hip12_alu2|hip12_alu5") ++ ++; 3. All ALU pipelines ++ ++(define_reservation "hip12_alu" "hip12_alu0|hip12_alu1|hip12_alu2|hip12_alu3|hip12_alu4|hip12_alu5") ++ ++; 4. 3 pipelines for load micro opetations: Load0, Load1, Load2 ++ ++(define_cpu_unit "hip12_load0" "hip12_ldst") ++(define_cpu_unit "hip12_load1" "hip12_ldst") ++(define_cpu_unit "hip12_load2" "hip12_ldst") ++ ++(define_reservation "hip12_ld" "hip12_load0|hip12_load1|hip12_load2") ++ ++; 5. 2 pipelines for store micro operations: Store1, Store2 ++ ++(define_cpu_unit "hip12_store0" "hip12_ldst") ++(define_cpu_unit "hip12_store1" "hip12_ldst") ++ ++(define_reservation "hip12_st" "hip12_store0|hip12_store1") ++ ++; 6. 2 pipelines for store data micro operations: STD0, STD1 ++ ++(define_cpu_unit "hip12_store_data0" "hip12_ldst") ++(define_cpu_unit "hip12_store_data1" "hip12_ldst") ++ ++(define_reservation "hip12_std" "hip12_store_data0|hip12_store_data1") ++ ++; 7. 4 asymmetric pipelines for Asimd/FP/SVE micro operations: V0, V1, V2, V3 ++ ++(define_cpu_unit "hip12_v0" "hip12_v") ++(define_cpu_unit "hip12_v1" "hip12_v") ++(define_cpu_unit "hip12_v2" "hip12_v") ++(define_cpu_unit "hip12_v3" "hip12_v") ++ ++(define_reservation "hip12_v0123" "hip12_v0|hip12_v1|hip12_v2|hip12_v3") ++(define_reservation "hip12_v02" "hip12_v0|hip12_v2") ++ ++; 8. 2 pipelines for branch operations: Branch0, Branch1 ++ ++(define_cpu_unit "hip12_b0" "hip12") ++(define_cpu_unit "hip12_b1" "hip12") ++ ++(define_reservation "hip12_b" "hip12_b0|hip12_b1") ++ ++;; Block all issue queues. ++ ++(define_reservation "hip12_block" " ++ hip12_alu0+hip12_alu1+hip12_alu2+hip12_alu3 ++ +hip12_alu4+hip12_alu5+hip12_load0+hip12_load1+hip12_load2+hip12_store0+hip12_store1+hip12_store_data0+hip12_store_data1+hip12_v0+hip12_v1+hip12_v2+hip12_v3") ++ ++;; Branch execution Unit ++ ++(define_insn_reservation "hip12_branch" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "branch")) ++ "hip12_b") ++ ++(define_insn_reservation "hip12_branch_and_link" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "call")) ++ "hip12_b+hip12_alu14") ++ ++;; Integer arithmetic/logic instructions. ++ ++(define_insn_reservation "hip12_alu_basic" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "alu_imm,alu_sreg,\ ++ adc_reg,adc_imm")) ++ "hip12_alu") ++ ++(define_insn_reservation "hip12_alu_basic_flagset" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "alus_imm,alus_sreg,\ ++ adcs_reg,adcs_imm")) ++ "hip12_alu1425") ++ ++(define_insn_reservation "hip12_alu_basic_extend" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "alu_ext,alus_ext,\ ++ alu_shift_imm,\ ++ alu_shift_reg,alus_shift_imm,alus_shift_reg")) ++ "hip12_alu25") ++ ++(define_insn_reservation "hip12_alu_logical" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "logic_reg,logic_imm")) ++ "hip12_alu") ++ ++(define_insn_reservation "hip12_alu_logical_imm" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "logic_imm")) ++ "hip12_alu14") ++ ++(define_insn_reservation "hip12_alu_logical_flagset" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "logics_reg")) ++ "hip12_alu1425") ++ ++(define_insn_reservation "hip12_alu_logical_flagset_imm" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "logics_imm")) ++ "hip12_alu25") ++ ++(define_insn_reservation "hip12_alu_conditional" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "csel")) ++ "hip12_alu14") ++ ++;; Divide and Multiply instructions. ++ ++(define_insn_reservation "hip12_divide" 8 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "sdiv,udiv")) ++ "hip12_alu25") ++ ++(define_insn_reservation "hip12_multiply" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "mul,muls")) ++ "hip12_alu25") ++ ++(define_insn_reservation "hip12_multiply_long" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "smull,umull,smulls,umulls")) ++ "hip12_alu25") ++ ++(define_insn_reservation "hip12_multiply_accumulate" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "mla,mlas")) ++ "hip12_alu25+hip12_alu0134") ++ ++(define_insn_reservation "hip12_multiply_accumulate_long" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "smlal,umlal")) ++ "hip12_alu25+hip12_alu0134") ++ ++;; no Pointer Authentication instructions in backend types. ++ ++;; Miscellaneous Data-Processing instructions. ++ ++(define_insn_reservation "hip12_address" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "adr")) ++ "hip12_alu14") ++ ++(define_insn_reservation "hip12_bitfield" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "bfm,bfx")) ++ "hip12_alu14") ++ ++;; Todo: Does hip12 have reg move or mvn instructions? ++(define_insn_reservation "hip12_move" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "mov_imm,mov_shift_reg")) ++ "hip12_alu") ++ ++(define_insn_reservation "hip12_count_leading" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "clz")) ++ "hip12_alu14") ++ ++(define_insn_reservation "hip12_reverse_bits_bytes" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "rbit,rev")) ++ "hip12_alu14") ++ ++; Todo: Does hip12 have imm shift instructions? ++(define_insn_reservation "hip12_variable_shift" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "shift_reg")) ++ "hip12_alu14") ++ ++; Block all issue pipes for a cycle ++(define_insn_reservation "hip12_block" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "block")) ++ "hip12_block") ++ ++;; Load and Store instructions. ++ ++(define_insn_reservation "hip12_load_register" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "load_4,load_8")) ++ "hip12_ld") ++ ++(define_insn_reservation "hip12_load_pair" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "load_16")) ++ "hip12_ld") ++ ++(define_insn_reservation "hip12_store" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "store_4,store_8")) ++ "hip12_st+hip12_std") ++ ++(define_insn_reservation "hip12_store_pair" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "store_16")) ++ "hip12_st+hip12_std") ++ ++;; FP Data Processing instructions. ++; abs/neg/cpy ++(define_insn_reservation "hip12_fp_arith" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "ffariths,ffarithd")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_compare" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fcmpd,fcmps")) ++ "hip12_v02+hip12_alu0134") ++ ++(define_insn_reservation "hip12_fp_conditional_compare" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fccmpd,fccmps")) ++ "hip12_alu14,hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_conditional_select" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fcsel")) ++ "hip12_alu14,hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_divide_single" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fdivs")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_divide_double" 8 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fdivd")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_square_single" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fsqrts")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_square_double" 8 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fsqrtd")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_fused_multiply_add" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "ffmad,ffmas")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_max_min" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_minmaxd,f_minmaxs")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_add" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fadds,faddd")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_multiply" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fmuld,fmuls")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_round_int" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_rintd,f_rints")) ++ "hip12_v0123") ++ ++;; FP Miscellaneous instructions. ++ ++(define_insn_reservation "hip12_fp_covert_i2f" 7 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_cvti2f")) ++ "hip12_alu14,hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_covert_f2i" 5 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_cvtf2i")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_covert_f2f" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_cvt")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_move" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "fmov")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_transfer_arm2vfp" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_mcr")) ++ "hip12_alu14") ++ ++; transfer low half + high half ++(define_insn_reservation "hip12_fp_transfer_2arm2vfp" 10 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_mcrr")) ++ "hip12_alu14,nothing*3,hip12_alu14,hip12_v0123") ++ ++(define_insn_reservation "hip12_fp_transfer_vfp2arm" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_mrc,f_mrrc")) ++ "hip12_v0123") ++ ++;; FP Load instructions. ++; only basic double/single load ++(define_insn_reservation "hip12_fp_load" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_loadd,f_loads")) ++ "hip12_ld") ++ ++(define_insn_reservation "hip12_fp_load_vector_pair" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_ldr,neon_ldp,neon_ldp_q")) ++ "hip12_alu+hip12_ld") ++ ++;; FP Store instructions. ++; only basic double/single store ++(define_insn_reservation "hip12_fp_store" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "f_stored,f_stores")) ++ "hip12_st+hip12_std") ++ ++(define_insn_reservation "hip12_fp_store_vector" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_ldr")) ++ "hip12_alu+hip12_st+hip12_std") ++ ++(define_insn_reservation "hip12_fp_store_pair" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_ldp,neon_ldp_q")) ++ "hip12_st+hip12_std+hip12_alu") ++ ++;; ASIMD Int instructions. ++ ++(define_insn_reservation "hip12_neon_absolute_diff" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_abd,neon_abd_q,neon_abd_long")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_arith_basic" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_abs,neon_abs_q,\ ++ neon_add,neon_add_q,\ ++ neon_sub,neon_sub_q,\ ++ neon_neg,neon_neg_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_arith_long" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_add_long,neon_sub_long")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_arith_wide" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_add_widen,neon_sub_widen")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_arith_complex" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_qadd,neon_qadd_q,\ ++ neon_qsub,neon_qsub_q,\ ++ neon_qneg,neon_qneg_q,\ ++ neon_qabs,neon_qabs_q")) ++ "hip12_v0123") ++; arith pair not specified ++ ++; neon_reduc_add is used for both addp and [su]adalp ++(define_insn_reservation "hip12_neon_arith_reduce" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_reduc_add,neon_reduc_add_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_arith_cmp" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_compare,neon_compare_q,neon_compare_zero,\ ++ neon_tst,neon_tst_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_arith_dot" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_dot,neon_dot_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_logical" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_logic,neon_logic_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_multiply_accumulate" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_mla_b,neon_mla_b_q,\ ++ neon_mla_h,neon_mla_h_q,\ ++ neon_mla_s,neon_mla_s_q,\ ++ neon_mla_b_long,neon_mla_h_long,\ ++ neon_mla_s_long,neon_mla_h_scalar,\ ++ neon_mla_h_scalar_q,neon_mla_s_scalar,\ ++ neon_mla_s_scalar_q,neon_mla_h_scalar_long,\ ++ neon_mla_s_scalar_long,neon_sat_mla_b_long,\ ++ neon_sat_mla_h_long,neon_sat_mla_s_long,\ ++ neon_sat_mla_h_scalar_long,neon_sat_mla_s_scalar_long")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_minmax" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_minmax,neon_minmax_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_minmax_reduce" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_reduc_minmax,neon_reduc_minmax_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_multiply" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_mul_b,neon_mul_b_q,\ ++ neon_mul_h,neon_mul_h_q,\ ++ neon_mul_s,neon_mul_s_q,\ ++ neon_mul_b_long,neon_mul_h_long,\ ++ neon_mul_s_long,neon_mul_d_long,\ ++ neon_mul_h_scalar,neon_mul_h_scalar_q,\ ++ neon_mul_s_scalar,neon_mul_s_scalar_q,\ ++ neon_mul_h_scalar_long,neon_mul_s_scalar_long,\ ++ neon_sat_mul_b,neon_sat_mul_b_q,\ ++ neon_sat_mul_h,neon_sat_mul_h_q,\ ++ neon_sat_mul_s,neon_sat_mul_s_q,\ ++ neon_sat_mul_b_long,neon_sat_mul_h_long,\ ++ neon_sat_mul_s_long,neon_sat_mul_h_scalar,\ ++ neon_sat_mul_h_scalar_q,neon_sat_mul_s_scalar,\ ++ neon_sat_mul_s_scalar_q,neon_sat_mul_h_scalar_long,\ ++ neon_sat_mul_s_scalar_long")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_shift" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_shift_imm,neon_shift_imm_q,\ ++ neon_shift_imm_narrow_q,neon_shift_imm_long,\ ++ neon_shift_reg,neon_shift_reg_q,\ ++ neon_sat_shift_imm,neon_sat_shift_imm_q,\ ++ neon_sat_shift_imm_narrow_q,neon_sat_shift_reg,\ ++ neon_sat_shift_reg_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_shift_accumulate" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_shift_acc,neon_shift_acc_q")) ++ "hip12_v0123") ++ ++;; ASIMD FP instructions. ++ ++(define_insn_reservation "hip12_neon_fp_abs" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_abs_s,neon_fp_abs_s_q,\ ++ neon_fp_abs_d,neon_fp_abs_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_neg" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_neg_s,neon_fp_neg_s_q,\ ++ neon_fp_neg_d,neon_fp_neg_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_abd" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_abd_s,neon_fp_abd_s_q,\ ++ neon_fp_abd_d,neon_fp_abd_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_arith" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_addsub_s,neon_fp_addsub_s_q,\ ++ neon_fp_addsub_d,neon_fp_addsub_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_compare" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_compare_s,neon_fp_compare_s_q,\ ++ neon_fp_compare_d,neon_fp_compare_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_convert_narrow" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_cvt_narrow_s_q,neon_fp_cvt_narrow_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_convert_2int" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_to_int_s,neon_fp_to_int_d")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_convert_2int_q" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_to_int_s_q,neon_fp_to_int_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_convert_from_int" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_int_to_fp_s,neon_int_to_fp_d")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_convert_from_int_q" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_int_to_fp_s_q,neon_int_to_fp_d_q")) ++ "hip12_v0123") ++ ++; D/F32 ++(define_insn_reservation "hip12_neon_fp_divide_s" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_div_s")) ++ "hip12_v0123") ++ ++; Q/F32 ++(define_insn_reservation "hip12_neon_fp_divide_s_q" 7 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_div_s_q")) ++ "hip12_v0123") ++ ++; Q/F64 ++(define_insn_reservation "hip12_neon_fp_divide_d" 9 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_div_d,neon_fp_div_d_q")) ++ "hip12_v0123") ++ ++; D/F32 ++(define_insn_reservation "hip12_neon_fp_sqrt_s" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_sqrt_s")) ++ "hip12_v0123") ++ ++; Q/F32 ++(define_insn_reservation "hip12_neon_fp_sqrt_s_q" 7 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_sqrt_s_q")) ++ "hip12_v0123") ++ ++; Q/F64 ++(define_insn_reservation "hip12_neon_fp_sqrt_d" 9 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_sqrt_d,neon_fp_sqrt_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_minmax" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_minmax_s,neon_fp_minmax_s_q,\ ++ neon_fp_minmax_d,neon_fp_minmax_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_minmax_reduce" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_reduc_minmax_s,neon_fp_reduc_minmax_s_q,\ ++ neon_fp_reduc_minmax_d,neon_fp_reduc_minmax_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_multiply" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_mul_s,neon_fp_mul_s_q,\ ++ neon_fp_mul_s_scalar,neon_fp_mul_s_scalar_q,\ ++ neon_fp_mul_d,neon_fp_mul_d_q,\ ++ neon_fp_mul_d_scalar_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_multiply_add" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_mla_s,neon_fp_mla_s_q,\ ++ neon_fp_mla_s_scalar,neon_fp_mla_s_scalar_q,\ ++ neon_fp_mla_d,neon_fp_mla_d_q,\ ++ neon_fp_mla_d_scalar_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_round" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_round_s,neon_fp_round_d")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_round_q" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_round_s_q,neon_fp_round_d_q")) ++ "hip12_v0123") ++ ++;; ASIMD Miscellaneous instructions ++ ++(define_insn_reservation "hip12_neon_bit_reverse" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_rbit,neon_rbit_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_bitwise_insert" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_bsl,neon_bsl_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_count" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_cls,neon_cls_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_count_ds" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_cnt_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_count_bh" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_cnt")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_duplicate" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_dup,neon_dup_q")) ++ "(hip12_v0123)+hip12_alu0134") ++ ++(define_insn_reservation "hip12_neon_extract" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_dup,neon_dup_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_insert" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_ins,neon_ins_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_move" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_move,neon_move_q,neon_move_narrow_q")) ++ "hip12_v0123") ++ ++; gcc only gen neon fp recp ++(define_insn_reservation "hip12_neon_fp_recp" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_recpe_s,neon_fp_recpe_d,\ ++ neon_fp_rsqrte_s,neon_fp_rsqrte_d")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_recp_q" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_recpe_s_q,neon_fp_recpe_d_q,\ ++ neon_fp_rsqrte_s_q,neon_fp_rsqrte_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_recpx" 3 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_recpx_s,neon_fp_recpx_s_q,\ ++ neon_fp_recpx_d,neon_fp_recpx_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_fp_recps" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_fp_recps_s,neon_fp_recps_s_q,\ ++ neon_fp_recps_d,neon_fp_recps_d_q,\ ++ neon_fp_rsqrts_s,neon_fp_rsqrts_s_q,\ ++ neon_fp_rsqrts_d,neon_fp_rsqrts_d_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_rev" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_rev,neon_rev_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_tbl_12" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_tbl1,neon_tbl1_q,\ ++ neon_tbl2,neon_tbl2_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_tbl_3" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_tbl3,neon_tbl3_q,\ ++ neon_tbl2,neon_tbl2_q")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_tbl_4" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_tbl4,neon_tbl4_q,\ ++ neon_tbl2,neon_tbl2_q")) ++ "hip12_v0123") ++; gcc only gen neon tbl, no tbx ++ ++; no neon transfer specified ++ ++(define_insn_reservation "hip12_neon_zip" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_zip,neon_zip_q")) ++ "hip12_v0123") ++ ++;; ASIMD Load instructions. ++ ++(define_insn_reservation "hip12_neon_ld1_12reg" 6 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_load1_1reg,neon_load1_1reg_q,\ ++ neon_load1_2reg,neon_load1_2reg_q")) ++ "hip12_ld") ++ ++(define_insn_reservation "hip12_neon_ld1_34reg" 7 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_load1_3reg,neon_load1_3reg_q,\ ++ neon_load1_4reg,neon_load1_4reg_q")) ++ "hip12_ld") ++ ++(define_insn_reservation "hip12_neon_ld1_lane" 8 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_load1_all_lanes,neon_load1_all_lanes_q,\ ++ neon_load1_one_lane,neon_load1_one_lane_q")) ++ "hip12_ld+(hip12_v0123)") ++ ++(define_insn_reservation "hip12_neon_ld2" 8 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_load2_2reg,neon_load2_2reg_q,\ ++ neon_load2_4reg,neon_load2_4reg_q,\ ++ neon_load2_all_lanes,neon_load2_all_lanes_q,\ ++ neon_load2_one_lane,neon_load2_one_lane_q")) ++ "(hip12_ld)+(hip12_v0123)") ++ ++(define_insn_reservation "hip12_neon_ld3" 8 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_load3_3reg,neon_load3_3reg_q,\ ++ neon_load3_all_lanes,neon_load3_all_lanes_q,\ ++ neon_load3_one_lane,neon_load3_one_lane_q")) ++ "hip12_ld+hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_ld4_reg" 10 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_load4_4reg,neon_load4_4reg_q")) ++ "hip12_ld+hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_ld4_lane" 8 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_load4_all_lanes,neon_load4_all_lanes_q,\ ++ neon_load4_one_lane,neon_load4_one_lane_q")) ++ "hip12_ld+hip12_v0123") ++ ++;; ASIMD Load instructions. ++ ++(define_insn_reservation "hip12_neon_st1_12reg_4reg" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_store1_1reg,neon_store1_1reg_q,\ ++ neon_store1_2reg,neon_store1_2reg_q,\ ++ neon_store1_4reg")) ++ "hip12_st+hip12_std+hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_st1_3reg" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_store1_3reg,neon_store1_3reg_q")) ++ "hip12_st+hip12_std+hip12_v0123") ++ ++(define_insn_reservation "hip12_neon_st1_4reg_q" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_store1_4reg_q")) ++ "hip12_st+hip12_std") ++ ++(define_insn_reservation "hip12_neon_st1_lane_st2" 1 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_store1_one_lane,neon_store1_one_lane_q,\ ++ neon_store2_2reg,neon_store2_2reg_q,\ ++ neon_store2_4reg,neon_store2_4reg_q,\ ++ neon_store2_one_lane,neon_store2_one_lane_q")) ++ "hip12_st+hip12_std") ++ ++(define_insn_reservation "hip12_neon_st3_st4_q" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_store3_3reg,neon_store3_3reg_q,\ ++ neon_store3_one_lane,neon_store3_one_lane_q,\ ++ neon_store4_4reg_q")) ++ "hip12_v0123+hip12_st+hip12_std") ++ ++(define_insn_reservation "hip12_neon_st4" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "neon_store4_4reg,\ ++ neon_store4_one_lane,neon_store4_one_lane_q")) ++ "hip12_v0123+hip12_st+hip12_std") ++ ++;; Cryptography Extensions ++ ++(define_insn_reservation "hip12_crypto_aes" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_aese,crypto_aesmc")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_crypto_pmull" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_pmull")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_crypto_sha1_fast" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sha1_fast,crypto_sha1_xor")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_crypto_sha256_fast" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sha256_fast")) ++ "hip12_v02") ++ ++(define_insn_reservation "hip12_crypto_complex_1" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sha1_slow")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_crypto_complex_256" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sha256_slow")) ++ "hip12_v02") ++ ++(define_insn_reservation "hip12_crypto_sha512" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sha512")) ++ "hip12_v02") ++ ++(define_insn_reservation "hip12_crypto_sha3" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sha3")) ++ "hip12_v0123") ++ ++(define_insn_reservation "hip12_crypto_sm3" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sm3")) ++ "hip12_v02") ++ ++(define_insn_reservation "hip12_crypto_sm4" 4 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crypto_sm4")) ++ "hip12_v0123") ++ ++;; CRC instructions ++ ++(define_insn_reservation "hip12_crc" 2 ++ (and (eq_attr "tune" "hip12") ++ (eq_attr "type" "crc")) ++ "hip12_alu25") ++ ++;; Simple execution unit bypasses ++(define_bypass 2 "hip12_fp_fused_multiply_add" ++ "hip12_fp_fused_multiply_add") ++ ++(define_bypass 2 "hip12_neon_arith_dot" ++ "hip12_neon_arith_dot") ++ ++(define_bypass 2 "hip12_neon_multiply_accumulate" ++ "hip12_neon_multiply_accumulate") ++ ++(define_bypass 1 "hip12_neon_shift_accumulate" ++ "hip12_neon_shift_accumulate") ++ ++(define_bypass 2 "hip12_neon_fp_multiply_add" ++ "hip12_neon_fp_multiply_add") ++ ++(define_bypass 2 "hip12_neon_fp_recps" ++ "hip12_neon_fp_recps") +\ No newline at end of file +diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi +index 4e7b657a9..5be6e0399 100644 +--- a/gcc/doc/invoke.texi ++++ b/gcc/doc/invoke.texi +@@ -15090,6 +15090,17 @@ generation. This option is enabled by default for @option{-march=armv8.5-a}. + Enable the Armv8-a Execution and Data Prediction Restriction instructions. + This option is only to enable the extension at the assembler level and does + not affect code generation. This option is enabled by default for ++@item sve2 ++Enable the Armv8-a Scalable Vector Extension 2. This also enables SVE ++instructions. ++@item bitperm ++Enable SVE2 bitperm instructions. This also enables SVE2 instructions. ++@item sve2-sm4 ++Enable SVE2 sm4 instructions. This also enables SVE2 instructions. ++@item sve2-aes ++Enable SVE2 aes instructions. This also enables SVE2 instructions. ++@item sve2-sha3 ++Enable SVE2 sha3 instructions. This also enables SVE2 instructions. + @option{-march=armv8.5-a}. + + @end table +diff --git a/gcc/gcc.c b/gcc/gcc.c +index 095aadb4d..7c3ba638c 100644 +--- a/gcc/gcc.c ++++ b/gcc/gcc.c +@@ -43,6 +43,7 @@ compilation is specified by a string called a "spec". */ + #include "params.h" + #include "filenames.h" + #include "spellcheck.h" ++#include "ai4c-infer.h" + + + +@@ -4971,6 +4972,17 @@ do_self_spec (const char *spec) + do_spec_2 (spec); + 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] = { ++ PARAM_VALUE_SIMULTANEOUS_PREFETCHES, ++ PARAM_VALUE_L1_CACHE_SIZE, ++ PARAM_VALUE_L1_CACHE_LINE_SIZE, ++ PARAM_VALUE_L2_CACHE_SIZE, ++ PARAM_VALUE_PREFETCH_LATENCY, ++ PARAM_VALUE_PREFETCH_MIN_INSN_TO_MEM_RATIO}; ++ ++ 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 +@@ -7311,6 +7406,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 (!maybe_print_and_exit ()) + return 0; +diff --git a/gcc/gcc.h b/gcc/gcc.h +index ddbf42f78..ce0fc6f4a 100644 +--- a/gcc/gcc.h ++++ b/gcc/gcc.h +@@ -48,6 +48,7 @@ class driver + void build_option_suggestions (void); + const char *suggest_option (const char *bad_opt); + 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-hardware-detection.c b/gcc/ipa-hardware-detection.c +index 79a4ce9c4..9166b72bd 100644 +--- a/gcc/ipa-hardware-detection.c ++++ b/gcc/ipa-hardware-detection.c +@@ -73,7 +73,7 @@ create_part_bb (basic_block last_bb, tree part_base) + &gsi, PLUS_EXPR, unsigned_type_node, part_base, + build_int_cst (unsigned_type_node, 4294963967)); + gcond *cond = gimple_build_cond (LE_EXPR, part_cond, +- build_int_cst (unsigned_type_node, 2), ++ build_int_cst (unsigned_type_node, 128), + NULL_TREE, NULL_TREE); + gimple_set_location (cond, input_location); + gsi_insert_before (&gsi, cond, GSI_SAME_STMT); +@@ -177,7 +177,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/optimizer.fdata b/gcc/optimizer.fdata +new file mode 100644 +index 000000000..adc2f5399 +--- /dev/null ++++ b/gcc/optimizer.fdata +@@ -0,0 +1 @@ ++363632656234393365653936383862396662623134303865623332333461363066633935643264653466326132626331353831633763636239616630353032653233326432366139383465346338376266393132363438333765656463366235613461313434346139333334396265306163333731646537376430643834323664623863366163343363643130313435636565623834363361316133393230363937653835653762353534626439663133633538623062353439646237616630333237666136663433386334626639643465303163653832333062643863333664336630376231643964316231663933656333386338656262303734376137313565643963396535653131303763646533393234333735613333633132353061393531333935623539643834373266303861633739373862366663376365383233326139383939363566373061373361613939336537366631353334346563313061373365663635633332663437653136383235343635623234366430373330366336363237623962656465373233346131343264313137653838643334616430346339363732613237623866636364313232613934343261643231386531356430343965303330306332326266336634626163333461643139653962326566303064343333623037313762303934626336363537616339343637306633633066333231613063623339333539376461316632653234353938616133616463623534356232346135666261616339646638373031356633306161626465643665633066616264623965656138613233353331303236363565616133323131653935643363353832366663633434626236376663386335666364333530336433353234383031636264353761616638663031613263353738326438656265623236653338323232386565626464393034633962373835363264656664616439353336623462376139333134656662373033626135336138333136643032636430653334303861616439333736306363383862306439623962646435383931613161656137356462666463346364333361623035396139323366643262383764363763323530613631653861653634666630333030316562323662346133633566316533346236663132363663663739623565366564343962313033386462653236663864613664303962623535363134336361326534666230613461643036356361623365363830623066373064386262326466363934623535633664373865313164633832393733616432306435663337366633306565386434613131613061636663616630643839623737366361623235653838653366303334613439393362313664373862653234396263323361663762626238373531633765333530666363393132333237616137663336333434326233313735303764323765346238313333393839646334396539663036633034396237393461336263306463333738313939303164393262336465656237616466643562303362333632303838336162666434636265363361333466646564333736303437383432623738636461313736336539396364306164393937313064353136643231663135306364643936646137386434613564373335643430363162623534623133366633353137633236613635643433306239653638666165343231636163376139643564653333336637393138356233633164623465396435396364353031663066643661353130633336303365313365323461343062346266323135653337393561396134306436306138343536666237623633633264313165383137303161323331653835616664346235646238316162633164643536643237313733326564303234356638363931626463356461643334373664366636333536303737623161613164363334613937633431303962353962373335343766393430633164633864323163306631383733383866326433613865656366656537666438363739303266643461633331626131633966646635316261613261343337626663303932613166373332653261386630363637363337343262303737306136653236653332663262653565343761333733303934663366396234313838633834336435633462656135613261346163346262626466376661633763653339623230363235373337373135343130663233646239373035346236656232373065666363666237636234313736663832363831343263656131366532386232316463393539383730306566316462386539616434346537653138343162313462313235636366333561313765386366666138663036383233626434366366383536653461663236336238616166393638626138353062346439616666626563343933666130666530613833336539653436336538376132636365306338633038613133636363613461326134313639623365396134633133666339346337613637616230393963653932636336386561343761626166313638386162386131373230326638646130353133633533353964323735303632336632343431363463396634353338346566303665373533363061373664346462643034663036336636303863636363373830663166313238363531353931346461393730353236373461643039653235366663353938646363643464333835646164353838623664643261303365363139346361393864383734623331623662306231653235333362633134303864306436613862616330613136316136393132323137343831363364646362313231303362316131313733366362386566353536356266663035356633313633323533383133633963343136313330346561336663653535616438303565656331373466386437396361363764323037616334366664653362323562366531643531633464616561653330386532663361613839666339613234623762623864303635363239323732633437313135623561393362653837653361396438373339666232653861316665613661613331383532616631393033383937333463353139343637613931376332666164316634363432366132316638656362363461336166626237613736336638306232313930636165346330323165386264366337316432613138666663393538363731333639396239666362393336373737643238636639643761343231346131333463353261623633343633343866663966663365346237313164613933626232343636366137616430363063643632626237306335353536396233353566633932393964346162636636376563656530396161306664656561626436396635656632346534653861376262386430376664633866626464346666653730326362316232653965343564313031306138623562363130623230313963396265366130303837616232366634326335646531303539353531353137366562353736323737393436626630343433346330356264653562376231356532663139306164303361383836396333393339616236383439363661313661303665643535633961666563613431303466333534346564633533373866306361633035363431646633643837383532663863303765623439623930373061333833633261383932383764356133323539336632353130303266643661643531313731316638343339653162613735393261653833333065316432643865616634333936326236626432663237656439643439633335656534373338313766633338646231316433363030393633613732386331363461303139393161316436323764326235316237613364393537303630633634616137656338386231636561313937653562653562623466633136316330383533636239353561353063623464363362636438656639613233323237373363323039386566366463587754430153274178ab274197be33430e5f1545710254412d9ade3b12f1c13df163bf3d6fc4c73bf7cbb4385e4ed83d0000803f2cb90e3da388cd3cc0d6913df226b13b37ea123df90a1fbdd201113df1081dbcfb240f3d5f280f3dfeddc33cb6dbcc3ca32f563d8f1439bd8f63c43c80ad8abb5381bd3ce8fd2bbc269fcd3cb97ec33c6ec41a3dc386f53c3d5d4d3d6cc298bb9f961a3db4aab0bc95381e3d5729653d44681d3dee941a3d90433fbc70e5923b3d7dbf3cff8520be91bf6dbc27c7c83b5d16f3bcca7df0bc45588fbccd02babc4734773bdea39e3b21d98cbc0efb3bbf915e7f3cbd8a693e81270f3df4800ebcfe88c53c1388723d8111eb3cc873ea3cd5229d3de385e33b7b1ff13c1f0716bde8d4ef3c7d7995bd3a59ef3c01ebed3ca01d9d3e56e4903efb8d87bf0ef454be0ec09f3e0fc3ba3d45b5853e4e5451be51c2933effca853e2089ba3e3c499f3eeca07abfd7e185bed66aba3e803e993c803c9f3e59ad82be5a3aad3e19029e3e40b19a3efa1c8f3e7f218ebf3d244abee2fa9b3e4c9ded3dafba803e967047be2b3c8e3e2442803ee242b13ec290993e4ed57dbf85b277beef3cb13e21ed283de6d4963eb12075be9ca4a43e7c86963e169ca43edf468d3e344288bf0a635fbe2d8da43e240a9a3dfe158b3e4e8862be131a983ede6d8a3e35359e3e6468843e6c3789bf41f853bea7ae9d3e4a86ec3d5b5a843e487c52be8037913e3569843e136ba13eb5b08c3e032d8cbfc8205abe124da13e52e7a33d2958873e76d758bed14e943eef50863e5b649a3e64bd9b3e616e88bfee8256be1afa993ed799e23de378833e478047be97698f3e063d833eefbea03e19e48e3ee8b784bf3e7294be1e05a03e55c5c83dbad5863e94de53bea379943e7209873ef5f9b23e1d759a3ea97e80bf32fb77becb04b33eb3e42b3d4049983e3eab73be7e12a63e9740973ecb00c43eb4cdd7be03fa8fbf8ad246be78a2b63ea3944e3e917d8d3e408b3bbeb9edad3e2c76933ee961ad3e6df49d3ec32481bf59c66ebe9b25ac3e42056c3df161933e17306ebe2feca03e2276923ea226ac3e6864953e869380bfca3a6fbed434ac3e93cf713dc559913e261366be5dcb9e3ecf8c8f3e8308303d1fa8f73c29b77a3d304de5bcc204303dd90991bd8200233dcf8b04bdfd2a2a3d3a5e223da196eb3cd8857e3cae62133d43e191bc3cc4eb3c88b65bbd0ec7d23c208ca8bc9c4ddf3c123fd23c388fd23c0543df3b72d31e3dd47550bc9099d23cb8078fbd9f0aba3c27607fbca930c73cbef0b83c4792f73c5ac3653ccce6d93cbd6898bcc4a1f73c17056dbd3133de3c99d0afbc2a31eb3cc8bfdc3cd64dc63c3223893c15f9053dcda1ceba5355c63cf64b8dbdd8bcac3c21517ebc3385ba3c287dab3cdf90923c831a0c3cc103253d785cd6bb6985923c6b0005bddf89743cc32410bce514873c708b813cd98bfcbbc6f28fbcc4494dbcd18fc53c7a33fcbb31fcc6bb639330bc1387963c4b7115bcbe7831bc34a0ca3cf3eaaa3b8c70263d7d688ebcd3baca3ced9ab2bd1af8bd3ca95b8abcc5dfc03c4e19c33c3e57c33c6d7e153c88ce473dc2c661bd4149c33cec8e39bdbbb4a83c79665bbcb664b63c9306a73ca846bc3c5e51bf3a57a7143d803c5d3b311dbc3c632858bd48fc9d3cdbf91fbcdca2b03c3ff0a33c5ee8143d5b98953c26eba93ce07bc33c9536153d64a087bd8fe1083dffb8dfbc21610f3dd624083d3cca21bcc31ee3bc7851873df730ebbdd9c821bce66076bb12fd52bc0b40943c48e339bc50e855bc7f51a63cf3c6fd3b80c6103d93bb3fbc0340a63cf3e489bdc830913c236b5dbc1a3f9d3ca1b3903cf7b4b23c1cdf853bd0845c3dd0bd8abc58a0b23c3aab2abdf0a89c3c348e66bcdf4ea93cf25a9b3caae50b3d52dd443c2cffa93ce424c03c51d10b3d8acab3bdeb00013d3a51d0bc84f9063d065f003d4303a73c7df79d3bc4fc0b3d804fbd3b0a15a73c82906cbdfd808b3cb7842bbccd80993cbed8893ce798d43c2d2b863cd80e133d73b586bc5ca8d43c09f402bd32ecbc3c383f84bcf163ca3c89e1bc3cb1dad63cd182ed3bdcc55a3de3801ebc1072d63ccba849bd60c8b83cfb299bbcc59fc73caeaeb63c6acc833cc716c9bbef34493d4061b83a43ea833c0617b0bde43c503c90da2cbb06d46e3cb3b3513c783b063d3d5b38bbe97b9d3dc22094bceb19063d1c62c6bdad65f33c0a63bdbc4fe8ff3cacc4f13c930c5d3c8bfe113b84e0443d81407dbb78d25c3c206262bde4282d3c823e5bbbe042483ce69a2b3c3651013d9acd873c07ac0d3d5ba1323bbc47013db7acaebd5359ec3c7a63bbbc53a4f73c704aeb3ce846aa3e5bc5933e155c81bf15296abef633aa3ea748863d5e938f3e088a67be4e479d3e0e278e3e2bb19d3e9d73c43ecbae8cbfd56543be6633a03eb6180b3e27f68c3e98f639be73119c3e9841873e0105a03eae559e3e0be287bfb3ef5dbe5700a03ec1a5a33db862863ed8b558be0436933e5b2d853e25d0b83eb7c09d3e264674bf8ae981beabadb83e10c62b3cf36e9e3ea3a07ebe7779ac3e22e59e3e8849bd3e8fdda23e94197abf5a3b87be853abd3e4d05443c55cea13ec2b984be82e5af3e3a40a03ea7f0bb3eccadb1bde73988bf9c1b6ebe252eb93edc77e13d325e9f3ed03970beefb1ae3e9cf59e3e94a6bb3e6bcaa03eda1f76bf1a6d85bef1a1bb3e080c223c9007a03e8a9482be525dae3e36e99e3eae5f973e822790be10f892bf8f0233be833ca33e112c503e59c27f3e6d2933bea41b943e81d07f3e3a75ac3ef97c943e349b82bfaa346fbe7775ac3e049e3c3d55fe903ed9ba69be0e689f3e9451913e601fa03e984e9d3ecb5988bf948b6cbebd049a3e0f9bd53d10d1853ee89d4ebef969913eefae813ea184923e6f74dc3e613992bf1d5ef2bdb8d27f3e1098493e190a703e826ad7bdf025803ee45d7b3e4e7e9d3ed220883e5c208bbf56964ebe564a9d3e5c20903d36f6823e33b747be0584903e647a813ee3f0a43e3c67903ee40d84bf1f6796becae5a43e7c49963d06228b3e1fbc5fbe1ea6983e68ec893ea15acf3c2013803c957fc13cdb357cbcdb90cf3cce0829bd492fbd3c34ba92bc9e80c63cf106bc3c6b5df43c91ab813c30a6e03c1888a53c5246f43c8d535dbd6205dc3c530ca2bc0127e83c1688da3c2dcef23cc81c2e3c6e022d3dbccf30bc35e3f23c4535a2bd5c38da3ce452a3bcd8bbe73cdc2ed93c5461db3c592fa43c05e8fb3c297c91bca84fdb3c29090fbd0d14c63c39b0a4bc8274d13c64bec43c2556523ddde19c3c3ef5113dc426283dc24c523dd0d6d1bda03f453df5df2fbd2ce44b3dc474443de402d63c672c6e3a61ec333d449869bceef7d53cbde897bd36cbc23cbdc581bc735ecc3c6624c03cdecd76bb47bcac3b9691a33c510366bdc19e7abbd27a2ebc1e2c92bb7c2b45bd98b587bb2bd290bb5c3e963c45a9fd3b00fa2c3d438d63bbaa3d963c3adc24bd32a3783c9c1915bcd75d893cbc5d7b3c52f5a83c1af84a3c82710b3d91790fbc7ae7a83c338218bd88a28f3cef2737bc4d559c3c05508e3c3866d33c6f19a33c670c023d73a51bbb62b1d33c563756bd3d50bb3c8cdf3fbc7f15c83ca09bba3c22478f3ca39fb33b0baa2f3d68addbbb83538f3c1c751bbd3885703c7a6a0abc67ac843ca718723cb056acbce32bd5bc32f9023d116e57bd5083acbcf553963c867cbdbc79a0ff3cb7a2b3bc58bebebca86a783ca906db3bd6f40a3d025f813b8583793cab2f3cbd6e654e3cde6424bbaa50653c6e454c3c17dac43b6bded03a16284f3c34b671bcb01bd73b072529bdd84eae3b97b5f2ba0d11bd3bc523ac3bf743df3ca411a03c7685bb3cc426863c6e17df3cf8c583bdf4aac93c1c18a5bc4531d53c0d47c83c9deec73cd80c4a3cf40eff3c036e8bbc3ccdc73cb3e72fbd17acb13ce69587bc08b4bd3cdf4bb03c175ebd3cd36b493c7c316b3d30ad47bcb786bd3c3f509dbdf735a73cb05d77bc3fa3b23c1b0ca63cc85ce43c75c93e3cf0aa063d232389bcbc4ce43c529f0dbd1305ce3cb8c49ebc869cd93c6eaccc3c4338ae3c12f5883c8666ff3c6addb43c7b2fae3c74fd67bd75b4963cf9f24dbca51ca33ccfcf953cbac4c83cb966b93bcb04103db8775bbc0cb1c83c4ccd56bdf87fb33cb37785bc7b74be3cb042b23cdfd73f3cba0f253bbf531d3d9cb5bcbcc90e403c934dd2bc109c123c63d911bae344293cee1e103c2c318a3c83aed33b4d92203d132343bb04718a3c566e48bdf281683c41df13bc96017f3c14bf653c7dac8f3e97c0763e2cee8dbf6d172cbe18778f3e01e5153ed9746a3e15a32bbe8f9e803e1879633e5496b23ebbcaa63e0f997fbf953778beb389b23ee506f73c824a973e89596fbe1648a53e0a41963ebf0ab63ec04c9b3ef9817abf6bdb7fbe53f6b53eaa16273d362e9c3ee6d079be157aa93e29f49a3ed4eb9c3e83ed9e3e529387bf3d0753be9d199e3e9e24d73d3e05843e214348bedeed8f3e69ef803e0ece983ec72a943e50528abf40573dbe1410983e779c1c3e0c19793e123b3bbe7cb28e3e5067743ecbd5a83e29af8f3e01b983bfa0ac68be68c3a83e1bf84c3d790e8e3efd7763be7adb9b3ea3a28c3ee9f0b93e46afa13eb4917cbfa22386be1cecb93ece69a93c6b029f3e3fb382beace9ac3e66ed9d3e40daae3e8b2ca03e5c6e80bf8af574be0f6eaf3e79c2903d73bd943ea09b70beba6ea13e3b3f933eb147a03e71c4893eadba89bf273f53be558aa03e3acc9f3d8cd3853e94344dbe1402943e1493853eb1f7a53e4927a13ed9e185bf65828ebe7b1fa73eb289bd3d6fb38b3e676860be2248983e7a998b3e7ac0a93e8f79963ed42987bfe8696bbe90e9a93e9c4a823d92e18f3e644868be26679c3e8bd68e3eea77903eeca0ba3e378590bf48673abe1194993eac2f1d3e1581713e01da3cbec9b68a3e15aa783ef257923e07d87c3ebc7e8dbf8e5d3ebef293923eb380cd3dfead6f3e96c53abe4c59853e01616d3e6c22c03ced16bc3b1a9b153dff7a20bdd42ac03c2b694cbd15c4a53cc91a4dbc1a32b33c21d8a33cbd319e3cb3918c3a7c041d3de002593c981a9e3c9854fdbd8432823c08e217bc5a3b913cbac1813cfdc58a3c94e459bb88d9143d612b2ebc45bb8a3c4ac952bd2ef5673c182717bcef037f3cd6356b3c2c2af83c3bd5053c20910d3da175abbc6a37f83c375461bd65abde3c962caebc6132ec3c9511dd3c9a7a8b3c93bcb13b6c3f2c3d4bf800bca5678b3c82b6febc872e683c97be20bccf39803c5689653cebc3cd3c39f40eba331c003dac9987bbc1a5cd3ca3d265bd6ec1b73ccd7b57bc7204c33c9f5bbc3c069fc1bbc6c4ebbc23a0793dd889f2bdbadfc1bbe438d8ba71950fbc4ef8943c1713efbb47f110bca31dc13cd28d1b3cee964f3dbfd759bb3c28c13c699ca0bda0dca43c6a6b3cbcf84fb33c701fa33ca7864c3ddb04fd3c777ef23bdae15a3d689a4c3d447f92bd9ff63f3dddbc2cbddad9463d26963f3dc42a9a3c19e7823b5f281e3d01d30dbc14ab9a3c23d31ebdf063843c9f9e49bc48938f3cdaeb823c5504b63c32e63bbca2e93a3d0c9b29bc4f9fb53cd414b1bde2869b3ca37b46bcf4c7a93c7ca69a3cb914d13cf389f23b3aac033d85e269bc2013d13c031c61bdd747ba3c36e690bc64dec53c3eefb83cac0b933c311910bcd170083dd00ec6ba9022933c069453bd95a1753cafc30fbc3d8c873c0e24763c6f0a2b3c03f36d3b9cb5413daa6320bd12242d3c627eb7bc5996153c05fc58bd2ac5243cc50e133c6d8ea43c0a10ce3a172d2f3d30c81e3cbd80a43c0a997abd28dd8a3ce1fd44bc4e12983c287b893c47c1113d8e2c9f3ce295123d15bde43cbda6113dfec5c1bd9516023d5e06bebce9700a3dde4a013d9886c43c6e20273b3c5ff53cc30e60bc4fc1c43cdf7fe8bcf370ae3c391a7ebca88eba3c1daaad3c6b86903ce290163b03360e3da759fbbbb857903ce37414bd2d0d753c11921cbce514863cdbc7733c3523923cd22736bb60f70c3dbd9dbabb621c923cad34f2bce147703ce156f3bbce1c863cb9796e3ce7da993c4782053c7415213d99f8c9bb2ff6993cbab564bd718f813c0d3f24bc513d8e3cbfd4893c30e5033da286243ca09b733dc912b6bcdce5033d3e2cafbdd645ee3ca92abdbc2c6cfb3ce2d7ec3c22b7ef3c6a973b3ce0d1d63c558b6fbcecdeef3c880274bda28ad73c0d07adbc9fc1e33ccbfcd53cc9f9ae3ede4e893eeeb685bf989567be5e7aae3e0fcb2b3df540933e825563be0dfca13e0951923e36c2ac3e6958b63d81ee89bf51ce64be8d0fad3ed4cfc23da1fb8e3ecd7a5fbe26639e3eda248d3ea1b0a63ee3288f3e21bb82bf69f961bea282a63ec633753dc6108b3e31915cbe40f6983e69b9893e1b37a33e7df58a3e70fa88bfadc060bea237a33eab35993d2582883e808c5abec00c963ebdda873e7cca9f3ea7e08b3efb3e88bfe92457be91ac9f3e76048c3d8794843e740451bed5a3923e9eec833ec662933e9a4d983e07be8dbf040249befff5933e342efa3dbcb2783e430243be8a388a3ed773783e8ff49a3e00aca53e190c8fbfaea851be838c9a3ef21a093efa2d823ef71732be9e66863e835e7b3e0128b23edb7d0bbea3638ebf347061be70deb03ee88ce93d87d7933ec9fb61bec783a23e244c943e7515a73ea5728f3e2e8387bf187f68be690ca73e140d7d3d22ab8c3ed07462be70219a3ece628b3e65eeb03eaf5a923e1df27fbf7e716fbee9b1b03e4371593d10f4953eab676fbe8305a33e6ef8933e4062a13e12cfab3ef2a385bf43fc91bec2e6a03e049bd33de41c8d3e4a1456be3908963e6cc5863e64d2bd3ef2d7a33e33b17dbfd26c88be0fd5bd3ea6e5823c22f1a23e4fc485beb13bb13ea079a23e40e7bb3e08eea03e2d2176bfd80286be2bdabb3ebaf34f3c6e95a13e998e82be5f3eaf3e0456a03ef02fbe3cb7835f3a6190183d0f6801bc7275be3cc390afbd1caca53c2f1975bc5d59b23c7928a73c087b393d96d11b3dc91e893c54fe903dee79393d4d4a9bbd5a392d3df75c16bdd48a333d63962c3d2210b93c7fdf463c94bf383dfa52fcbb3c0ab93c98a223bd39509e3c12295cbc7238ac3c5ab89c3c0a60a93cce1edb3b3cc6fe3cba1932bcd757a93cf7964abd6d40923cef4757bc78229e3cadd3903cf651de3cfee98e3c25dd503d8f2f9dbcc39cde3c76a96ebdeaaac63c995a9ebc5275d33c4531c73cb2150b3d7baf813c0c61903d2eb094bc541b0b3d006468bdd820fc3cabe0c7bc17b2043db0affa3c925b9e3cf9dd063c7c24593d9e9ae9bbb94c9e3c2fbf78bdbdad863cb47a26bcada1923c754c853c409eb63c83d7aa3b1a461b3d947342bc7ec7b63c7d5546bdee109e3cb2176cbc95eaaa3c08ca9c3c5892af3c78e1453c99010f3dc3a163bd22f9af3c00afd6bc4f1f9a3cbd0b50bcf6bba53cb989993ce353983c14be3e3cd90e2a3dff8d8ebdd685983cf087adbc208f833cb53383bdae108e3c97cf823c9607a43ca1a6933bad801b3d7c8894bb1f2aa43c4a9620bd2f8d8d3cbe9a38bc8bdf993c59f28b3c917fc63cbb0e173c7b66153d2a9bf13c2ceec63c2dfca8bd4532ae3c091f82bc2fadbb3cc07bad3c003bc83c5a9fa0bb9b0a183d73412ebcecacc63c6a48adbd0b78ac3c262250bc6f03bc3cf6e2a93ceb04d7bbf183bbbc4b88683dc410d1bdbe01d7bb6aabecbbe56619bcd335793cd9c801bc5b0d1cbc6e5c1a3ddb1ebf3c81c2863cb2ad113dac1b1a3d7d4ba3bde0e50e3dea50d5bc61b9143d43380e3d77d5cf3cdb7d3c3c41bf243d959285bc0ebbcf3ce85ed6bced6bb73c68d185bc4811c53c1d49b73cb3efd13c7733923c3594083d038a81bc98fbd13c502e40bdc842ba3c2b6194bceb3fc63cb3ebb83cc09bdc3c1ccc883c3e62043d22fb39bcc460dc3c6b2086bd90dbc03cd72655bc05e6ce3cfa2dbf3c2c2f8e3cea09d83b275b2f3d0211a8bbc4d88d3c110545bda4d16b3c3016f2bb1a25823c74a9683c341cbe3cd4740f3c67428a3c26b39f3a6c46c23c95a125bd282baf3c5f5b4fbbd274ba3cb679af3cc7829c3ce375df3bab4e313daad3d13a6a119c3c52c75fbd927b7d3c35b519bce6c88d3ca5c6793ca755983ce850843b5dd4043dd22b273c6592973c2fae55bd4b77853c3db180bb75c78e3cb3c4883cc96f9a3e96bc9c3ef0f887bf04d349be18239f3eb887be3d0219833eba374dbe2c38913e64c0843e4995aa3efc07923e30eb80bf49f16cbe5699aa3e489d823d1816903e748b67be8e729d3ee1ed8e3e07f4bb3e40b0a13e69fd7abfd56086be39d8bb3e2915803c10d9a03ee4d083be02ceae3e30e19f3e4797a73e87788f3edf1088bf87ae68bea18fa73e38c07c3dafec8c3eadd362beadfd9a3ec5838d3e38ceb23ea3b6993e14177ebfb09e7bbe3be2b23ed6bcee3cdb4c973e732075be889fa53e4d72963ef6dbb03eb8e29b3e2b527dbf121d8bbe1d24b13ebc1fc33c1b04963ec6b963be597ca33e8872953e8c37a53e42a5983e4e3988bfc6b669be1affa43e0fdfbc3d60978b3eebe361be9a42993e7ba18b3e5a289b3e2a96a33e4d3b87bffbb33ebe81ba973e08d6f63d38a5833e3e3137be80d98b3ea600773e60abab3e33f79a3e72ae86bf3b4b66be418bab3e82ab9b3dde9e8f3ec17767be7bb69c3ee3e68f3e20aaa93eb7a79a3ede4883bf95146bbe393da93e16324a3da3a18e3e003d65beb93c9c3ec4ec8c3ebc33973eee7aa23e73678abfeda138be796d973e070e1d3e66f77a3e644836be89ba8f3e75d6813ee98e9a3e95fa943ef28b87bfe66d4bbe38fc9e3e7f54fe3d5e8a823ea9e045be2fb3913e2676863e2e3c8c3e302aac3eb30b8fbf4df01ebece99833eb8b22c3e41e6593ed6b021be33d77c3eebe4653e64b6dd3c23c1213c08edd43cbf7d66bcddcbdd3c993b3ebdb445c43c3dc492bcf061d13cc981c33cb82ee93c1b7e5b3c7db3f83c503552bac311e93cce7b63bd788fd23ce40aa9bc9c33df3ceabdd03cdef0d43ca2f657b9c07b263d3f6a80bcaed9d43c201f63bd9787b83c338d74bc74d9c63c46b5b63c494aa03c443c7b3a3332fc3c2eecffbb0413a03c8e271ebd661b8b3c8bb044bc8610963c45d7893ce14ff33c1bbfa53a921bfd3c163974bcbafbf23c328f73bde18ada3c3e854abc3701e73cbf0cd93cbb66bd3c159717bb7d640e3d71bd79bbbb68bd3cfb1794bd1dd9a03c7eba3ebc6426af3cc50c9f3ced8b7d3ce15e453a9441f33cf626dcbb35747d3c65b892bda7a6533cbd8407bc7cdf693c950a523cbcc1be3c355c983a62b8443dcfc051bc8ba8be3cc388adbded13a53c791b67bcbf2fb23c7e82a33cc7e3083d3100063c956a093db14284bc4cc8083d18a252bd297cf43cd8a1bdbcbfaf013dc0bdf23c21e9cb3c1da5ea3b4b5b1b3d787a97ba6db7cb3c225c81bd42dbb33c232762bc26dcc23c75f3b13ccb47e5bba1c7c9bc7369843d6fb8d2bd3f8de5bbc2ac69bc6a9627bca1b5823cc3cc0cbc5db226bc39bb36b913854cbc64e94ebbf9b6963c26f937b9761305bce65e56bb1870903cea0de0baf6b063bb33920f3d7c73583c82d3733d0c4cc3bc548d0f3d195d90bdb191023d3f78d4bc301c093db6c5013d7f318a3c2fb774ba2c981e3d6f2a9e3cb4228a3c7c67b6bd21ff613cba810ebcda667b3c59435f3c3015483cab130c39dfff533d03441dbd5a5e473c6233b4bc348b1e3c24773abd800c353c9c1d1f3cf7a3673cc1903ab9f985323d79ba9d3a514a683c45a14abd3c8a393cfa88d3bb6bd5523c7193363c801d9f3c86f2fa3be68d1b3d29ec05bbf9759f3c595b65bd62ca853ccbeb19bcfd31933cf691843ce622953c589be53b8752243d0f6c32bc3536953c2d8242bce09f7c3cac121abc19248b3c75057b3ca00fa83ce060d83a64c5223da97a8abbc459a83c43becfbca5838d3c7c3f07bc22ad9a3c16d88b3c9927b03c4339ab3bea03f83c5f2122bcd312b03c608389bd06f6963cb44f3dbc3bd5a33c263b983cb7e5ce3cc5bb25bcba13393d3ced51bc2a61ce3c3d2781bd3121b83c2d8483bca0f9c33c7fbab63ccec6123d9c6f913cd42fa43c0d5d4e3d43b0123dc4ab98bd2095073d8e66e8bc32490d3dee28073d820c9c3ee5e7883e21d58bbf115e41be6998a63e8851ed3d4fa3823eb1323bbe3b03973eb62b833e9a929a3e9a719b3ef6b091bfd34d19bed30c8d3e09a5383ea0ec683e812e21befbcf8e3ec5e07c3ed040a33e91ef8b3e5e9b84bf75778bbee639a33ee8b2ac3d1d35893e7d095bbe6b60963ececc873e97d3a53ead48913e532387bfd3e364beed95a53e7944933dc5ce8b3e9c415fbe34e1983ed2ad8a3e1927b03ee5cc983e371882bfc65b78be4824b03e1f154b3df0ce953e79de72bea0a8a33e2d9f943e3e56c03ef31ca43e1b6972bf83bf88be0e48c03edc8e833ba500a43ef16385be98c7b23e34f9a23e544ca63e32dc7e3eb13a85bfafc55fbe54e6a53e1712b23d84678b3e234a5dbe45a5983e97808a3e42a3a83ebc55a23e10d084bf46ce64be9d7aa83e1b14a43d60648f3e808565be19669c3e62108e3e56d6a13eb755893e75b487bf84315dbe1bc7a13e3cde703d574f873ea04b56be5aeb943e5df0853e223d933edf64b73e54898bbf43cc20beb317943e6760343e0e13753eb3c526beccd9853efe237c3e591bad3e081d643ecd7b86bf56b86dbeab68ad3e41b2583df740913eaace67be689e9f3e288a8f3e93cea73e83a9923e6b6483bf176e69be48aca73e617c983d43498e3e029265be25fb9a3e87ae8c3e32d8a23ebf44843ef16887bfb02450be4c5fa33e7fecc33d8c718b3e782054be3aed963efd5c893ed197083dcaba823c6113003d1cae0f3cacb5083dd46f9fbda316f83cdbe4ccbc0474023d09acf63cf8a6a83c5da90d3ac2861d3dfc1c47bad185a83cebcf9abd2455903c090e47bc1b9f9c3c726e8f3cad5dbe3ba90d22bb26f5cbbac2f77ebbf18ebe3b8fbe61bccbda5d3b5457a93aa94f9b3b58725c3b8563c53cea9e0f3cdd7cec3c546664bc054cc53caf5188bdbf3caf3cd0847abc8750bb3c382cae3c96589c3c4c5e353bd5f21e3d3ec868bc097d9c3cdd365fbd631c863cb8fa28bc6aca923c92dc863c38d4023d838a433c574ec93c22538e3c5bcc023de06d7dbd2e20e93c2f91b5bc52f4f73c71cce73c4f17753c9c991dbbbba1763d53f09fbd9d567a3c9d630cbc6c514a3cb4e034bdee6c563c1e974c3ccdfd873cb2e7b4bb7f3f483da5fca1bbf09c873c78507bbdd2295f3c5f6b0ebc2687783c5f4c633c704d5c3c304cb33a9edd243d3909ac3add755c3cc30a80bdfd9e2b3c4af28bbb8f92453c8e9e293c339ba23c4f2696bbaa0e013d8d6e8fbbd490a23ce0cb93bd10dd893cd1033fbcebb0953cc9c6933cd4cfbe3c3d45ef3bd61b503d5fa76dbcd091be3cd92976bd2c77a73c6d477ebc56edb33c1d5aa63c3ae62a3d0a49a63c9efba73c13784c3d7a9f2a3d4e4ca7bdcad11e3db19005bd80fb243dde7b213db4ecc33c6c3e6f3bb1b8113d2bb68ebcd3b2c33cdf003fbd2ca4ac3ccd8e73bc81f4b83c7849ab3cd2a2c03cfc709339a2e0043d9f8a02bc674cc03c0e9944bd126bab3cb92c62bc6320b93c78eaaa3cca0b2cbc73e0e2bccd76913d2c9cedbd9d162cbc8d589dbbc6f962bc9c75a43c0cda46bc6fdb65bc15af763c68bd443b2e02223d90eb9c3cfa48763c99de89bd36dc4c3c1a0adfbb84da673ce7a74f3ceefdd93cfa98193cab141c3d4446613a71c4d93c1d159ebd08c2c03c465f82bc8be7ce3cd619bf3c282a963c5267223b7598253d5f769dbba94d963caddeeebc2d6b7d3c791924bc711f8b3c3272853ca4c5283b893cacbb26eeadbcb0603b3c27ad293ba5c885bcef5a083ae687fa3b27d5c93af2ad903a4cc7bc3c922b3d3c99ed353d0af795bb3addbc3c1dee89bd5326a13c16fa39bc0d1faf3cb1729f3cd0c2963cdb520439c743163daface8bb63d8963c72d358bdc0457b3cf4bdd0bb57548a3c9f707b3ce93eb03c516d2f3be5ea483d377824bcfc9ab03c24e597bd7440973cd88540bc90d1a43c7fce953c730ab03e7ed7a03e649b7fbfea7277bef776b23ee665563de5b2973ea9a975be64cea43e75dc963e1318963e01e98d3e0a908abf14b24abe0ada973e6d38cf3dfc3f7c3eab5145be6db48d3e8a187d3ea856aa3e3eeb943e098885bf636f6dbe9845aa3ec25c833d759c903e90d668be010d9e3ec46c8f3edf6ba13e098b973ee31087bf41665cbed9aba03e21aab53d048f863ed47554bed218943e8e2e863e7252a13e0b488c3e1ef985bf1e7a52bed3a0a13e0f0a923dc2ef863e4b774dbe0c93943e5c35863eb193a83e6f21973e641e85bff43869be96d4a83ecd648c3da6e68e3e92f464bec6209c3e80b58d3ea3f1ad3eb81e7e3ec19082bfa91d59be150bb53e0be5be3dc248943ee22866be46f7a23ee39b933e183ca03e868b873e1f0e89bfa7ef57becf28a03e59f58a3db348853eb2f651bed0f7923e290c843e66aaaa3ec9f78b3efe8886bf3ea95dbeadb1aa3e10eca03df3848e3eb8f563be7c409c3e33188e3e5086b83e91d987bedd0c8dbfdd9944be8ad8b13ee3c0353e5f5a953e190947be5457aa3ec113923e6b8d983ed309843e1c9a8cbfb46768be3855983e9869ec3db8227f3e0c164cbed8718c3e141e7e3e6b12be3ed1c9a23e500277bfa39387befff0bd3e2217f63bee7aa23e6d3784be1cdcb03ef3e2a03efebe9f3ec75c873e660a88bf546053be14b69f3e2d29853dfa5e843e830a51beb669923ee522833ed5f47d3c76dc8f3bab3a1f3ded72fdbb3be77d3cf4e503bd9edc4f3c1cf6debbaa59683cf778523c00b3ea3cd168373c2a152d3dc6670e3c4babea3c54a297bd60ffcc3c7c7996bc8041dc3ccf46cd3cd515a23c5d27983b1db9543dd402bcbb583ea23c993a54bd696e873cb54f1bbb634e953cb9c1853c112cf43ca1daba3ce697123d55674dbda931f53c184700bd0ea9dc3c3ca4b5bc630be93c6b22db3c428f083d9b33ca3c588b9b3dc59ebebc94b4083d08b764bdf676f63c7756bdbc0530023dc1fbf43c90ffbb3cc097523b7b69ea3ca90736bc6981bc3c5ab0fdbcd2f0a33c8a5b76bc1caab03c2b6ca43cf022953c81d5a5bac9373e3d626923bb3645953cf4dd58bdfafe7b3c2e1d30bc0dd8893ced6f7d3c6a1bcd3c1ff5543ccbeec83c1834f73caf07cd3cf0065dbdeccab73c856485bc0957c33c806ab63c38359f3c60ba3a3b3042253d8eaa443cc0ca9f3c50463abd6ccf813c26430eba7da2913c09c38a3c6a92b03c1852ce3bbcbae73c768b84bcfcbbb03c7bab71bd17b19b3c5c4068bc4b6da63ce9859a3cdbb3eb3cab0b733b4cbb053d146391bcfe6ceb3ca8806cbd100dd33cfeb3b0bcbf29e03c48f9d33ce397a43cc819043c0102583db3ab2fbc04c4a43c963166bd0319933ce3085abc9d119f3c5e04953cfcd6183d0831663c9cca383d6a098bbc8830193dccdf8fbded6e083dca0eaabca265113d73d9073d33b1373dcc15fb3cc9e0363c1692963df9a7373d8b2993bde8a9293d8abf11bdf1c7303d11d4283df46f653c1aab2bbcdbc1573d40e0e4babece673ccdbc5cbdcca1353c5092b2bb5d8a503c55ce323c0049d43c3a96863c4b83cd3c55c24d3d70b5d43cf82395bd9359bb3c3cd575bc2748c83c06d6ba3cdd90a63cbc29fd3b9fca203d928e5ebcd6d0a63cfc7b61bd96478e3cde524fbca0e99a3c67b18c3cf720153df7f3c63c58cde33c91acc0bc6815153de88348bd93ec073de7c2d7bcede90e3dc719073dfc32dcba0cccd8ba7f520f3b705e9ebdad49f9ba6f3624bc9c576abb2e7a213c846338bbb17f67bb9c86ebbb2e80c1bc7528853dcc27f6bd9108ebbb0db074bcc59127bc06ea863cdc230ebc1aa31fbcaef2f03cbdb8433cf0f3fb3cbc398dbce23df13c294209bd57edd63cd970a3bc5f74e43c5f97d53cc6e0f43b263ca03b333a563df14285bde690f53bfa138abb2adfb13bcf476fbd80ccd43b9cb0b03bdc17a23ef5c88f3efff784bf69036abe752da23e15b0863d8f6e883e32d259be758b953e431d873edc94b13efa2e9a3e72c780bfeb4281be3f9cb13ecf97363d9abb973e3ae279be51f9a43ea3b4983e4b5dab3e1ef4a03e18c185bf09666fbe89a1ac3e4846a63de1ef933e533e6dbe0a45a23e67bb933e7dcfb33efb11623e2b7384bfeebd54be3dd7b73ed5da9a3d0995973e0d055abe6469a93ef0e9993ec156a03eb186a33e1d4586bfd30f4ebe68999f3e821cd73d6ec3883e844d56be4742953e4ecc873e0d9d9a3e034f9c3e8a148cbfae2543bef7979e3e80d30a3e191f7d3e130448bed044903e62cd843e9721ab3ed76b913e2a5b83bf4ea18ebe5221ab3e6ba97a3de55c903e5cf566be3e6b9d3e78ae8e3e118c933e9fe9af3eb02f89bfd9d23cbe0cd9923ee37a073ec27f7c3e629338be808b893ef4c9823eedd4883e646f8d3e256995bfa1c925be98e1883e7eb2193e4e7f693ef5a325be5d467e3e009e6e3e32b4963ea7d58b3e0b758cbfcaa23bbe4b09953e186ae43d63f7783ec1613abe51bf873eee75753e2df1b73e59b59c3e00ed77bf176881be2fcfb73e8aa5023d55fe9b3ec86c7cbe9c84aa3e77be9a3e11f1a53ef114703eace587bf6d174bbe8258ac3e6a80f73db853893ee53154be25599c3e9281913e1a16983ecfd8ae3ecd138cbf996a47beddbb983efc4ff73d81387f3e681e49be65048c3e69b67c3ea8cadf3c28886e3c1307163d12f153bc72f3df3c4a7411bd18b9c53c287593bca1fdd23c4829c73c4a6e863cfaea7fbbdd75153da83e8bbbe749863cebb61dbd4300593c517fddbb0f98733c7af6583c0643e93c9dc787bb7be7553d801e88bc6611e93c8091a3bd098cd13c5bbda5bc684cdf3cc0dbd23c5d2c2c3d69548d3c0781c53c500ba33cc20a2c3d0393bdbda3421c3d70a1e1bc4bb6243dc4751b3d064a9b3c78d86a3a64dc1a3d7659193dfc439b3c5ae2a4bd5235813c4e7c0bbc5bbd8e3cdb707f3c25bcf23c49910b3c7d24253dc8acb4bcf699f23c2d5da7bd31a7d83c2912a2bc5437e73c7852db3c77f0c13cd0691d3b8678fb3c6094a33b33ecc13c4ec984bd3eeba53c14ed72bc013cb53c53b6a53cb0e92abb330ba8bcfb3a653ddc610ebe12dc29bb930b8839d9c7b2bba3eb373ca3787bbb902cb8bb43efd93c352ee83be083123dee1f8fbc66d7d93c57c392bc1001c23c36c58ebc0c95cf3c3e72c03c63b0ae3c889ecd3b4c7f143ddd0d40bccd99ae3cc33668bd25c6943c407656bc44e0a13c7e39933c0b68f83ce7dc593c7010f23cab366dbc4059f83c74bd8dbda48bde3ce4708dbcac03ec3ca118dd3cbb37c43c7473503cda1ded3c38583cbcd168c43cb401d7bc0550ac3cd0107bbc347db83c6ed9b33cca52a7bafadb3fbc4fa884bc8ab8953ccd17a8ba5a4d8fbcdb5694bb8895723c216b3dbb035c9abb1817ae3cd69cef3a4086193d7eae30ba6d33ae3c4dd42cbd0c80923c48ecbdbbf87ca03c7bb1903c1c9e813c68c040bc13403c3d74af21bda092813c584f23bd697d553ced45bbbbe61f6d3c0ed4523ce9d0c43c4944153c78c6103d209eedbb62e7c43c6ce52ebda354a83c510045bc745cb73c0685a73cd8a3823c1a4cb7bb733f333d30f0f13a44ee833c8cb91dbdf4885a3c54a6c0bba98f6f3c5a3a5a3cf714f13c10568f3c19b8393c8e53b83c249cf03ceeb8b8bded0cdc3c046facbceca5e63c59cada3c2dee9f3cb6e7ab3ad28c2d3dbd9f29bce43aa03c6d824dbdb36d883cd7f238bcf200973c8bec883c4dbee53c205f273b946af43cc5fe443b6ca3e53caa017dbdacbfc93ce4f38fbc0a3bd93c34d5c83ce4e46d3cc4784aba4a8e323d779a253b86e76d3c60f15bbd5dd0393c213e9dbbd1ba543c35dc373c6aaa293db865963c2eee063dc46f17bdfcce293df449c7bd8ab91e3d330905bd0985243dc79b1e3d6d82b33effd4973eafb089bf0c185bbed15cb43eab30603d6623993eb08774beee85a73e1581993e172e9d3eda51f53d0d938fbfcdda97bedad49b3eaedbec3d684d803e12de45bee7918e3ef8eb7d3ec828973ebd9f973ea62b8bbfa44749be47d3953e8b70073eefaf863ea4233dbeb66f953e8177843e6f06a83e0861a63ef8d587bff78551be175da93efb5dae3d14258d3efc625fbe2db69c3e688a8b3e2230a33e1a7b8f3e864289bfd23960be6f91a33e8f737c3d14c5893ef3245dbe3cca973e70d1883e5b4d9c3e2066953e4f0c8bbf4f3557be77639e3ea49cac3d9c05833e0d9351bea7b0913e31e1813e3edba43ebfd38d3e262284bf163b6bbe03daa43e878a5c3d3c8e8a3e61395abe780e983e6268893ec55bb33e540d9c3e3b8281bfcaba81beb853b33e40af323d37cc993eb6847dbe41b7a63e78c7983e2414993ef5f892bec46d94bf75fa1bbe10e59a3e7c9a7b3ea53a7f3e354107beed0fa63e1253933e638ba23ed9a78d3e7a8789bf98845fbe9a6ca23e0d587f3dd78e873e0eef53bead09953e26ee853eade2bb3ec607a13e6a437cbff23186bef9e9bb3e1204ed3cd250a03e4b9b82be7d7fae3ef81b9f3e3137b43e0c4d9c3ee8ce81bf636081be0f22b43ebb53263d0f4c9a3eabc37cbec4dda73e52e0993ee3d4b13e9ba9873eb3bd7cbf99a772be6eb7b13e20e7443d66d3953e3bdd6abe492ea43eb922943eb8170d3d72c6a73c9126893dbdc6bfbcdf0d0d3dee9b4fbda53c003d6c50d9bc9dc2063d54ecfe3c0aa2ce3c99dc5f3cd2dfa93c894494bcf151cf3c0b713bbd1753c03c8bc49ebcde53c83cf8bcbf3c827ecd3c8cb95e3caee7f53c00795c3d3d8acd3cb4ef8bbd5002b33c65d719bc3063c43c849cb83c55bdf83c4199863c7347633de43eb2bcfda6f83c79d4acbd9c5fe03c6c1ca1bc0008ed3c818ce03c2a63e23c0cc7a93c2588243d4809e43bf840e23c72d2cfbde883cc3c10eea3bcb72bd83c312ccb3c0449dc3c0f33f93b13cd243d419c45bc51f2db3c1c3d6fbd25b5c13cbc5e80bcf463cf3ce12cc03cd2199f3cb9a19d3b4482163d6c2bd0bbbc039f3cdf3306bd045b873c320a40bc024a933c5054863c9a5f9e3cca664d3bd7d3833d867369bd68de9e3c444a15bd4664863c67ad64bdb24a9b3cd9188d3ccae4a83cf4dbd13b06e4283d8a06fa3890cba83c949384bd2a1e8f3c05734cbc91689f3c96cb903cd1b4a93c94e4b33b7fd80f3d107f1bbcbb3baa3c68f10cbda64b923c592b56bca5849e3ca5de903c0334c13cce18b93b9f3f673dfa8631bca040c13c7daf86bd1ad4a63cf01572bcc302b53c6d42a53c2f36df3c9e22083cb016543d6d4c5cbcde08df3c42599dbdbc80c53c625f6ebc0591d23c4dd9c33cae59aa3cb2fc4e3b762c253d5761d03b7052aa3c3e618ebd4475913c859144bcc44a9e3c8df88f3c09aa863c47492b390b0fad3c17ce0b3d4656863cc1715ebde2766b3ce3cb05bc2905773c2235693c4e8ac63c0b59873c52cac93c5624b0bc8ab7c63cd514e2bc159fb23c646693bc63e9bc3c9409b33cb999833c0c33d93a95c2383df7b702bd367f833c061ddcbc0cca533c9ae4a5bb22fa6c3c695e513ccf92c83c9331823c65f1633dd4252fbc4f91c83cc1dd66bddd28ac3ce66a6abc93b2ba3ce865aa3c7050e33c31e34c3ce11c2c3d588d80bc4780e33c35f740bdd66bca3ca64099bcda44d73cae0ac93c0e3eb13cad4de33bebcc033d0a95b7bbb52fb13cbac576bd6831963ce05f09bca6f7a33c01ba943cc31f8a3cfc90e23bf2b0373dab405d39740b8a3cf67968bdb619603c647ee5bba66b7b3cc13d5d3ccd4ebabbd308b5bce3fe7d3d5385debd4859babb20e1cbbbbeb613bcbe229b3c4e8ae8bb26b811bc5d2fa13c032a2e3c0932063db9098ebcd252a13c6dbe5bbdd1e5903c48455bbc9e599b3cdbec8f3c88a39b3e65cf8a3eab9388bfa45656beb5739c3e26f6d43db006833e655e54be46568f3e0a13823e66d9a23e4a06943e1f0688bf09014bbecca1a23e89d19d3d34e3883e303b46befc1d963e7a31893e5835ad3eda26943e4e2884bfd92171be1235ad3e30d8133d1d47923e18446bbe2469a03ef82b913e26fdab3e23af963ecb9880bfaa8178bedaffab3eeb355f3d2a76913e28fc69be2c9b9e3e75a98f3eb27cb13ee4d1983e6f3581bf45917abe437eb13e3952293dcdbd963e6e1d74bed99ca43ee083953e3643a53e6b4e9f3e76e187bfc68e38be1fb9a23eda5abe3d5585893e977658be86e0963e2ccc8b3e3f47a33e92508d3eba0d88bf421a3abed58ea43e1bc1cd3d1015863e9e263fbeadef973e0f9d863e0875a53e23963a3e15e289bf9e524dbe4068a73e9f73e33d41028a3eaa9b4dbe8f6e973e614c8b3e7aa18a3ec72da33e84148fbfdc6e26beefca8e3eff033a3ef00d6f3eef7d28be002f803ec78c753e9a969b3e7619933e8e1b88bffca361be72929c3e64c3bc3d9dae823e435a4fbe25168f3ef835833e196bb13e6b369b3e198e84bf3d9180bed558b13e1453423d598e963e699974be397aa43e57d2953e5776a63e1be09f3e20ef80bfcda98cbee320a73e78e49a3d3e348f3e8e2964be6b1d9f3e69fd903e5109b93e36f5a03e069f7dbf872683be0203b93ed9d3a33c853a9e3ec0d380be58abab3ecfa79d3eb1eccb3c7bf5c53badcf1b3dfdf334bc3ee4cb3ca96835bd490bb23cf3ed79bc995bbf3c5279b03c5e1cb33cf6fb1e3a396f6b3dea2909bd1332b33c0de8cfbc1db79a3c5c0913bd1a3ca73cd736993ceca63c3c0c594abb63f8113d3d3b67bb14cf3c3c544b1bbd0b6e173c05b17cbb1aa62a3c3daa163cfc91093d7771953c37be013d3301e53b86ae093daa78c6bdd65cfd3c157ed1bccb9d043d5850fc3cb72106bc838eedbcfe987e3dbf3905beab7206bc638e69bbdf963abc6a8b963c04811ebca9ba3cbceae4f33cc580443c506d253d2ff2c1bb67f8f33c21bcb6bdbe22de3c43d8bcbc003ceb3c55aee43c05598b3caf968ebae5445a3d54c920bc2a2c8b3c0db078bd4896663c06d315bcca697f3c6903653c81ba553cbed6e83b2a243c3c5c26fc3b6c514a3c6340f2bc85cd233c9ecfd83954ca433cc67e2a3ce5bbac3c679e803b0900fa3c1a5f3ebc6eb3ac3c6df84ebd5ff5963c71cd6ebc23f9a13c41a5953c27929e3cc454133c9ea5093d77c040bc56ad9e3ce8acebbc2f72883c290a48bc484c943c845a873c91fdc03c46d3513c5b383a3d84fb79bc240ec13cbcf554bd7d10a73ce28d55bca73ab43c7686a53cebbfdb3cfe20e53bd2ff573de6ac73bca6bddb3c21d78ebd3d06bf3c4bba7abcea96cd3c0b54bd3cd449013cb35c20bc98b8023c3963b13bd39f013c04c9dcbcb219a83b37f0823bd29dd53b8b80a23be52df53cb222773b5f5b1d3d6ed672bce134f53c668f85bd5393da3cc44aa9bc9f62e83c1135da3c687a433d84fdf93c422c9f3cf78cac3d6d7f433d6009bdbdf7b0363d29111fbd456e3d3d58a0363d9255dc3ca950523bb778dc3c95428fbc794adc3c68cd46bd5314c13c1e81c0bbd319cf3c808ac03c1c6a8b3c39817e3a474e2e3dfa0f173caa568b3c2f2aaabd9d6c693c61ef07bcbf43803c9fad663c3faabf3c4a3d163c74e3f73c3d8c3dbb84a9bf3c83c79dbdd605a73c4a5e7fbc0c06b43c0cf9a53c2dc4da3cab84673c7056263d788f7dbc85a6da3c275122bd36cdc03cdfab94bc7cdbcd3c7141bf3c6edcf83b0712a6ba17098c3c483a0c3cd2f7fc3b0a50c6bc64deb73b5549193c21b3d83b5f52b43b78dec53c1403173c8fe6053d619053bc4aa0c53cd2bb41bdc518ac3c25ac7bbca3abb93ced81ab3cfda5e63ca2a9483cace2063dc2aa83bc2094e63cbaa92dbd3360cf3c1c7fa4bc9f76db3c3a8dce3c760a00404305a83fd2f79ebe24efc6bf787c004029cbdbbfae13e33f0c8ab3bfcb70f33f5832e13f1ff80140400cac3f9ce15dbea00ec9bf13c10240de3eefbf6f9de93fdb33c1bfe38ff83fce98e83fae8c064079bfac3fa3fc31be2af4c1bfd2ef06400bf0f3bfb7f4eb3f2aeebcbfab92fd3f0814ea3fe37673bfee12e2bed21e0f33fbc90f3f452076bfd3531a3f75943cbf80b40b3f71ef63bf163540bfcb0c8dbf5bde10bfd63996335731d83e56598abfa866263fd2ed4abffbbf0c3f39c278bfea2b5ebf503473bfc52da5be4f8d0837740d063f679f63bfaf30103fb6743fbfe75bfd3eba575dbf9d933abf5ffe77bf4abd08bf68351734e60ed93eb95b6cbfb45b073f8c2940bfbb28f03e49a74fbf0b6b36bf38ed89bfc856a8be7bf03234db750b3f2b707dbfc4831c3f3c3949bff020043faad57cbf11ff5bbf2a1651bf483afcbe862ec633063ce63ec33f55bf22a7083fb47f28bfae94f53e10fe4dbf690c33bfb3aa65bfb58025bf72474e34b17be83e6f5f81bf514a183f88b341bf01df013f36055cbf275a3ebfdf8d8e40dc717140d84d39c1f13b4bc06d6a8e40910d63c0accc7040928e45c01ab483401b416e40865ed3bf71c0a8bf29b7a53f8bcdfd3f034cd8bf4fb7e23f674cc9bf8eb0ff3fa92cd5bfa593cebff54f50bf +\ No newline at end of file +diff --git a/gcc/opts-common.c b/gcc/opts-common.c +index a55499ab2..594f0cd37 100644 +--- a/gcc/opts-common.c ++++ b/gcc/opts-common.c +@@ -28,13 +28,6 @@ along with GCC; see the file COPYING3. If not see + #include "params.h" + #include "ai4c-infer.h" + +-#define PARAM_VALUE_SIMULTANEOUS_PREFETCHES 3 +-#define PARAM_VALUE_L1_CACHE_SIZE 64 +-#define PARAM_VALUE_L1_CACHE_LINE_SIZE 32 +-#define PARAM_VALUE_L2_CACHE_SIZE 512 +-#define PARAM_VALUE_PREFETCH_LATENCY 200 +-#define PARAM_VALUE_PREFETCH_MIN_INSN_TO_MEM_RATIO 3 +- + static void prune_options (struct cl_decoded_option **, unsigned int *); + + /* An option that is undocumented, that takes a joined argument, and +@@ -842,106 +835,6 @@ opts_concat (const char *first, ...) + return newstr; + } + +-static int64_t +-ai_infer_optimization (int argc, const char **argv, +- const char *mcpu_option, +- int argc_hw, int64_t *argv_hw) +-{ +- /* Load dependent AI-framework libraries. */ +- int model_pred = graph_infer (argc, argv, mcpu_option, argc_hw, argv_hw); +- +- if (model_pred == 1) +- putenv ("AI_INFER_LEVEL=1"); +- return model_pred; +-} +- +-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; +- 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; +- } +- if (!flag_hip09 || !flag_Om) +- { +- return ret; +- } +- +- const char *ai_infer_level = getenv ("AI_INFER_LEVEL"); +- if (ai_infer_level) +- { +- return ret; +- } +- const int argc_hw = 6; +- int64_t argv_hw[argc_hw] = { +- PARAM_VALUE_SIMULTANEOUS_PREFETCHES, +- PARAM_VALUE_L1_CACHE_SIZE, +- PARAM_VALUE_L1_CACHE_LINE_SIZE, +- PARAM_VALUE_L2_CACHE_SIZE, +- PARAM_VALUE_PREFETCH_LATENCY, +- PARAM_VALUE_PREFETCH_MIN_INSN_TO_MEM_RATIO}; +- int64_t output_pred = ai_infer_optimization ( +- argc, argv, "hip09", 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 +@@ -994,8 +887,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; +diff --git a/gcc/opts-global.c b/gcc/opts-global.c +index 8bb45a26f..9ca3680a6 100644 +--- a/gcc/opts-global.c ++++ b/gcc/opts-global.c +@@ -321,6 +321,31 @@ decode_options (struct gcc_options *opts, struct gcc_options *opts_set, + finish_options (opts, opts_set, loc); + } + ++/* 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 484fc1c39..58da78b53 100644 +--- a/gcc/opts.h ++++ b/gcc/opts.h +@@ -351,6 +351,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, void *opts); + extern bool get_option_state (struct gcc_options *, int, + struct cl_option_state *); +diff --git a/gcc/toplev.c b/gcc/toplev.c +index c6bab20bb..eba40c1db 100644 +--- a/gcc/toplev.c ++++ b/gcc/toplev.c +@@ -84,6 +84,7 @@ along with GCC; see the file COPYING3. If not see + #include "tree-pass.h" + #include "dumpfile.h" + #include "ipa-fnsummary.h" ++#include "ai4c-infer.h" + + #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) + #include "dbxout.h" +@@ -2276,6 +2277,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/SPECS/gcc.spec b/SPECS/gcc.spec index aef0c04be54ffb6146b750b2dae538e61dbdd300..a51504dd27c017b88906b9219a945ebdaa258adf 100644 --- a/SPECS/gcc.spec +++ b/SPECS/gcc.spec @@ -105,7 +105,7 @@ Summary: Various compilers (C, C++, Objective-C, ...) Name: gcc Version: %{gcc_version} -Release: %{gcc_release}%{?dist}.ap.2 +Release: %{gcc_release}%{?dist}.ap.3 # libgcc, libgfortran, libgomp, libstdc++ and crtstuff have # GCC Runtime Exception. License: GPLv3+ and GPLv3+ with exceptions and GPLv2+ with exceptions and LGPLv2+ and BSD @@ -329,6 +329,9 @@ Patch5004: gcc8-Instruction-generation-optimization.patch Patch5005: gcc8-Vect-Enable-skipping-vectorization-on-reduction-chai.patch Patch5006: gcc8-static-field-compression-optimization.patch Patch5007: gcc8-aarch64-tune-rebuild.patch +Patch5008: gcc8-struct-dfc-optimization.patch +Patch5009: gcc8-array-dse-optimization.patch +Patch5010: gcc8-support-for-hip12.patch # On ARM EABI systems, we do want -gnueabi to be part of the # target triple. @@ -964,6 +967,9 @@ so that there cannot be any synchronization problems. %patch5005 -p1 -b .Vect-Enable-skipping~ %patch5006 -p1 -b .static-field-compression~ %patch5007 -p1 -b .aarch64-tune-rebuild~ +%patch5008 -p1 -b .struct-dfc-optimization~ +%patch5009 -p1 -b .array-dse-optimization~ +%patch5010 -p1 -b .support-for-hip12~ cd nvptx-tools-%{nvptx_tools_gitrev} %patch2000 -p1 -b .nvptx-tools-no-ptxas~ @@ -3377,6 +3383,10 @@ fi %{ANNOBIN_GCC_PLUGIN_DIR}/gcc-annobin.so.0.0.0 %changelog +* Thu Jul 3 2025 huzife <634763349@qq.com> - 8.5.0-26.ap.3 +- Add array-dse and dfc optimization +- Add support for hip12 pipeline + * Fri Apr 18 2025 TencentOS Team - 8.5.0-26.ap.2 - Add static field compression optimization - rebuild aarch64-tune.md and delete unused variable