diff --git a/0376-Struct-dynamic-field-compression-optimization.patch b/0376-Struct-dynamic-field-compression-optimization.patch new file mode 100644 index 0000000000000000000000000000000000000000..039903fdeb9c677cee0e0ad5f3b34ecd5e046ab4 --- /dev/null +++ b/0376-Struct-dynamic-field-compression-optimization.patch @@ -0,0 +1,6805 @@ +diff --git a/gcc/ai-optimizer.cc b/gcc/ai-optimizer.cc +index 70ff24077..8908d1be3 100644 +--- a/gcc/ai-optimizer.cc ++++ b/gcc/ai-optimizer.cc +@@ -30,14 +30,14 @@ along with GCC; see the file COPYING3. If not see + #define M_OPTION_SIZE 11 + #define M_MODE_SIZE 6 + #define NATIVE_TUNE_SIZE 128 +-#define CATS_STRINGS_ROW 34 ++#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 356 ++#define COEFFICIENT_ROW 366 + #define COEFFICIENT_COL 10 + #define COEFFICIENT1_ROW 10 + #define COEFFICIENT1_COL 1 +@@ -121,6 +121,7 @@ preprocess (int argc1, const char **argv1, const char *mops, + 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) +@@ -344,9 +345,9 @@ graph_infer (int argc1, const char **argv1, const char *mops, + the ONNX model. concat_result is a 1 × 18 matrix, and encoder_out is a + 1 × 12 matrix. */ + +- const int concat_out_size = 350; ++ const int concat_out_size = 360; + float concat_result[concat_out_size]; +- const int encoder_out_size = 34; ++ const int encoder_out_size = 35; + const int encoder_last_size = 10; + int concat_size = 0; + const int size = encoder_out_size; +@@ -378,7 +379,7 @@ graph_infer (int argc1, const char **argv1, const char *mops, + /* This requires performing matrix multiplication between a 1 × 356 matrix + and an 356 × 10 matrix */ + +- const int m = 1, k = 356, n = 10; ++ const int m = 1, k = 366, n = 10; + float mul_result[n]; + matmul (transformed_column, coefficient[0], m, k, n, mul_result); + +@@ -412,7 +413,7 @@ graph_infer (int argc1, const char **argv1, const char *mops, + return argmax_output; + } + +-int ++void + get_optimize_decision_from_optimizer (int argc, const char **argv, + const char *mops, int argc2, + int64_t *argv2) +@@ -422,5 +423,4 @@ get_optimize_decision_from_optimizer (int argc, const char **argv, + { + putenv ("AI_INFER_LEVEL=1"); + } +- return model_pred; + } +diff --git a/gcc/ai4c-infer.cc b/gcc/ai4c-infer.cc +index 4cd4bfb00..4cf040be2 100644 +--- a/gcc/ai4c-infer.cc ++++ b/gcc/ai4c-infer.cc +@@ -42,7 +42,7 @@ along with GCC; see the file COPYING3. If not see + + /* Model info. */ + static int64_t argv_hw1[M_MODE_SIZE]; +-static char native_tune[NATIVE_TUNE_SIZE]; ++char native_tune[NATIVE_TUNE_SIZE]; + + /* Intermediate computation results from the ONNX model. */ + static char cats_strings[CATS_STRINGS_ROW][CATS_STRINGS_COL]; +diff --git a/gcc/ai4c-infer.h b/gcc/ai4c-infer.h +index fa5156ab1..41106a54e 100644 +--- a/gcc/ai4c-infer.h ++++ b/gcc/ai4c-infer.h +@@ -37,7 +37,7 @@ execute_sha256 (const char *, char *, size_t); + extern float read_float_from_file (FILE*); + + extern int get_optimize_decision_from_ai4c (); +-extern int get_optimize_decision_from_optimizer (int, const char **, ++extern void get_optimize_decision_from_optimizer (int, const char **, + const char *, int , + int64_t *); + extern void set_cache_info (int, int, int, int, int, int); +diff --git a/gcc/common.opt b/gcc/common.opt +index 2578c7cd0..4cd2574e4 100644 +--- a/gcc/common.opt ++++ b/gcc/common.opt +@@ -2094,6 +2094,18 @@ fipa-struct-sfc-shadow + Common Var(flag_ipa_struct_sfc_shadow) Init(0) Optimization + Enable field shadowing optimization in static struct field compression. + ++fipa-struct-dfc ++Common Var(flag_ipa_struct_dfc) Init(0) Optimization ++Perform dynamic structure field compression. ++ ++fipa-struct-dfc-bitfield ++Common Var(flag_ipa_struct_dfc_bitfield) Init(0) Optimization ++Enable compressing to bitfield in dynamic struct field compression. ++ ++fipa-struct-dfc-shadow ++Common Var(flag_ipa_struct_dfc_shadow) Init(0) Optimization ++Enable field shadowing optimization in dynamic struct field compression. ++ + fipa-extend-auto-profile + Common Var(flag_ipa_extend_auto_profile) + Use sample profile information for source code. +diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc +index b62ba344f..80242ddf8 100644 +--- a/gcc/config/aarch64/aarch64.cc ++++ b/gcc/config/aarch64/aarch64.cc +@@ -19095,7 +19095,9 @@ 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_gnu89_inline = 1; + opts->x_flag_convert_minmax = 1; + opts->x_flag_tree_slp_transpose_vectorize = 1; +@@ -19151,13 +19153,13 @@ override_CPP_optimize_options (struct gcc_options *opts) + opts->x_param_max_inline_insns_auto = 128; + opts->x_param_inline_unit_growth = 256; + opts->x_flag_cmlt_arith = 1; ++ opts->x_flag_if_conversion_gimple = 1; + } + + static void + override_optimize_options_1 (struct gcc_options *opts) + { + opts->x_flag_split_ldp_stp = 1; +- opts->x_flag_if_conversion_gimple = 1; + opts->x_flag_ifcvt_allow_complicated_cmps = 1; + opts->x_param_ifcvt_allow_register_renaming = 2; + opts->x_param_max_rtl_if_conversion_unpredictable_cost = 48; +@@ -19179,8 +19181,6 @@ override_Fortran_optimize_options (struct gcc_options *opts) + opts->x_flag_inline_functions_called_once = 0; + opts->x_flag_ira_algorithm = IRA_ALGORITHM_PRIORITY; + opts->x_flag_delayed_branch = 1; +- opts->x_flag_gcse_las = 1; +- opts->x_flag_gcse_sm = 1; + opts->x_flag_ipa_pta = 1; + opts->x_flag_reorder_blocks_and_partition = 1; + opts->x_flag_reorder_blocks = 1; +@@ -19188,6 +19188,7 @@ override_Fortran_optimize_options (struct gcc_options *opts) + opts->x_param_flexible_seg_len = 1; + opts->x_flag_alias_analysis_expand_ssa = 1; + opts->x_flag_chrec_mul_fold_strict_overflow = 1; ++ opts->x_flag_if_conversion_gimple = 1; + } + + /* Reset the optimize option. +@@ -19196,14 +19197,6 @@ 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, "tsv110") == NULL +- && strstr (opts->x_aarch64_cpu_string, "hip09") == NULL)) +- { +- return; +- } +- + const char *ai_infer_level = getenv ("AI_INFER_LEVEL"); + if (ai_infer_level) + { +diff --git a/gcc/gcc.cc b/gcc/gcc.cc +index 5bc6b7bcf..e9851b28d 100644 +--- a/gcc/gcc.cc ++++ b/gcc/gcc.cc +@@ -45,6 +45,7 @@ compilation is specified by a string called a "spec". */ + #include "filenames.h" + #include "spellcheck.h" + #include "opts-jobserver.h" ++#include "ai4c-infer.h" + + + +@@ -5809,6 +5810,7 @@ do_self_spec (const char *spec) + } + setenv ("GCC_AI4C_TUNE_INFO", tune_native, 1); + ++ + /* Mark %= sizeof (input)) ++ len = sizeof (input) - 1; ++ strncpy (input, start, len); ++ input[len] = '\0'; ++ } ++ } ++ ++ bool flag_Om = false; ++ bool flag_O3 = false; ++ bool flag_mcpu = false; ++ bool flag_native = false; ++ char mcpu_name[64]; ++ ++ for (unsigned i = 1; i < argc; i ++) ++ { ++ if (strcmp (argv[i], "-Om") == 0) ++ flag_Om = true; ++ if (strstr (argv[i], "-O3") != NULL) ++ flag_O3 = true; ++ if (strstr (argv[i], "mcpu=native") != NULL) ++ flag_native = true; ++ if (strstr (argv[i], "mcpu=") != NULL) ++ { ++ flag_mcpu = true; ++ const char* pos = strchr(argv[i], '='); ++ int len = sizeof(mcpu_name) - 1; ++ strncpy(mcpu_name, pos +1, len); ++ mcpu_name[len] = '\0'; ++ } ++ } ++ ++ if ((!flag_native) & flag_mcpu) ++ { ++ strcpy(input, mcpu_name); ++ } ++ ++ const int argc_hw = 6; ++ int64_t argv_hw[argc_hw] = { ++ global_options.x_param_simultaneous_prefetches, ++ global_options.x_param_l1_cache_size, ++ global_options.x_param_l1_cache_line_size, ++ global_options.x_param_l2_cache_size, ++ global_options.x_param_prefetch_latency, ++ global_options.x_param_ipa_prefetch_distance_factor}; ++ ++ 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 +@@ -8135,6 +8220,7 @@ driver::main (int argc, char **argv) + maybe_putenv_OFFLOAD_TARGETS (); + putenv_ONNX_FDATA (); + handle_unrecognized_options (); ++ putenv_AI_INFER_LEVEL(argc, const_cast (argv)); + + if (completion) + { +diff --git a/gcc/gcc.h b/gcc/gcc.h +index ff3ae8bed..2a7e28ce4 100644 +--- a/gcc/gcc.h ++++ b/gcc/gcc.h +@@ -47,6 +47,7 @@ class driver + void putenv_ONNX_FDATA () const; + void maybe_putenv_OFFLOAD_TARGETS () const; + void handle_unrecognized_options (); ++ void putenv_AI_INFER_LEVEL(int argc, const char **argv); + int maybe_print_and_exit () const; + bool prepare_infiles (); + void do_spec_on_infiles () const; +diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +index b8a5f029c..18b41eb1b 100644 +--- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc ++++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc +@@ -75,7 +75,6 @@ along with GCC; see the file COPYING3. If not see + #include "config.h" + #include "system.h" + #include "coretypes.h" +-#include "tm.h" + #include "tree.h" + #include "tree-pass.h" + #include "cgraph.h" +@@ -99,16 +98,17 @@ along with GCC; see the file COPYING3. If not see + #include "tree-cfg.h" + #include "alloc-pool.h" + #include "symbol-summary.h" +-#include "ipa-prop.h" ++#include "bitmap.h" + #include "ipa-struct-reorg.h" + #include "tree-eh.h" +-#include "bitmap.h" + #include "tree-ssa-live.h" /* For remove_unused_locals. */ + #include "ipa-param-manipulation.h" + #include "gimplify-me.h" + #include "cfgloop.h" + #include "langhooks.h" + #include "cfgexpand.h" ++#include "gimplify.h" ++#include + + /* Check whether in C language or LTO with only C language. */ + +@@ -149,17 +149,31 @@ using namespace struct_relayout; + #define VOID_POINTER_P(type) \ + (POINTER_TYPE_P (type) && VOID_TYPE_P (TREE_TYPE (type))) + +-#define FC_DUMP_MSG(message) \ ++#define FC_DUMP_MSG(...) \ + do \ + { \ + if (dump_file && (dump_flags & TDF_DETAILS)) \ +- fprintf (dump_file, "[field compress] %s", (message)); \ ++ { \ ++ fprintf (dump_file, "[field compress] "); \ ++ fprintf (dump_file, __VA_ARGS__); \ ++ } \ + } while (0) + ++#define STRING_STARTS_WITH(s, suffix) \ ++ (strncmp (s, suffix, sizeof (suffix) - 1) == 0) ++ + /* Flags for operand_equal_p to treat decls with the same name equal. */ + + #define COMPARE_DECL_FLAGS (OEP_DECL_NAME | OEP_LEXICOGRAPHIC) + ++#define APPEND_GASSIGN_1(gsi, lhs, op, rhs) \ ++ gsi_insert_after (&gsi, gimple_build_assign (lhs, op, rhs), \ ++ GSI_NEW_STMT) ++ ++#define APPEND_GASSIGN_2(gsi, lhs, op, rhs1, rhs2) \ ++ gsi_insert_after (&gsi, gimple_build_assign (lhs, op, rhs1, rhs2), \ ++ GSI_NEW_STMT) ++ + static void + set_var_attributes (tree var) + { +@@ -349,6 +363,212 @@ gimple_assign_rhs_code_p (gimple *stmt, enum tree_code code) + && gimple_assign_rhs_code (stmt) == code; + } + ++static fc_field * ++find_fc_field (const auto_vec &fc_fields, tree field) ++{ ++ for (auto *fc_f : fc_fields) ++ if (fc_f->field == field) ++ return fc_f; ++ ++ return NULL; ++} ++ ++/* Return true if the stmt is a copy/convert to integer. */ ++ ++static bool ++is_copy_int (const gimple *stmt) ++{ ++ if (!is_gimple_assign (stmt)) ++ return NULL_TREE; ++ ++ tree rhs = gimple_assign_rhs1 (stmt); ++ ++ if (gimple_assign_single_p (stmt)) ++ if (TREE_CODE (rhs) == SSA_NAME) ++ return true; ++ ++ if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))) ++ { ++ tree lhs = gimple_assign_lhs (stmt); ++ ++ if (TREE_CODE (rhs) == SSA_NAME ++ && TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE) ++ return true; ++ } ++ ++ return false; ++} ++ ++/* Strip the copy with typecasting of int or unsigned int. */ ++ ++static gimple * ++strip_copy_stmts (gimple *stmt) ++{ ++ while (is_copy_int (stmt)) ++ stmt = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)); ++ ++ return stmt; ++} ++ ++const char * ++get_func_name (tree decl) ++{ ++ if (!decl || TREE_CODE (decl) != FUNCTION_DECL || !DECL_NAME (decl)) ++ return NULL; ++ ++ tree decl_name = DECL_NAME (decl); ++ if (TREE_CODE (decl_name) != IDENTIFIER_NODE) ++ return NULL; ++ ++ return IDENTIFIER_POINTER (decl_name); ++} ++ ++/* Compare the gimple order of input_ssa for fc fields. */ ++ ++static int ++input_order_cmp (const void *p, const void *q) ++{ ++ const fc_field *a = *static_cast (p); ++ const fc_field *b = *static_cast (q); ++ ++ gimple *ga = SSA_NAME_DEF_STMT (a->input_ssa); ++ gimple *gb = SSA_NAME_DEF_STMT (b->input_ssa); ++ ++ if (gimple_uid (ga) < gimple_uid (gb)) ++ return -1; ++ else if (gimple_uid (ga) > gimple_uid (gb)) ++ return 1; ++ else ++ return 0; ++} ++ ++/* Called by walk_tree to check if ssa_name DATA exists in an expression. */ ++ ++static tree ++check_for_ssa (tree *opnd_ptr, int *walk_subtrees ATTRIBUTE_UNUSED, void *data) ++{ ++ tree ssa = (tree) data; ++ if (*opnd_ptr == ssa) ++ return ssa; ++ ++ return NULL_TREE; ++} ++ ++/* Helper to create a function declaration together with arguments and result ++ declarations. */ ++ ++static tree ++create_new_fn_decl (char *fn_name, int n_args, tree *arg_types, ++ tree return_type) ++{ ++ tree fn_type = build_function_type_array (return_type, n_args, arg_types); ++ tree fndecl = build_fn_decl (fn_name, fn_type); ++ tree id = get_identifier (fn_name); ++ SET_DECL_ASSEMBLER_NAME (fndecl, id); ++ DECL_NAME (fndecl) = id; ++ DECL_ARTIFICIAL (fndecl) = 1; ++ DECL_EXTERNAL (fndecl) = 0; ++ DECL_CONTEXT (fndecl) = NULL_TREE; ++ DECL_INITIAL (fndecl) = make_node (BLOCK); ++ DECL_STATIC_CONSTRUCTOR (fndecl) = 0; ++ ++ /* Function result declairation. */ ++ tree resdecl ++ = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, return_type); ++ DECL_RESULT (fndecl) = resdecl; ++ ++ /* Function arguments. */ ++ tree prev_arg = NULL_TREE; ++ for (int i = 0; i < n_args; i++) ++ { ++ tree arg_decl ++ = build_decl (UNKNOWN_LOCATION, PARM_DECL, NULL, arg_types[i]); ++ DECL_ARTIFICIAL (arg_decl) = 1; ++ DECL_IGNORED_P (arg_decl) = 1; ++ TREE_USED (arg_decl) = 1; ++ DECL_CONTEXT (arg_decl) = fndecl; ++ DECL_ARG_TYPE (arg_decl) = arg_types[i]; ++ TREE_READONLY (arg_decl) = 1; ++ if (prev_arg) ++ TREE_CHAIN (prev_arg) = arg_decl; ++ else ++ DECL_ARGUMENTS (fndecl) = arg_decl; ++ prev_arg = arg_decl; ++ } ++ ++ return fndecl; ++} ++ ++static void ++release_srdecl_ssa_name (srdecl *srd) ++{ ++ if (!srd->has_new_decl ()) ++ return; ++ ++ tree ssa_name = NULL_TREE; ++ if (srd->argumentnum >= 0) ++ ssa_name = ssa_default_def (cfun, srd->decl); ++ else if (TREE_CODE (srd->decl) == SSA_NAME) ++ ssa_name = srd->decl; ++ ++ if (ssa_name && num_imm_uses (ssa_name) == 0) ++ release_ssa_name (ssa_name); ++} ++ ++static char * ++append_suffix (const char *s1, unsigned suffix) ++{ ++ char s2[32]; ++ sprintf (s2, "%u", suffix); ++ return concat (s1, s2, NULL); ++} ++ ++static unsigned HOST_WIDE_INT ++get_bitsize (tree field) ++{ ++ tree bitsize = DECL_BIT_FIELD (field) ? DECL_SIZE (field) ++ : TYPE_SIZE (TREE_TYPE (field)); ++ return tree_to_uhwi (bitsize); ++} ++ ++/* Generate SSA_NAME for the given var. */ ++ ++static tree ++generate_ssa_name (tree var, gimple_stmt_iterator *gsi) ++{ ++ if (TREE_CODE (var) == SSA_NAME) ++ return var; ++ ++ tree name = make_ssa_name (TREE_TYPE (var)); ++ gimple *stmt = gimple_build_assign (name, var); ++ gsi_insert_after (gsi, stmt, GSI_NEW_STMT); ++ ++ return name; ++} ++ ++/* Get type node by bit precision and sign. */ ++ ++static tree ++get_integer_type_node (unsigned precision, bool is_unsigned) ++{ ++ switch (precision) ++ { ++ case 64: ++ return is_unsigned ? long_long_unsigned_type_node ++ : long_long_integer_type_node; ++ case 32: ++ return is_unsigned ? unsigned_type_node : integer_type_node; ++ case 16: ++ return is_unsigned ? short_unsigned_type_node ++ : short_integer_type_node; ++ case 8: ++ return is_unsigned ? unsigned_char_type_node ++ : signed_char_type_node; ++ default: ++ return NULL_TREE; ++ } ++} ++ + /* Enum the struct layout optimize level, + which should be the same as the option -fstruct-reorg=. */ + +@@ -364,6 +584,15 @@ enum struct_layout_opt_level + SEMI_RELAYOUT = 1 << 6 + }; + ++enum class fc_level ++{ ++ NONE, ++ STATIC, ++ DYNAMIC ++}; ++ ++fc_level current_fc_level; ++ + srfunction *current_function; + vec csrfun_stack; + +@@ -387,6 +616,31 @@ public: + + #define SET_CFUN(srfn) csrfun_context csrfn_ctx(srfn); + ++/* RAII class to change current dump_file and dump_flags, ++ and restore when the object goes out of scope. */ ++ ++class dump_file_saver ++{ ++public: ++ dump_file_saver (FILE *file, dump_flags_t flags) ++ { ++ old_dump_file = dump_file; ++ old_dump_flags = dump_flags; ++ dump_file = file; ++ dump_flags = flags; ++ } ++ ~dump_file_saver () ++ { ++ dump_file = old_dump_file; ++ dump_flags = old_dump_flags; ++ } ++private: ++ FILE *old_dump_file; ++ dump_flags_t old_dump_flags; ++}; ++ ++#define SET_DUMP_FILE(file, flags) dump_file_saver fd_saver(file, flags); ++ + /* Defines the target pointer size of compressed pointer, which should be 8, + 16, 32. */ + +@@ -502,7 +756,7 @@ srfield::srfield (tree field, srtype *base) + type (NULL), + clusternum (0), + field_access (EMPTY_FIELD), +- static_fc_field (NULL), ++ fc_f (NULL), + field_class (NULL) + { + for (int i = 0; i < max_split; i++) +@@ -531,7 +785,8 @@ srtype::srtype (tree type) + { + if (TREE_CODE (field) == FIELD_DECL) + { +- if (DECL_BIT_FIELD (field)) ++ if (current_fc_level != fc_level::DYNAMIC ++ && DECL_BIT_FIELD (field)) + { + escapes = escape_bitfields; + continue; +@@ -698,6 +953,20 @@ srfunction::record_decl (srtype *type, tree decl, int arg, tree orig_type) + return decl1; + } + ++/* A function is either partially cloned or fully cloned (versioning). */ ++ ++bool ++srfunction::partial_clone_p () ++{ ++ return fc_path.start_stmt != NULL; ++} ++ ++bool ++srfunction::entry_function_p () ++{ ++ return strcmp (node->name (), "main") == 0 && !node->callers; ++} ++ + /* Find the field at OFF offset. */ + + srfield * +@@ -716,6 +985,17 @@ srtype::find_field (unsigned HOST_WIDE_INT off) + return NULL; + } + ++/* Find the field according to field decl. */ ++srfield * ++srtype::find_field_by_decl (tree fielddecl) ++{ ++ for (auto *field : fields) ++ if (operand_equal_p (fielddecl, field->fielddecl, COMPARE_DECL_FLAGS)) ++ return field; ++ ++ return NULL; ++} ++ + /* Add the function FN to the list of functions if it + is there not already. */ + +@@ -900,8 +1180,9 @@ srfield::reorder_fields (tree newfields[max_split], tree newlast[max_split], + else + { + tree tmp = newfields[clusternum]; +- if (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (field))) +- > tree_to_uhwi (TYPE_SIZE (TREE_TYPE (tmp)))) ++ auto field_bitsize = get_bitsize (field); ++ auto tmp_bitsize = get_bitsize (tmp); ++ if (field_bitsize > tmp_bitsize) + { + DECL_CHAIN (field) = tmp; + newfields[clusternum] = field; +@@ -909,9 +1190,7 @@ srfield::reorder_fields (tree newfields[max_split], tree newlast[max_split], + else + { + while (DECL_CHAIN (tmp) +- && (tree_to_uhwi (TYPE_SIZE (TREE_TYPE (field))) +- <= tree_to_uhwi ( +- TYPE_SIZE (TREE_TYPE (DECL_CHAIN (tmp)))))) ++ && field_bitsize <= get_bitsize (DECL_CHAIN (tmp))) + tmp = DECL_CHAIN (tmp); + + /* Now tmp size > field size +@@ -932,11 +1211,18 @@ srfield::create_new_reorder_fields (tree newtype[max_split], + tree newfields[max_split], + tree newlast[max_split]) + { ++ /* For dynamic shadow. */ ++ if (current_fc_level == fc_level::DYNAMIC && fc_f && fc_f->original) ++ { ++ newfield[0] = NULL_TREE; ++ return; ++ } ++ + /* newtype, corresponding to newtype[max_split] in srtype. */ + tree nt = NULL_TREE; + if (type == NULL) + /* Common var. */ +- nt = static_fc_field ? static_fc_field->new_type : fieldtype; ++ nt = fc_f ? fc_f->new_type : fieldtype; + else + /* RECORD_TYPE var. */ + nt = type->has_escaped () ? type->type : type->newtype[0]; +@@ -986,24 +1272,28 @@ srfield::create_new_reorder_fields (tree newtype[max_split], + TREE_THIS_VOLATILE (field) = TREE_THIS_VOLATILE (fielddecl); + DECL_CONTEXT (field) = newtype[clusternum]; + +- if (flag_ipa_struct_sfc && base->fc_info && base->fc_info->static_fc_p) ++ fc_type_info *info = base->fc_info; ++ if (info && (info->static_fc_p || info->dynamic_fc_p)) + { + DECL_PACKED (field) = 1; ++ DECL_BIT_FIELD (field) = DECL_BIT_FIELD (fielddecl); ++ if (DECL_BIT_FIELD (field)) ++ DECL_SIZE (field) = DECL_SIZE (fielddecl); + +- if (static_fc_field) ++ if (fc_f) + { + /* Always not align compressed fields. */ + SET_DECL_ALIGN (field, 0); + +- if (static_fc_field->bits) ++ if (fc_f->bits) + { + DECL_BIT_FIELD (field) = 1; +- DECL_SIZE (field) = bitsize_int (static_fc_field->bits); ++ DECL_SIZE (field) = bitsize_int (fc_f->bits); + DECL_NONADDRESSABLE_P (field) = 1; + /* Build unsigned bitfield integer type. */ +- nt = build_nonstandard_integer_type (static_fc_field->bits, 1); ++ nt = build_nonstandard_integer_type (fc_f->bits, 1); + TREE_TYPE (field) = nt; +- static_fc_field->new_type = nt; ++ fc_f->new_type = nt; + } + } + } +@@ -1022,6 +1312,19 @@ srfield::dead_field_p () + && !FUNCTION_POINTER_TYPE_P (fieldtype); + } + ++bool ++srfield::dfc_type_change_p () ++{ ++ return fc_f && fc_f->cond ++ && fc_f->cond->old_type != TREE_TYPE (newfield[0]); ++} ++ ++fc_closure * ++srfield::get_closure () ++{ ++ return &(field_class->closure); ++} ++ + /* Given a struct s whose fields has already reordered by size, we try to + combine fields less than 8 bytes together to 8 bytes. Example: + struct s { +@@ -1095,22 +1398,38 @@ srtype::has_recursive_field_type () + void + srtype::check_fc_fields () + { +- if (!fc_info || !fc_info->static_fc_p) ++ if (!fc_info || (!fc_info->static_fc_p && !fc_info->dynamic_fc_p)) + return; + +- for (unsigned i = 0; i < fields.length (); i++) ++ for (auto *srf : fields) + { +- fc_field *fc_f; +- unsigned j; +- FOR_EACH_VEC_ELT (fc_info->static_fc_fields, j, fc_f) +- if (fields[i]->fielddecl == fc_f->field) ++ tree field = srf->fielddecl; ++ if (fc_info->static_fc_p) ++ srf->fc_f = find_fc_field (fc_info->static_fc_fields, field); ++ else + { +- fields[i]->static_fc_field = fc_f; +- break; ++ srf->fc_f = find_fc_field (fc_info->dynamic_shadow_fields, field); ++ if (!srf->fc_f) ++ srf->fc_f = find_fc_field (fc_info->dynamic_fc_fields, field); + } + } + } + ++bool ++srtype::reorg_name_p () ++{ ++ const char *name = get_type_name (type); ++ return name && strstr (name, ".reorg"); ++} ++ ++bool ++srtype::has_escaped () ++{ ++ return escapes != does_not_escape ++ && (current_fc_level != fc_level::DYNAMIC ++ || !reorg_name_p ()); ++} ++ + /* Create the new TYPE corresponding to THIS type. */ + + bool +@@ -1124,7 +1443,7 @@ srtype::create_new_type (void) + + visited = true; + +- if (escapes != does_not_escape) ++ if (has_escaped ()) + { + newtype[0] = type; + return false; +@@ -1176,10 +1495,10 @@ srtype::create_new_type (void) + if (tname) + { + name = concat (tname, ".reorg.", id, NULL); +- TYPE_NAME (newtype[i]) = build_decl (UNKNOWN_LOCATION, +- TYPE_DECL, +- get_identifier (name), +- newtype[i]); ++ tree name_id = get_identifier (name); ++ TYPE_STUB_DECL (newtype[i]) ++ = build_decl (UNKNOWN_LOCATION, TYPE_DECL, name_id, newtype[i]); ++ TYPE_NAME (newtype[i]) = name_id; + free (name); + } + } +@@ -1206,8 +1525,12 @@ srtype::create_new_type (void) + { + TYPE_FIELDS (newtype[i]) = newfields[i]; + layout_type (newtype[i]); +- if (TYPE_NAME (newtype[i]) != NULL) +- layout_decl (TYPE_NAME (newtype[i]), 0); ++ } ++ ++ if (current_fc_level == fc_level::DYNAMIC) ++ { ++ gcc_assert (maxclusters == 1); ++ fc_info->variant->new_type = newtype[0]; + } + + warn_padded = save_warn_padded; +@@ -1277,7 +1600,7 @@ srfunction::create_new_decls (void) + return; + + if (node) +- set_cfun (DECL_STRUCT_FUNCTION (node->decl)); ++ push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + + for (unsigned i = 0; i < decls.length (); i++) + { +@@ -1388,7 +1711,8 @@ srfunction::create_new_decls (void) + } + } + +- set_cfun (NULL); ++ if (node) ++ pop_cfun (); + } + + /* Dump out the field structure to FILE. */ +@@ -1459,34 +1783,34 @@ sraccess::dump (FILE *f) const + fprintf (f, "}\n"); + } + +-/* Check if it's an assignment to the given type. */ ++/* Check if it's an assignment to the given field(fielddecl != NULL_TREE) ++ or any field(fielddecl == NULL_TREE). */ + + bool +-sraccess::write_type_p (tree type) const ++sraccess::write_field_p (tree fielddecl) const + { +- return this->type && this->type->type == type +- && is_gimple_assign (stmt) +- && index == 0; ++ return write_p () && field && (!fielddecl || field->fielddecl == fielddecl); + } + +-/* Check if it's an assignment to the given field. */ ++/* Check if it's an assignment that read the given ++ field(fielddecl != NULL_TREE) or any field(fielddecl == NULL_TREE). */ + + bool +-sraccess::write_field_p (tree fielddecl) const ++sraccess::read_field_p (tree fielddecl) const + { +- return field && field->fielddecl == fielddecl +- && is_gimple_assign (stmt) +- && index == 0; ++ return read_p () && field && (!fielddecl || field->fielddecl == fielddecl); + } + +-/* Check if it's an assignment that read the given field. */ ++bool ++sraccess::write_p () const ++{ ++ return is_gimple_assign (stmt) && index == 0; ++} + + bool +-sraccess::read_field_p (tree fielddecl) const ++sraccess::read_p () const + { +- return field && field->fielddecl == fielddecl +- && is_gimple_assign (stmt) +- && index > 0; ++ return is_gimple_assign (stmt) && index > 0; + } + + /* Dump out the decl structure to FILE. */ +@@ -1504,6 +1828,133 @@ srdecl::dump (FILE *file) + type->simple_dump (file); + } + ++void ++fc_closure::add_read_change (gimple *stmt) ++{ ++ if (!read_change_set.contains (stmt)) ++ read_change_set.add (stmt); ++} ++ ++bool ++fc_closure::read_change_p (gimple *stmt) ++{ ++ return read_change_set.contains (stmt); ++} ++ ++void ++fc_closure::add_read_unchange (gimple *stmt) ++{ ++ if (!read_unchange_set.contains (stmt)) ++ read_unchange_set.add (stmt); ++} ++ ++bool ++fc_closure::read_unchange_p (gimple *stmt) ++{ ++ return read_unchange_set.contains (stmt); ++} ++ ++void ++fc_closure::add_write_change (gimple *stmt) ++{ ++ if (!write_change_set.contains (stmt)) ++ write_change_set.add (stmt); ++} ++ ++bool ++fc_closure::write_change_p (gimple *stmt) ++{ ++ return write_change_set.contains (stmt); ++} ++ ++void ++fc_closure::add_write_unchange (gimple *stmt) ++{ ++ if (!write_unchange_set.contains (stmt)) ++ write_unchange_set.add (stmt); ++} ++ ++bool ++fc_closure::write_unchange_p (gimple *stmt) ++{ ++ return write_unchange_set.contains (stmt); ++} ++ ++bool ++fc_closure::change_p (gimple *stmt) ++{ ++ return write_change_p (stmt) || read_change_p (stmt); ++} ++ ++bool ++fc_closure::unchange_p (gimple *stmt) ++{ ++ return write_unchange_p (stmt) || read_unchange_p (stmt); ++} ++ ++/* Call compress/decompress function for rhs. */ ++ ++tree ++fc_closure::convert_rhs (tree rhs, tree fn) ++{ ++ tree newrhs = build_call_expr (fn, 1, rhs); ++ cgraph_node *callee = cgraph_node::get (fn); ++ cgraph_node *node = cgraph_node::get (current_function_decl); ++ node->create_edge (callee, NULL, profile_count::zero ()); ++ ++ return newrhs; ++} ++ ++void ++closure_helper::record_origin_closure (basic_block bb) ++{ ++ for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) ++ { ++ gimple *stmt = gsi_stmt (si); ++ if (!is_gimple_assign (stmt)) ++ continue; ++ ++ uid++; ++ ++ if (cinfo->read_change_p (stmt)) ++ bitmap_set_bit (read_change_map, uid); ++ else if (cinfo->write_change_p (stmt)) ++ bitmap_set_bit (write_change_map, uid); ++ else if (cinfo->read_unchange_p (stmt)) ++ bitmap_set_bit (read_unchange_map, uid); ++ else if (cinfo->write_unchange_p (stmt)) ++ bitmap_set_bit (write_unchange_map, uid); ++ } ++} ++ ++void ++closure_helper::add_cloned_closure (basic_block bb) ++{ ++ for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) ++ { ++ gimple *stmt = gsi_stmt (si); ++ if (!is_gimple_assign (stmt)) ++ continue; ++ ++ uid++; ++ ++ if (bitmap_bit_p (read_change_map, uid)) ++ cinfo->add_read_change (stmt); ++ else if (bitmap_bit_p (write_change_map, uid)) ++ cinfo->add_write_change (stmt); ++ else if (bitmap_bit_p (read_unchange_map, uid)) ++ cinfo->add_read_unchange (stmt); ++ else if (bitmap_bit_p (write_unchange_map, uid)) ++ cinfo->add_write_unchange (stmt); ++ } ++} ++ ++void ++closure_helper::reset_uid () ++{ ++ uid = 0; ++} ++ + void + fc_field_class::dump (FILE *file) const + { +@@ -1545,6 +1996,37 @@ fc_field_class::get_field_index (srfield *field) const + return -1; + } + ++void ++fc_ref::dump (FILE *file) const ++{ ++ fprintf (file, "var: "); ++ print_generic_expr (dump_file, var); ++ fprintf (dump_file, ", type: "); ++ print_generic_expr (dump_file, orig_type ? orig_type : TREE_TYPE (var)); ++ fprintf (dump_file, ", array: "); ++ print_generic_expr (dump_file, source->var); ++ if (size) ++ { ++ fprintf (dump_file, ", array size: "); ++ print_generic_expr (dump_file, size); ++ } ++ if (field) ++ { ++ fprintf (dump_file, ", field: "); ++ print_generic_expr (dump_file, field); ++ } ++ fprintf (dump_file, "\n"); ++} ++ ++fc_type_info::~fc_type_info () ++{ ++ if (variant) ++ { ++ delete variant; ++ variant = NULL; ++ } ++} ++ + fc_field_class * + fc_type_info::find_field_class_by_type (tree type) const + { +@@ -1577,12 +2059,131 @@ fc_type_info::record_field_class (srfield *srf) + return field_class; + } + +-} // namespace struct_reorg ++fc_cond * ++fc_type_info::find_cond (tree type) const ++{ ++ for (auto *cond : fc_conds) ++ { ++ if (cond->old_type == type) ++ return cond; ++ } + ++ return NULL; ++} + +-namespace struct_relayout { ++fc_cond * ++fc_type_info::create_cond (tree type) ++{ ++ fc_cond *cond = find_cond (type); ++ if (cond) ++ return cond; + +-/* Complete Structure Relayout Optimization. ++ /* New cond will be stored in an auto_delete_vec(fc_conds). */ ++ cond = new fc_cond (type); ++ fc_conds.safe_push (cond); ++ ++ /* Record the fc_cond to corresponding fc_field_class. */ ++ fc_field_class *field_class = find_field_class_by_type (type); ++ gcc_assert (field_class); ++ field_class->cond = cond; ++ cond->field_class = field_class; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Create new fc_cond, type: "); ++ print_generic_expr (dump_file, type); ++ fprintf (dump_file, "\n"); ++ } ++ ++ return cond; ++} ++ ++void ++fc_type_info::record_cond (fc_field *fc_f) ++{ ++ if (fc_f->cond) ++ return; ++ ++ fc_cond *cond = create_cond (TREE_TYPE (fc_f->field)); ++ fc_f->cond = cond; ++ cond->fields.safe_push (fc_f); ++} ++ ++fc_path_info::~fc_path_info () ++{ ++ if (cloned_func) ++ delete cloned_func; ++} ++ ++/* Search and store basic_blocks that: ++ 1) can reach STMT (when DIRECTION == PRED); ++ 2) can be reached from STMT (when DIRECTION == SUCC). ++ Return false if field compression cannot be performed. */ ++ ++bool ++fc_path_info::collect_blocks (gimple *stmt, direction dir) ++{ ++ basic_block start_bb = gimple_bb (stmt); ++ if (!start_bb) ++ return false; ++ ++ /* The start block should not be in a loop. */ ++ if (start_bb->loop_father != NULL ++ && loop_outer (start_bb->loop_father) != NULL) ++ return false; ++ ++ bool prev = dir == direction::PRED; ++ basic_block stop_bb = prev ? ENTRY_BLOCK_PTR_FOR_FN (cfun) ++ : EXIT_BLOCK_PTR_FOR_FN (cfun); ++ auto *store_list = prev ? &pre_bbs : &reach_bbs; ++ ++ auto_bitmap visited; ++ auto_vec worklist; ++ worklist.safe_push (start_bb); ++ bool exit_p = false; ++ ++ while (!worklist.is_empty ()) ++ { ++ basic_block bb = worklist.pop (); ++ if (!bitmap_set_bit (visited, bb->index)) ++ continue; ++ ++ if (bb != stop_bb) ++ store_list->safe_push (bb); ++ else ++ exit_p = true; ++ ++ if (prev) ++ for (auto *e : bb->preds) ++ worklist.safe_push (e->src); ++ else ++ for (auto *e : bb->succs) ++ worklist.safe_push (e->dest); ++ } ++ ++ if (!exit_p) ++ return false; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "Found %d blocks in func ", store_list->length ()); ++ print_generic_expr (dump_file, cfun->decl); ++ fprintf (dump_file, " %s:\n", ++ prev ? "before start-point" : "to be cloned"); ++ for (auto *bb : store_list) ++ fprintf (dump_file, "%d ", bb->index); ++ fprintf (dump_file, "\n\n"); ++ } ++ ++ return true; ++} ++ ++} // namespace struct_reorg ++ ++ ++namespace struct_relayout { ++ ++/* Complete Structure Relayout Optimization. + It reorganizes all structure members, and puts same member together. + struct s { + long a; +@@ -1680,7 +2281,11 @@ struct const_map + struct ipa_struct_reorg + { + private: ++ /* The current srfield set in rewrite_expr. For dfc. */ ++ srfield *cur_srfd; ++ + auto_vec_del global_consts; ++ hash_set visited_vars; + + public: + // Constructors +@@ -1712,6 +2317,8 @@ public: + void detect_cycles (void); + bool walk_field_for_cycles (srtype *); + void prune_escaped_types (void); ++ void prune_function (srfunction *); ++ void prune_globals (); + void propagate_escape (void); + void propagate_escape_via_original (void); + void propagate_escape_via_empty_with_no_original (void); +@@ -1725,11 +2332,12 @@ public: + void create_new_functions (void); + void create_new_args (cgraph_node *new_node); + unsigned rewrite_functions (void); ++ void rewrite_block (basic_block); + srdecl *record_var (tree decl, + escape_type escapes = does_not_escape, + int arg = -1); + void record_safe_func_with_void_ptr_parm (void); +- srfunction *record_function (cgraph_node *node); ++ srfunction *record_function (cgraph_node *node, srfunction *sfn = NULL); + srfunction *find_function (cgraph_node *node); + void record_field_type (tree field, srtype *base_srtype); + void record_struct_field_types (tree base_type, srtype *base_srtype); +@@ -1856,8 +2464,8 @@ public: + const auto_vec &); + srfield *read_field_in_fc_class_p (gimple *, fc_field_class *); + srfield *write_field_in_fc_class_p (gimple *, fc_field_class *); +- fc_field *fc_fields_contains (auto_vec &, tree); + bool fc_pair_stmts_rhs_equal_p (const auto_vec &); ++ bool unique_init_const_p (const fc_shadow_info &); + bool fc_operand_equal_p (tree, tree); + bool fc_global_const_p (tree, HOST_WIDE_INT &); + bool fc_peephole_const_p (tree, HOST_WIDE_INT &); +@@ -1873,10 +2481,92 @@ public: + bool find_hot_access (fc_type_info *, auto_vec &); + void cleanup_shadow_write (fc_type_info *); + void rewrite_shadow_read (fc_type_info *); +- void insert_shadow_stmt (gimple *, unsigned, fc_field *, tree); ++ void modify_shadow_read (gimple *, unsigned, fc_field *, tree); + bool compress_fields_static (fc_type_info *info); +- void compress_to_bitfields (fc_type_info *info); ++ bool compress_to_bitfield_static (fc_type_info *info); ++ bool compress_to_bitfield_dynamic (fc_type_info *info); + auto_vec collect_all_predecessor (gimple *); ++ bool types_fc_equal_p (tree, tree); ++ bool types_fc_compatible_p (tree, tree); ++ bool find_dynamic_fc_fields (fc_type_info *); ++ bool find_fields_in_input_stmt (fc_type_info *); ++ bool find_input_stmt (gimple *, gimple *&, gimple *&); ++ tree find_file_handler (gimple *); ++ bool find_fopen_fclose (fc_type_info *); ++ bool check_dynamic_shadow_fields (fc_type_info *); ++ bool find_fc_paths (fc_type_info *); ++ bool find_fc_data (fc_type_info *); ++ bool find_fc_arrays (fc_type_info *); ++ bool find_fc_array (fc_type_info *, tree, varpool_node *); ++ bool duplicative_array_p (fc_type_info *, tree); ++ bool is_stmt_before_fclose (fc_type_info *, gimple *, symtab_node *); ++ bool reorg_ptr_p (tree); ++ bool get_allocate_size_iterate (tree, gimple *, tree &, tree * = NULL); ++ bool get_allocate_size_assign (tree, gassign *, tree &, tree *); ++ bool get_allocate_size_call (tree, gcall *, tree &, tree *); ++ bool get_allocate_size_reorg_ptr (gimple *, tree &); ++ tree get_allocate_size (tree, tree, tree, gimple *); ++ bool find_fc_refs (fc_type_info *); ++ bool find_fc_refs_iterate (fc_type_info *, fc_array *, tree, bool); ++ bool find_fc_refs_ssa_name (fc_type_info *, fc_array *, tree, bool); ++ bool find_fc_refs_mem_ref (fc_type_info *, fc_array *, tree); ++ bool find_fc_refs_component_ref (fc_type_info *, fc_array *, tree); ++ bool fc_type_pointer_p (fc_type_info *, tree); ++ bool add_fc_ref (fc_type_info *, fc_array *, tree, tree); ++ check_ref_result check_duplicative_ref (fc_type_info *, fc_array *, tree, ++ tree, tree &, tree &); ++ gimple *find_def_stmt_before_fclose (fc_type_info *, tree); ++ tree get_ptr_decl (tree); ++ bool check_fc_array_uses (fc_type_info *); ++ void calc_fc_ref_count (fc_type_info *); ++ bool compress_fields_dynamic (fc_type_info *); ++ bool calc_dynamic_boundary (fc_type_info *); ++ bool fc_cond_field_p (tree, const fc_cond *); ++ bool fc_input_ssa_p (tree, const fc_cond *); ++ bool fc_field_load_p (tree, const fc_cond *); ++ void update_high_bound (fc_cond *, HOST_WIDE_INT); ++ bool check_closure (fc_type_info *); ++ bool check_closure (fc_type_info *, fc_cond *); ++ bool write_field_class_only_p (fc_type_info *, fc_field_class *, tree); ++ void collect_closure_read_change (fc_type_info *, fc_field_class *); ++ unsigned execute_dynamic_field_compression (); ++ unsigned dynamic_fc_rewrite (); ++ bool create_dynamic_fc_newtypes (); ++ void create_dynamic_fc_variant (fc_type_info *); ++ void create_global_var_dfc_path (fc_type_info *); ++ void create_dynamic_fc_convert_fn (fc_type_info *); ++ tree create_convert_fn (fc_cond *, unsigned, bool); ++ edge create_normal_part (fc_cond *); ++ void create_conversion_part (fc_cond *, edge, bool); ++ void clone_dynamic_fc_path (fc_type_info *); ++ void clone_partial_func (fc_type_info *, srfunction *); ++ void clone_whole_func (srfunction *); ++ void rewrite_dynamic_shadow_fields (fc_type_info *); ++ void rewrite_dynamic_fc_path (); ++ void record_dfc_path_info (fc_type_info *); ++ void collect_closure_info_dynamic (fc_type_info *); ++ void collect_closure_info_partial (srfunction *, fc_closure *); ++ void collect_closure_info_whole (srfunction *, fc_closure *); ++ void rewrite_partial_func (srfunction *); ++ void rewrite_whole_func (srfunction *); ++ void clean_func_after_rewrite (srfunction *); ++ void dynamic_fc_rewrite_assign (gimple *, tree, tree &, tree &); ++ void add_dynamic_checking (fc_type_info *); ++ void insert_code_calc_dfc_path (fc_type_info *); ++ void insert_code_calc_max_min_val (fc_type_info *); ++ tree insert_code_calc_cond (fc_type_info *, gimple_stmt_iterator *); ++ void insert_code_check_init_const (fc_type_info *, gimple_stmt_iterator *, ++ tree &); ++ void insert_code_compress_data (fc_type_info *, edge); ++ void insert_code_compress_variant (fc_type_info *, basic_block, ++ const auto_vec &, ++ const auto_vec &); ++ void insert_code_compress_array (fc_type_info *, edge &, ++ const auto_vec &, ++ const auto_vec &); ++ void insert_code_modify_refs (fc_type_info *, edge); ++ void create_compress_object_fn (fc_type_info *); ++ edge insert_code_modify_single_ref (edge, tree, fc_array *, tree, tree); + }; + + struct ipa_struct_relayout +@@ -2248,6 +2938,8 @@ ipa_struct_relayout::rewrite_address (tree xhs, gimple_stmt_iterator *gsi) + /* Emit gimple _X4 = gptr[I]. */ + tree gptr_field_ssa = create_ssa (gptr[field_num], gsi); + tree new_address = make_ssa_name (TREE_TYPE (gptr[field_num])); ++ tree new_address_type = TREE_TYPE (new_address); ++ tree new_type = TREE_TYPE (new_address_type); + gassign *new_stmt = gimple_build_assign (new_address, POINTER_PLUS_EXPR, + gptr_field_ssa, step3); + gsi_insert_before (gsi, new_stmt, GSI_SAME_STMT); +@@ -2257,9 +2949,15 @@ ipa_struct_relayout::rewrite_address (tree xhs, gimple_stmt_iterator *gsi) + should be transformed to + MEM[gptr + sizeof (member)] = 0B + */ +- HOST_WIDE_INT size +- = tree_to_shwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (new_address)))); +- tree new_size = rewrite_offset (pointer_offset, size); ++ tree new_size = NULL_TREE; ++ if (integer_zerop (pointer_offset)) ++ new_size = build_int_cst (TREE_TYPE (new_address), 0); ++ else ++ { ++ HOST_WIDE_INT size = tree_to_shwi (TYPE_SIZE_UNIT (new_type)); ++ new_size = rewrite_offset (pointer_offset, size); ++ } ++ + if (new_size) + TREE_OPERAND (mem_ref, 1) = new_size; + +@@ -2531,7 +3229,7 @@ ipa_struct_reorg::dump_newtypes (FILE *f) + srtype *type = NULL; + FOR_EACH_VEC_ELT (types, i, type) + { +- if (type->has_escaped ()) ++ if (!type->has_new_type ()) + continue; + fprintf (f, "======= the %dth newtype: ======\n", i); + fprintf (f, "type : "); +@@ -3134,7 +3832,7 @@ check_each_call (cgraph_node *node, cgraph_edge *caller) + return false; + } + +- if (!check_node_def (ptr_layers)) ++ if (current_fc_level != fc_level::DYNAMIC && !check_node_def (ptr_layers)) + return false; + return true; + } +@@ -4066,61 +4764,10 @@ ipa_struct_reorg::handled_allocation_stmt (gimple *stmt) + tree + ipa_struct_reorg::allocate_size (srtype *type, srdecl *decl, gimple *stmt) + { +- if (!stmt +- || gimple_code (stmt) != GIMPLE_CALL +- || !handled_allocation_stmt (stmt)) +- { +- if (dump_file && (dump_flags & TDF_DETAILS)) +- { +- fprintf (dump_file, "\nNot a allocate statment:\n"); +- print_gimple_stmt (dump_file, stmt, 0); +- fprintf (dump_file, "\n"); +- } +- return NULL; +- } +- + if (type->has_escaped ()) + return NULL; + +- tree struct_size = TYPE_SIZE_UNIT (type->type); +- +- /* Specify the correct size to relax multi-layer pointer. */ +- if (TREE_CODE (decl->decl) == SSA_NAME && isptrptr (decl->orig_type)) +- struct_size = TYPE_SIZE_UNIT (decl->orig_type); +- +- tree size = gimple_call_arg (stmt, 0); +- +- if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) +- || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC)) +- size = gimple_call_arg (stmt, 1); +- else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) +- { +- tree arg1; +- arg1 = gimple_call_arg (stmt, 1); +- /* Check that second argument is a constant equal to +- the size of structure. */ +- if (operand_equal_p (arg1, struct_size, 0)) +- return size; +- /* ??? Check that first argument is a constant +- equal to the size of structure. */ +- /* If the allocated number is equal to the value of struct_size, +- the value of arg1 is changed to the allocated number. */ +- if (operand_equal_p (size, struct_size, 0)) +- return arg1; +- if (dump_file && (dump_flags & TDF_DETAILS)) +- { +- fprintf (dump_file, "\ncalloc the correct size:\n"); +- print_gimple_stmt (dump_file, stmt, 0); +- fprintf (dump_file, "\n"); +- } +- return NULL; +- } +- +- tree num; +- if (!is_result_of_mult (size, &num, struct_size)) +- return NULL; +- +- return num; ++ return get_allocate_size (type->type, decl->decl, decl->orig_type, stmt); + } + + void +@@ -4423,18 +5070,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; + +@@ -4546,7 +5181,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)) +@@ -4751,8 +5393,15 @@ srfunction * + ipa_struct_reorg::find_function (cgraph_node *node) + { + for (unsigned i = 0; i < functions.length (); i++) +- if (functions[i]->node == node) +- return functions[i]; ++ { ++ if (functions[i]->node == node) ++ return functions[i]; ++ ++ srfunction *cloned_func = functions[i]->fc_path.cloned_func; ++ if (current_fc_level == fc_level::DYNAMIC ++ && cloned_func && cloned_func->node == node) ++ return cloned_func; ++ } + return NULL; + } + +@@ -5383,12 +6032,11 @@ ipa_struct_reorg::check_uses (srdecl *decl, vec &worklist) + /* Record function corresponding to NODE. */ + + srfunction * +-ipa_struct_reorg::record_function (cgraph_node *node) ++ipa_struct_reorg::record_function (cgraph_node *node, srfunction *sfn) + { + function *fn; + tree parm, var; + unsigned int i; +- srfunction *sfn = NULL; + escape_type escapes = does_not_escape; + + if (dump_file && (dump_flags & TDF_DETAILS)) +@@ -5408,8 +6056,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; + +@@ -5935,38 +6586,7 @@ ipa_struct_reorg::prune_escaped_types (void) + for (unsigned i = 0; i < functions.length ();) + { + srfunction *function = functions[i]; +- +- /* Prune function arguments of types that escape. */ +- for (unsigned j = 0; j < function->args.length ();) +- { +- if (function->args[j]->type->has_escaped ()) +- function->args.ordered_remove (j); +- else +- j++; +- } +- +- /* Prune global variables that the function uses of types +- that escape. */ +- for (unsigned j = 0; j < function->globals.length ();) +- { +- if (function->globals[j]->type->has_escaped ()) +- function->globals.ordered_remove (j); +- else +- j++; +- } +- +- /* Prune variables that the function uses of types that escape. */ +- for (unsigned j = 0; j < function->decls.length ();) +- { +- srdecl *decl = function->decls[j]; +- if (decl->type->has_escaped ()) +- { +- function->decls.ordered_remove (j); +- delete decl; +- } +- else +- j++; +- } ++ prune_function (function); + + /* Prune functions which don't refer to any variables any more. */ + if (function->args.is_empty () +@@ -5981,19 +6601,7 @@ ipa_struct_reorg::prune_escaped_types (void) + i++; + } + +- /* Prune globals of types that escape, all references to those decls +- will have been removed in the first loop. */ +- for (unsigned j = 0; j < globals.decls.length ();) +- { +- srdecl *decl = globals.decls[j]; +- if (decl->type->has_escaped ()) +- { +- globals.decls.ordered_remove (j); +- delete decl; +- } +- else +- j++; +- } ++ prune_globals (); + + /* Prune types that escape, all references to those types + will have been removed in the above loops. */ +@@ -6026,6 +6634,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 +@@ -6242,7 +6906,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; + +@@ -6254,10 +6917,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; +@@ -6389,6 +7049,7 @@ ipa_struct_reorg::rewrite_expr (tree expr, + } + return true; + } ++ cur_srfd = f; + + tree newdecl = newbase[f->clusternum]; + for (unsigned i = 0; i < max_split && f->newfield[i]; i++) +@@ -6959,8 +7620,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)) +@@ -7695,10 +8354,13 @@ ipa_struct_reorg::rewrite_assign (gassign *stmt, gimple_stmt_iterator *gsi) + + if (!rewrite_lhs_rhs (lhs, rhs1, newlhs, newrhs)) + return false; +- tree size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (lhs))); +- tree num; ++ tree struct_type = TREE_TYPE (TREE_TYPE (lhs)); ++ tree size = TYPE_SIZE_UNIT (struct_type); ++ tree num = NULL_TREE; + /* Check if rhs2 is a multiplication of the size of the type. */ +- if (!is_result_of_mult (rhs2, &num, size) ++ if ((current_fc_level != fc_level::DYNAMIC ++ || !POINTER_TYPE_P (struct_type)) ++ && !is_result_of_mult (rhs2, &num, size) + && !(current_layout_opt_level & SEMI_RELAYOUT)) + internal_error ( + "The rhs of pointer is not a multiplicate and it slips through"); +@@ -7836,6 +8498,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)) +@@ -7853,6 +8516,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; +@@ -7862,7 +8528,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)) + { +@@ -7997,6 +8666,9 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) + if (!decl || !decl->type) + return false; + srtype *type = decl->type; ++ if (type->has_escaped () || !type->has_new_type ()) ++ return false; ++ + tree num = allocate_size (type, decl, stmt); + gcc_assert (num); + memset (newrhs1, 0, sizeof (newrhs1)); +@@ -8114,9 +8786,9 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) + { + if (t && t->semi_relayout) + newexpr[0] = get_real_allocated_ptr (newexpr[0], gsi); +- gimple_call_set_arg (stmt, 0, newexpr[0]); +- update_stmt (stmt); +- return false; ++ gimple_call_set_arg (stmt, 0, newexpr[0]); ++ update_stmt (stmt); ++ return false; + } + + for (unsigned i = 0; i < max_split && newexpr[i]; i++) +@@ -8142,6 +8814,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); +@@ -8160,9 +8833,35 @@ ipa_struct_reorg::rewrite_call (gcall *stmt, gimple_stmt_iterator *gsi) + + /* Did not find the function or had not cloned it return saying don't + change the function call. */ +- if (!f || !f->newf) ++ if (!f) + return false; + ++ if (current_fc_level == fc_level::DYNAMIC) ++ { ++ if (f->partial_clone_p ()) ++ return false; ++ f = f->fc_path.cloned_func; ++ } ++ else ++ { ++ if (!f->newf) ++ return false; ++ /* Move over to the new function. */ ++ f = f->newf; ++ } ++ ++ if (current_fc_level == fc_level::DYNAMIC && f->is_safe_func) ++ { ++ tree expr = gimple_call_arg (stmt, 0); ++ tree newexpr[max_split] = {NULL_TREE}; ++ if (rewrite_expr (expr, newexpr) && newexpr[1] == NULL_TREE) ++ gimple_call_set_arg (stmt, 0, newexpr[0]); ++ ++ gimple_call_set_fndecl (stmt, f->node->decl); ++ update_stmt (stmt); ++ return false; ++ } ++ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Changing arguments for function call :\n"); +@@ -8170,9 +8869,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); +@@ -8456,9 +9152,8 @@ ipa_struct_reorg::rewrite_functions (void) + + if (flag_ipa_struct_sfc_shadow) + { +- for (unsigned i = 0; i < fc_infos.length (); i++) ++ for (auto *info : fc_infos) + { +- fc_type_info *info = fc_infos[i]; + if (!info || !info->static_fc_p) + continue; + cleanup_shadow_write (info); +@@ -8571,39 +9266,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) +@@ -8659,6 +9322,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) + { +@@ -8919,7 +9615,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: "); +@@ -8948,13 +9644,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; +@@ -8963,6 +9665,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; + } + +@@ -9012,7 +9716,7 @@ ipa_struct_reorg::find_static_fc_fields (fc_type_info *info) + continue; + + /* We have marked these fields as shadow, so skip them. */ +- if (fc_fields_contains (info->static_fc_fields, srf->fielddecl)) ++ if (find_fc_field (info->static_fc_fields, srf->fielddecl)) + continue; + + found_static_compress |= static_compress_p (info, srf->fielddecl); +@@ -9041,16 +9745,21 @@ 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; +- +- compress_to_bitfields (info); ++ gcc_assert (info->static_fc_p ^ info->dynamic_fc_p); + +- 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. */ + +@@ -9101,6 +9810,7 @@ ipa_struct_reorg::find_shadow_fields (fc_type_info *info, + /* Unpair assignment checking. */ + auto &srfields = field_class->srfields; + unsigned original_index = 0; ++ tree init_const = NULL_TREE; + if (shadow_info.unpair_stmt) + { + if (dump_file && (dump_flags & TDF_DETAILS)) +@@ -9117,6 +9827,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]; + +@@ -9128,7 +9851,10 @@ ipa_struct_reorg::find_shadow_fields (fc_type_info *info, + continue; + + fc_field *fc_f = new fc_field (shadow_srf->fielddecl, 1, original_srf); +- info->static_fc_fields.safe_push (fc_f); ++ auto &fc_fields = current_fc_level == fc_level::STATIC ++ ? info->static_fc_fields : info->dynamic_shadow_fields; ++ fc_fields.safe_push (fc_f); ++ fc_f->init_const = init_const; /* Not NULL only in dynamic. */ + + /* Record all shadow stmts to fc_field. */ + unsigned j; +@@ -9289,15 +10015,30 @@ ipa_struct_reorg::write_field_in_fc_class_p (gimple *stmt, + return field; + } + +-fc_field * +-ipa_struct_reorg::fc_fields_contains (auto_vec &fc_fields, +- tree field) ++/* Check if the init_const is a unique constant, which is different from all ++ constant rhs of pair statements. */ ++ ++bool ++ipa_struct_reorg::unique_init_const_p (const fc_shadow_info &shadow_info) + { +- for (auto *fc_f : fc_fields) +- if (fc_f->field == field) +- return fc_f; ++ tree init_const = gimple_assign_rhs1 (shadow_info.unpair_stmt); ++ HOST_WIDE_INT value = tree_to_shwi (init_const); ++ for (auto *stmts : shadow_info.pair_stmts_groups) ++ { ++ /* We have prove all rhs in a group are equal, checking one of them ++ is enough. */ ++ tree rhs = gimple_assign_rhs1 ((*stmts)[0]); ++ if (TREE_CODE (rhs) != INTEGER_CST) ++ continue; + +- return NULL; ++ if (tree_to_shwi (rhs) == value) ++ { ++ FC_DUMP_MSG ("Init const is not unique.\n"); ++ return false; ++ } ++ } ++ ++ return true; + } + + /* Check if the right operands of all assignments are equal. */ +@@ -9363,12 +10104,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) + { +@@ -9376,33 +10111,44 @@ ipa_struct_reorg::fc_global_const_p (tree var, HOST_WIDE_INT &value) + return true; + } + ++ if (visited_vars.contains (var)) ++ return false; ++ visited_vars.add (var); ++ + bool is_const = false; + HOST_WIDE_INT const_value = 0; +- for (auto *access : type->accesses) ++ for (auto *srfn : functions) + { +- SET_CFUN (access->function); +- +- gimple *stmt = access->stmt; +- if (!gimple_assign_single_p (stmt) +- || !operand_equal_p (gimple_assign_lhs (stmt), var)) +- continue; ++ SET_CFUN (srfn); ++ basic_block bb = NULL; ++ FOR_EACH_BB_FN (bb, cfun) ++ { ++ for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); ++ gsi_next (&si)) ++ { ++ gimple *stmt = gsi_stmt (si); ++ if (!gimple_assign_single_p (stmt) ++ || !operand_equal_p (gimple_assign_lhs (stmt), var)) ++ continue; + +- if (!fc_peephole_const_p (gimple_assign_rhs1 (stmt), value)) +- return false; ++ if (!fc_peephole_const_p (gimple_assign_rhs1 (stmt), value)) ++ return false; + +- /* Make sure the value is never changed. */ +- if (is_const) +- { +- if (value != const_value) +- return false; +- continue; +- } ++ /* Make sure the value is never changed. */ ++ if (is_const) ++ { ++ if (value != const_value) ++ return false; ++ continue; ++ } + +- is_const = true; +- const_value = value; ++ is_const = true; ++ const_value = value; + +- /* Record a global constant here. */ +- global_consts.safe_push (new const_map (var, value)); ++ /* Record a global constant here. */ ++ global_consts.safe_push (new const_map (var, value)); ++ } ++ } + } + + return is_const; +@@ -9785,7 +10531,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)) +@@ -9815,8 +10561,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). */ +@@ -9828,7 +10573,7 @@ ipa_struct_reorg::find_hot_access (fc_type_info *info, + continue; + + tree fielddecl = access->field->fielddecl; +- if (!fielddecl || !fc_fields_contains (fc_fields, fielddecl)) ++ if (!fielddecl || !find_fc_field (fc_fields, fielddecl)) + continue; + + auto &set = write_map.get_or_insert (bb); +@@ -9859,8 +10604,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); + } + } +@@ -9882,7 +10627,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); + } + } +@@ -9891,14 +10636,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); +@@ -9909,15 +10656,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)); +@@ -9970,34 +10728,97 @@ ipa_struct_reorg::compress_fields_static (fc_type_info *info) + + /* Compress fields to bitfield, for which bits will be the width. */ + +-void +-ipa_struct_reorg::compress_to_bitfields (fc_type_info *info) ++bool ++ipa_struct_reorg::compress_to_bitfield_static (fc_type_info *info) + { +- /* For static compression. Calculate bitsize for static field. */ +- if (flag_ipa_struct_sfc_bitfield && info->static_fc_p) ++ if (!flag_ipa_struct_sfc_bitfield) ++ return true; ++ ++ for (auto *fc_f : info->static_fc_fields) + { +- for (auto *fc_f : info->static_fc_fields) ++ HOST_WIDE_INT max_value = fc_f->max_value; ++ gcc_assert (max_value > 0 && max_value <= UINT_MAX); ++ ++ /* Calculate bitsize. */ ++ fc_f->bits = 0; ++ while (max_value) + { +- HOST_WIDE_INT max_value = fc_f->max_value; +- gcc_assert (max_value > 0 && max_value <= UINT_MAX); ++ fc_f->bits++; ++ max_value >>= 1; ++ } + +- /* Calculate bitsize. */ +- fc_f->bits = 0; +- while (max_value) +- { +- fc_f->bits++; +- max_value >>= 1; +- } ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Static bitfield: "); ++ print_generic_expr (dump_file, fc_f->field); ++ fprintf (dump_file, ":%d", fc_f->bits); ++ fprintf (dump_file, "\n"); ++ } ++ } + +- if (dump_file && (dump_flags & TDF_DETAILS)) +- { +- FC_DUMP_MSG ("Bitfield: "); +- print_generic_expr (dump_file, fc_f->field); +- fprintf (dump_file, ":%d", fc_f->bits); +- fprintf (dump_file, "\n"); +- } ++ return true; ++} ++ ++/* Compress fields to bitfield for dynamic field compression. */ ++ ++bool ++ipa_struct_reorg::compress_to_bitfield_dynamic (fc_type_info *info) ++{ ++ if (!flag_ipa_struct_dfc_bitfield) ++ return true; ++ ++ calc_fc_ref_count (info); ++ ++ /* Collect existing bitfields. */ ++ unsigned total_static_bits = 0; ++ for (auto *srf : info->type->fields) ++ { ++ tree field = srf->fielddecl; ++ if (DECL_BIT_FIELD (field)) ++ total_static_bits += tree_to_uhwi (DECL_SIZE (field)); ++ } ++ ++ unsigned max_ref_cnt = 0; ++ fc_field *max_f = NULL; ++ fc_cond *max_cond = NULL; ++ for (auto *cond : info->fc_conds) ++ { ++ /* Heuristically, only try bit field for big data size. */ ++ if (TYPE_MAIN_VARIANT (cond->old_type) != long_integer_type_node) ++ continue; ++ ++ /* Find the hottest field. */ ++ for (auto *fc_f : cond->fields) ++ { ++ if (fc_f->ref_cnt <= max_ref_cnt) ++ continue; ++ ++ max_ref_cnt = fc_f->ref_cnt; ++ max_f = fc_f; ++ max_cond = cond; + } + } ++ ++ /* Choose the hottest candidate to try bitfield. */ ++ unsigned new_type_bits = TYPE_PRECISION (max_f->new_type); ++ if (new_type_bits <= total_static_bits) ++ return false; ++ ++ /* The fc condition covering this field is marked as bitfield, ++ although not all of the fields for this condition are marked as ++ bitfield. */ ++ max_f->bits = new_type_bits - total_static_bits; ++ max_cond->bits = max_f->bits; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Dynamic bitfield: "); ++ print_generic_expr (dump_file, max_f->field); ++ fprintf (dump_file, ":%d", max_f->bits); ++ fprintf (dump_file, "\n"); ++ } ++ ++ return true; + } + + /* Collect all blocks that can reach stmt. */ +@@ -10031,127 +10852,2930 @@ ipa_struct_reorg::collect_all_predecessor (gimple *stmt) + return blocks; + } + +-/* Init pointer size from parameter param_pointer_compression_size. */ +- +-static void +-init_pointer_size_for_pointer_compression (void) ++bool ++ipa_struct_reorg::types_fc_equal_p (tree type1, tree type2) + { +- switch (param_pointer_compression_size) ++ if (type1 == type2) ++ return true; ++ if (TREE_CODE (type1) != TREE_CODE (type2)) ++ return false; ++ ++ const char *tname1; ++ const char *tname2; ++ size_t len1; ++ size_t len2; ++ const char *p; ++ ++ switch (TREE_CODE (type1)) + { +- case 8: +- // FALLTHRU +- case 16: +- // FALLTHRU +- case 32: compressed_size = param_pointer_compression_size; break; ++ case POINTER_TYPE: ++ return types_fc_equal_p (inner_type (type1), inner_type (type2)); ++ ++ case RECORD_TYPE: ++ tname1 = get_type_name (type1); ++ tname2 = get_type_name (type2); ++ if (!tname1 || !tname2) ++ return false; ++ ++ len1 = strlen (tname1); ++ len2 = strlen (tname2); ++ if (len1 > len2) ++ { ++ std::swap (len1, len2); ++ std::swap (tname1, tname2); ++ } ++ ++ p = strstr (tname2, tname1); ++ if (!p) ++ return false; ++ p += len1; ++ ++ /* As suffixes with '.' are generated by compiler, should be safe to ++ skip the rest of p. */ ++ return STRING_STARTS_WITH (p, ".reorg"); ++ + default: +- error ("Invalid pointer compression size, using the following param: " +- "\"--param compressed-pointer-size=[8,16,32]\""); ++ return false; + } + } + +-unsigned int +-ipa_struct_reorg::execute (unsigned int opt) ++bool ++ipa_struct_reorg::types_fc_compatible_p (tree type1, tree type2) + { +- unsigned int ret = 0; ++ return types_compatible_p (type1, type2) || types_fc_equal_p (type1, type2); ++} + +- if (dump_file) +- fprintf (dump_file, "\n\n====== ipa_struct_reorg level %d ======\n\n", +- opt); ++/* Scan all of fields to check whether each can be dynamically ++ compressed or not. */ + +- if (opt != COMPLETE_STRUCT_RELAYOUT) ++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 (current_layout_opt_level == STRUCT_SPLIT) +- analyze_types (); ++ FC_DUMP_MSG ("Fail finding fields in input stmt\n"); ++ return false; ++ } + +- 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 (); ++ if (!find_fopen_fclose (info)) ++ { ++ FC_DUMP_MSG ("Fail finding fopen/fclose stmt\n"); ++ return false; + } +- else ++ ++ /* Avoid compressing fields without hot access. */ ++ if (!find_hot_access (info, info->dynamic_fc_fields)) + { +- 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 (); ++ FC_DUMP_MSG ("Fail finding hot access for dynamic\n"); ++ return false; ++ } + +- ret = execute_struct_relayout (); ++ if (!check_dynamic_shadow_fields (info)) ++ { ++ FC_DUMP_MSG ("Fail checking dynamic shadow fields\n"); ++ return false; + } + +- return ret; ++ 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; + } + +-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 +-}; ++/* 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. */ + +-class pass_ipa_struct_reorg : public simple_ipa_opt_pass ++bool ++ipa_struct_reorg::find_fields_in_input_stmt (fc_type_info *info) + { +-public: +- pass_ipa_struct_reorg (gcc::context *ctxt) +- : simple_ipa_opt_pass (pass_data_ipa_struct_reorg, ctxt) +- {} ++ basic_block input_bb = NULL; + +- /* opt_pass methods: */ +- virtual bool gate (function *); +- virtual unsigned int execute (function *) +- { +- unsigned int ret = 0; +- unsigned int ret_reorg = 0; +- unsigned int level = 0; +- switch (struct_layout_optimize_level) +- { +- case 6: level |= SEMI_RELAYOUT; +- // FALLTHRU +- case 5: level |= POINTER_COMPRESSION_UNSAFE; +- // FALLTHRU +- case 4: level |= POINTER_COMPRESSION_SAFE; +- // FALLTHRU +- case 3: level |= DEAD_FIELD_ELIMINATION; +- // FALLTHRU +- case 2: level |= STRUCT_REORDER_FIELDS; +- // FALLTHRU +- case 1: +- level |= COMPLETE_STRUCT_RELAYOUT; +- level |= STRUCT_SPLIT; +- break; +- case 0: break; +- default: gcc_unreachable (); +- } ++ for (auto *access : info->type->accesses) ++ { ++ if (!access->write_field_p ()) ++ continue; + +- if (level & POINTER_COMPRESSION_SAFE) +- init_pointer_size_for_pointer_compression (); ++ srfield *field = access->field; ++ if (find_fc_field (info->dynamic_shadow_fields, field->fielddecl) ++ || find_fc_field (info->dynamic_fc_fields, field->fielddecl)) ++ continue; + +- if (level & SEMI_RELAYOUT) +- { +- semi_relayout_align = semi_relayout_level; +- relayout_part_size = 1 << semi_relayout_level; +- } ++ /* Skip dead field. */ ++ if (field->dead_field_p ()) ++ continue; + +- /* Preserved for backward compatibility, reorder fields needs run before ++ if (TREE_CODE (field->fieldtype) != INTEGER_TYPE) ++ continue; ++ ++ SET_CFUN (access->function); ++ /* Guarantee this struct field is from a file. */ ++ gimple *input_stmt = NULL; ++ gimple *var_stmt = NULL; ++ if (!find_input_stmt (access->stmt, input_stmt, var_stmt) ++ || gimple_bb (input_stmt)->loop_father->num == 0) ++ continue; ++ ++ tree var = gimple_assign_rhs1 (var_stmt); ++ ++ if (!info->input_stmt) ++ { ++ info->input_stmt = input_stmt; ++ info->input_var = access->base; ++ info->input_file_handler = find_file_handler (input_stmt); ++ if (!info->input_file_handler) ++ return false; ++ } ++ ++ /* Support only one input stmt now. */ ++ if (info->input_stmt != input_stmt ++ || info->input_var != access->base) ++ return false; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Found a dynamic compression field: "); ++ print_generic_expr (dump_file, field->fielddecl); ++ fprintf (dump_file, ", input var: "); ++ print_generic_expr (dump_file, var); ++ fprintf (dump_file, "\n"); ++ } ++ ++ tree in_ssa = gimple_assign_rhs1 (access->stmt); ++ fc_field *fc_f = new fc_field (field->fielddecl, var, in_ssa); ++ info->dynamic_fc_fields.safe_push (fc_f); ++ ++ /* All fc fields should define their ssas in the same block. */ ++ if (!input_bb) ++ input_bb = gimple_bb (SSA_NAME_DEF_STMT (in_ssa)); ++ else if (input_bb != gimple_bb (SSA_NAME_DEF_STMT (in_ssa))) ++ return false; ++ ++ info->start_srfn = access->function; ++ } ++ ++ if (info->dynamic_fc_fields.is_empty ()) ++ return false; ++ ++ /* Sort all fields in the order of input ssa position. This is required ++ to simplify the min_val and max_val calculation. */ ++ SET_CFUN (info->start_srfn); ++ renumber_gimple_stmt_uids_in_blocks (&input_bb, 1); ++ info->dynamic_fc_fields.qsort (input_order_cmp); ++ ++ return true; ++} ++ ++/* Find the input stmt for the rhs of the given stmt. ++ Now we only support sscanf. */ ++ ++bool ++ipa_struct_reorg::find_input_stmt (gimple *stmt, gimple *&input_stmt, ++ gimple *&var_stmt) ++{ ++ /* Check pattern fc_type->field = _ssa_name. */ ++ if (!gimple_assign_single_p (stmt) ++ || TREE_CODE (gimple_assign_rhs1 (stmt)) != SSA_NAME) ++ return false; ++ ++ stmt = strip_copy_stmts (SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt))); ++ /* Check pattern _ssa_name = var. */ ++ if (!gimple_assign_single_p (stmt) ++ || !VAR_P (gimple_assign_rhs1 (stmt))) ++ return false; ++ ++ var_stmt = stmt; ++ tree var = gimple_assign_rhs1 (stmt); ++ ++ /* Search backward to find a sscanf stmt. */ ++ while (gimple_bb (stmt)) ++ { ++ tree vuse = gimple_vuse (stmt); ++ if (!vuse) ++ break; ++ ++ stmt = SSA_NAME_DEF_STMT (vuse); ++ if (!gimple_call_builtin_p (stmt, BUILT_IN_SSCANF)) ++ continue; ++ ++ /* Search '&var' from the 3th arguments. */ ++ for (unsigned i = 2; i < gimple_call_num_args (stmt); i++) ++ { ++ tree arg = gimple_call_arg (stmt, i); ++ if (TREE_CODE (arg) != ADDR_EXPR ++ || TREE_OPERAND (arg, 0) != var) ++ continue; ++ ++ input_stmt = stmt; ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Found input stmt: "); ++ print_gimple_stmt (dump_file, stmt, 0); ++ } ++ return true; ++ } ++ ++ /* The sscanf stmt doesn't contain 'var', so the check failed. */ ++ break; ++ } ++ ++ return false; ++} ++ ++/* Find the file handler, which holds the file from which the given stmt ++ read data. ++ Now only support sscanf. */ ++ ++tree ++ipa_struct_reorg::find_file_handler (gimple *input_stmt) ++{ ++ if (!gimple_call_builtin_p (input_stmt, BUILT_IN_SSCANF)) ++ return NULL_TREE; ++ ++ /* Find fgets stmt. */ ++ gimple *stmt = SSA_NAME_DEF_STMT (gimple_vuse (input_stmt)); ++ if (gimple_code (stmt) != GIMPLE_CALL) ++ return NULL_TREE; ++ ++ tree callee = gimple_call_fn (stmt); ++ if (callee && TREE_CODE (callee) == OBJ_TYPE_REF) ++ return NULL_TREE; ++ ++ callee = gimple_call_fndecl (stmt); ++ const char *fn_name = get_func_name (callee); ++ if (!fn_name || strcmp (fn_name, "fgets") != 0) ++ return NULL_TREE; ++ ++ /* Check fget is using the string for sscanf. */ ++ tree fget_arg0 = gimple_call_arg (stmt, 0); ++ tree sscanf_arg0 = gimple_call_arg (input_stmt, 0); ++ if (TREE_OPERAND (fget_arg0, 0) != TREE_OPERAND (sscanf_arg0, 0)) ++ return NULL_TREE; ++ ++ return gimple_call_arg (stmt, 2); ++} ++ ++/* Find fclose in start function. */ ++ ++bool ++ipa_struct_reorg::find_fopen_fclose (fc_type_info *info) ++{ ++ SET_CFUN (info->start_srfn); ++ ++ basic_block bb; ++ FOR_EACH_BB_FN (bb, cfun) ++ { ++ for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) ++ { ++ gimple *stmt = gsi_stmt (si); ++ if (!is_gimple_call (stmt)) ++ continue; ++ ++ tree decl = gimple_call_fndecl (stmt); ++ const char *callee = get_func_name (decl); ++ if (!callee || strcmp (callee, "fclose") != 0) ++ continue; ++ ++ /* The fclose must use the same file handler as the fget, ++ which reads data for sscanf. */ ++ tree fh = gimple_call_arg (stmt, 0); ++ if (fh != info->input_file_handler) ++ continue; ++ ++ info->fclose_stmt = stmt; ++ renumber_gimple_stmt_uids_in_blocks (&bb, 1); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "\nFound fclose in function %s:\n", ++ get_func_name (info->start_srfn->node->decl)); ++ print_gimple_stmt (dump_file, stmt, 0); ++ } ++ ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++/* Check whether the original of a dynamic shadow field is one of ++ dynamic_fc_fields. */ ++ ++bool ++ipa_struct_reorg::check_dynamic_shadow_fields (fc_type_info *info) ++{ ++ for (auto *shadow_field : info->dynamic_shadow_fields) ++ { ++ srfield *original = shadow_field->original; ++ fc_field *input_field = find_fc_field (info->dynamic_fc_fields, ++ original->fielddecl); ++ if (!input_field) ++ return false; ++ ++ shadow_field->input_field = input_field; ++ shadow_field->input_ssa = input_field->input_ssa; ++ } ++ ++ return true; ++} ++ ++bool ++ipa_struct_reorg::find_fc_paths (fc_type_info *info) ++{ ++ /* Start point function. */ ++ srfunction *srfn = info->start_srfn; ++ gimple *start_stmt = info->fclose_stmt; ++ ++ while (srfn) ++ { ++ /* Already seen. */ ++ if (srfn->fc_path.start_stmt) ++ return false; ++ ++ SET_CFUN (srfn); ++ ++ srfn->fc_path.start_stmt = start_stmt; ++ if (!srfn->fc_path.collect_blocks (start_stmt, fc_path_info::PRED) ++ || !srfn->fc_path.collect_blocks (start_stmt, fc_path_info::SUCC)) ++ return false; ++ ++ /* Start at the entry function. */ ++ if (srfn->entry_function_p ()) ++ return true; ++ ++ /* The current function should only be called once. */ ++ cgraph_edge *edge = srfn->node->callers; ++ if (!edge || edge->next_caller || !edge->call_stmt) ++ return false; ++ ++ srfn = find_function (edge->caller); ++ start_stmt = edge->call_stmt; ++ } ++ ++ return false; ++} ++ ++bool ++ipa_struct_reorg::find_fc_data (fc_type_info *info) ++{ ++ if (!find_fc_arrays (info)) ++ { ++ FC_DUMP_MSG ("Fail finding fc arrays\n"); ++ return false; ++ } ++ ++ if (!check_fc_array_uses (info)) ++ { ++ FC_DUMP_MSG ("Fail checking fc array uses\n"); ++ return false; ++ } ++ ++ if (!find_fc_refs (info)) ++ { ++ FC_DUMP_MSG ("Fail finding fc refs\n"); ++ return false; ++ } ++ ++ return true; ++} ++ ++/* Find all arrays to be cached: ++ 1. Defined by malloc/calloc (before start-point) ++ 2. Will be used after fclose (e.g. global variables) */ ++ ++bool ++ipa_struct_reorg::find_fc_arrays (fc_type_info *info) ++{ ++ varpool_node *vnode; ++ tree type = info->type->type; ++ ++ /* 1) Process all global vars, search for arrays of cached objects. */ ++ FOR_EACH_VARIABLE (vnode) ++ { ++ tree node = vnode->decl; ++ tree node_type = TREE_TYPE (node); ++ /* Global object is not supported. */ ++ if (types_fc_compatible_p (node_type, type)) ++ return false; ++ ++ switch (TREE_CODE (node_type)) ++ { ++ /* POINTER->RECORD(fc_type) */ ++ case POINTER_TYPE: ++ if (types_fc_compatible_p (TREE_TYPE (node_type), type) ++ && !find_fc_array (info, node, vnode)) ++ return false; ++ break; ++ /* RECORD->POINTER->RECORD(fc_type) */ ++ case RECORD_TYPE: ++ for (tree t = TYPE_FIELDS (node_type); t; t = DECL_CHAIN (t)) ++ { ++ if (TREE_CODE (t) != FIELD_DECL) ++ continue; ++ ++ tree field_type = TREE_TYPE (t); ++ if (TREE_CODE (field_type) == RECORD_TYPE) ++ { ++ FC_DUMP_MSG ("RECORD->RECORD->... not supported\n"); ++ return false; ++ } ++ ++ if (POINTER_TYPE_P (field_type) ++ && types_fc_compatible_p (TREE_TYPE (field_type), type)) ++ { ++ tree field_node = build3 (COMPONENT_REF, field_type, node, ++ t, NULL_TREE); ++ if (!find_fc_array (info, field_node, vnode)) ++ return false; ++ } ++ /* More safe: trace-back following VDEF/VUSE. */ ++ } ++ break; ++ case ARRAY_TYPE: ++ case UNION_TYPE: ++ case QUAL_UNION_TYPE: ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("node_type not handled: "); ++ print_generic_expr (dump_file, node_type); ++ fprintf (dump_file, "\n"); ++ } ++ return false; ++ default: ++ break; ++ } ++ } ++ ++ return !info->fc_arrays.is_empty (); ++} ++ ++/* Check if node is a array to be cached, if so, record it. ++ vnode contains referrring to node. */ ++ ++bool ++ipa_struct_reorg::find_fc_array (fc_type_info *info, tree node, ++ varpool_node *vnode) ++{ ++ tree size_expr = NULL_TREE; ++ tree ssa_def = NULL_TREE; ++ ++ ipa_ref *ref = NULL; ++ for (unsigned i = 0; vnode->iterate_referring (i, ref); i++) ++ { ++ /* Filter for writes to NODE. */ ++ if (ref->use != IPA_REF_STORE) ++ continue; ++ /* Ignore assignments after start-point. */ ++ if (!is_stmt_before_fclose (info, ref->stmt, ref->referring)) ++ continue; ++ tree lhs = gimple_get_lhs (ref->stmt); ++ if (!operand_equal_p (lhs, node, COMPARE_DECL_FLAGS)) ++ continue; ++ ++ tree new_size_expr = NULL_TREE; ++ if (!get_allocate_size_iterate (info->type->type, ref->stmt, ++ new_size_expr, &ssa_def)) ++ return false; ++ ++ if (new_size_expr) ++ { ++ if (size_expr) ++ { ++ FC_DUMP_MSG ("fc_array allocated twice before start-point\n"); ++ return false; ++ } ++ size_expr = new_size_expr; ++ ++ /* Allocation must happen at start function. */ ++ if (ref->referring != info->start_srfn->node) ++ return false; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Add array: "); ++ print_generic_expr (dump_file, node); ++ fprintf (dump_file, ", size: "); ++ print_generic_expr (dump_file, size_expr); ++ fprintf (dump_file, ", ssa_def: "); ++ print_generic_expr (dump_file, ssa_def); ++ fprintf (dump_file, "\n"); ++ } ++ } ++ } ++ ++ if (size_expr) ++ { ++ if (duplicative_array_p (info, ssa_def)) ++ return false; ++ ++ fc_array *array = new fc_array (node, size_expr, ssa_def, vnode); ++ info->fc_arrays.safe_push (array); ++ } ++ ++ return true; ++} ++ ++/* Give the SSA_DEF of a array, check if it's duplicative. */ ++ ++bool ++ipa_struct_reorg::duplicative_array_p (fc_type_info *info, tree ssa_def) ++{ ++ for (auto *array : info->fc_arrays) ++ { ++ if (array->ssa_def != ssa_def) ++ continue; ++ ++ FC_DUMP_MSG ("Array assigned to multiple variable\n"); ++ return true; ++ } ++ ++ return false; ++} ++ ++bool ++ipa_struct_reorg::is_stmt_before_fclose (fc_type_info *info, gimple *stmt, ++ symtab_node *node) ++{ ++ gcc_assert (info->fclose_stmt); ++ srfunction *f = find_function (as_a (node)); ++ gcc_assert (f); ++ ++ if (gimple_bb (stmt) == gimple_bb (info->fclose_stmt)) ++ return gimple_uid (stmt) < gimple_uid (info->fclose_stmt); ++ ++ /* If array allocations are outside start-point's function, we may need to ++ create global vars to record the sizes. */ ++ return f->fc_path.pre_bbs.contains (gimple_bb (stmt)); ++} ++ ++/* Check if the VAR is a global pointer created by reorg. */ ++ ++bool ++ipa_struct_reorg::reorg_ptr_p (tree var) ++{ ++ if (TREE_CODE (var) != VAR_DECL) ++ return false; ++ ++ const char *decl_name = IDENTIFIER_POINTER (DECL_NAME (var)); ++ if (!decl_name) ++ return false; ++ ++ const char *reorg_name = strstr (decl_name, ".reorg"); ++ if (!reorg_name) ++ return false; ++ ++ return strstr (reorg_name, "_gptr"); ++} ++ ++/* Return number of objects of TYPE following define chain from STMT. ++ If the number is not certain, set ERROR so we can abort field compression. ++ If SSA_DEF is not NULL, the ssa_name of allocated ptr will be assigned to it. ++ */ ++ ++bool ++ipa_struct_reorg::get_allocate_size_iterate (tree type, gimple *stmt, ++ tree &size, tree *ssa_def) ++{ ++ if (!stmt) ++ return false; ++ ++ switch (gimple_code (stmt)) ++ { ++ case GIMPLE_ASSIGN: ++ return get_allocate_size_assign (type, as_a (stmt), ++ size, ssa_def); ++ case GIMPLE_CALL: ++ return get_allocate_size_call (type, as_a (stmt), ++ size, ssa_def); ++ default: ++ return false; ++ } ++} ++ ++bool ++ipa_struct_reorg::get_allocate_size_assign (tree type, gassign *stmt, ++ tree &size, tree *ssa_def) ++{ ++ tree rhs = gimple_assign_rhs1 (stmt); ++ if ((!gimple_assign_single_p (stmt) && !gimple_assign_cast_p (stmt)) ++ || TREE_CODE (rhs) != SSA_NAME) ++ return true; ++ ++ gimple *def_stmt = SSA_NAME_DEF_STMT (rhs); ++ /* Handle the global arrays split by struct_reorg. */ ++ if (reorg_ptr_p (gimple_assign_lhs (stmt))) ++ return get_allocate_size_reorg_ptr (def_stmt, size); ++ ++ return get_allocate_size_iterate (type, def_stmt, size, ssa_def); ++} ++ ++bool ++ipa_struct_reorg::get_allocate_size_call (tree type, gcall *stmt, ++ tree &size, tree *ssa_def) ++{ ++ tree lhs = gimple_call_lhs (stmt); ++ gcc_assert (TREE_CODE (lhs) == SSA_NAME); ++ if (ssa_def) ++ *ssa_def = lhs; ++ ++ size = get_allocate_size (type, lhs, NULL_TREE, stmt); ++ ++ return size != NULL_TREE; ++} ++ ++/* Handle the global arrays split by struct_reorg: ++ 1) The new array ptrs are marked with suffix "_gptr". ++ 2) The array ptr are calculated with form: ++ _gptr0 = calloc (NUM, size_all); ++ _gptr1 = _gptr0 + NUM * sizeof (TREE_TYPE (_gptr0)); ++ _gptr2 = _gptr1 + NUM * sizeof (TREE_TYPE (_gptr1)); ++ ... ++ */ ++ ++bool ++ipa_struct_reorg::get_allocate_size_reorg_ptr (gimple *plus_stmt, tree &size) ++{ ++ /* Check the POINTER_PLUS_EXPR. */ ++ if (!is_gimple_assign (plus_stmt) ++ || gimple_assign_rhs_code (plus_stmt) != POINTER_PLUS_EXPR) ++ return false; ++ ++ tree rhs1 = gimple_assign_rhs1 (plus_stmt); ++ tree rhs2 = gimple_assign_rhs2 (plus_stmt); ++ tree prev_type = TREE_TYPE (rhs1); ++ ++ /* Check the MULT_EXPR. */ ++ gcc_assert (TREE_CODE (rhs2) == SSA_NAME); ++ gimple *mul_stmt = SSA_NAME_DEF_STMT (rhs2); ++ if (!is_gimple_assign (mul_stmt) ++ || gimple_assign_rhs_code (mul_stmt) != MULT_EXPR) ++ return false; ++ ++ tree num = gimple_assign_rhs1 (mul_stmt); ++ tree mul_by = gimple_assign_rhs2 (mul_stmt); ++ if (TREE_CODE (mul_by) == SSA_NAME) ++ std::swap (num, mul_by); ++ ++ if (TREE_CODE (num) != SSA_NAME || TREE_CODE (mul_by) != INTEGER_CST ++ || !operand_equal_p (mul_by, TYPE_SIZE_UNIT (prev_type))) ++ return false; ++ ++ /* We can trace to original calloc/malloc to make this safer. */ ++ ++ size = num; ++ ++ return true; ++} ++ ++/* Returns the allocated size / T size for STMT. That is the number of ++ elements in the array allocated. */ ++ ++tree ++ipa_struct_reorg::get_allocate_size (tree type, tree decl, tree orig_type, ++ gimple *stmt) ++{ ++ if (!stmt ++ || gimple_code (stmt) != GIMPLE_CALL ++ || !handled_allocation_stmt (stmt)) ++ { ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "\nNot an allocate statement:\n"); ++ print_gimple_stmt (dump_file, stmt, 0); ++ fprintf (dump_file, "\n"); ++ } ++ return NULL_TREE; ++ } ++ ++ tree struct_size = TYPE_SIZE_UNIT (type); ++ ++ /* Specify the correct size to relax multi-layer pointer. */ ++ if (TREE_CODE (decl) == SSA_NAME && orig_type && isptrptr (orig_type)) ++ struct_size = TYPE_SIZE_UNIT (orig_type); ++ ++ tree size = gimple_call_arg (stmt, 0); ++ ++ if (gimple_call_builtin_p (stmt, BUILT_IN_REALLOC) ++ || gimple_call_builtin_p (stmt, BUILT_IN_ALIGNED_ALLOC)) ++ size = gimple_call_arg (stmt, 1); ++ else if (gimple_call_builtin_p (stmt, BUILT_IN_CALLOC)) ++ { ++ tree arg1; ++ arg1 = gimple_call_arg (stmt, 1); ++ /* Check that second argument is a constant equal to ++ the size of structure. */ ++ if (operand_equal_p (arg1, struct_size, 0)) ++ return size; ++ /* ??? Check that first argument is a constant ++ equal to the size of structure. */ ++ /* If the allocated number is equal to the value of struct_size, ++ the value of arg1 is changed to the allocated number. */ ++ if (operand_equal_p (size, struct_size, 0)) ++ return arg1; ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "\ncalloc the correct size:\n"); ++ print_gimple_stmt (dump_file, stmt, 0); ++ fprintf (dump_file, "\n"); ++ } ++ return NULL_TREE; ++ } ++ ++ tree num; ++ if (!is_result_of_mult (size, &num, struct_size)) ++ return NULL_TREE; ++ ++ return num; ++} ++ ++/* Find all fc_refs (variables/arrays to be modified according to some ++ fc_array): ++ - Will be used after fclose (e.g. global variables). ++ - Before fclose, value or array content is assigned with references to some ++ recognized fc_array. (If there are multiple fc_array variables referenced ++ by one fc_ref, quit field compression. Because we don't know how to ++ modify it then.) */ ++ ++bool ++ipa_struct_reorg::find_fc_refs (fc_type_info *info) ++{ ++ /* For each fc_array, follow the use chains and search for fc_refs. */ ++ for (auto *array : info->fc_arrays) ++ { ++ gcc_assert (array->ssa_def); ++ SET_CFUN (info->start_srfn); ++ if (!find_fc_refs_iterate (info, array, array->ssa_def, true)) ++ return false; ++ ++ ipa_ref *ref = NULL; ++ for (unsigned i = 0; array->vnode->iterate_referring (i, ref); i++) ++ { ++ /* Filter for memory loads. */ ++ if (ref->use != IPA_REF_LOAD) ++ continue; ++ /* Ignore assignments after start-point. */ ++ if (!is_stmt_before_fclose (info, ref->stmt, ref->referring)) ++ continue; ++ if (!gimple_assign_single_p (ref->stmt)) ++ return false; ++ tree rhs = gimple_assign_rhs1 (ref->stmt); ++ if (!operand_equal_p (rhs, array->var, COMPARE_DECL_FLAGS)) ++ continue; ++ ++ SET_CFUN (find_function (as_a (ref->referring))); ++ tree lhs = gimple_assign_lhs (ref->stmt); ++ if (!find_fc_refs_iterate (info, array, lhs, true)) ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* Given a fc_array ARRAY and a variable VAR referring to ARRAY, ++ find fc_refs iteratively follow the use chain of VAR. */ ++ ++bool ++ipa_struct_reorg::find_fc_refs_iterate (fc_type_info *info, fc_array *array, ++ tree var, bool loop_back) ++{ ++ switch (TREE_CODE (var)) ++ { ++ /* 1) For SSA_NAME, iterate through use chain. */ ++ case SSA_NAME: ++ return find_fc_refs_ssa_name (info, array, var, loop_back); ++ ++ /* 2) For VAR_DECL, submit a fc_ref. */ ++ case VAR_DECL: ++ return add_fc_ref (info, array, var, NULL_TREE); ++ ++ /* 3) For MEM_REF, find fc_ref following base's def chain. */ ++ case MEM_REF: ++ return find_fc_refs_mem_ref (info, array, var); ++ ++ case COMPONENT_REF: ++ return find_fc_refs_component_ref (info, array, var); ++ ++ default: ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Unknown use kind, code: %s, var: ", ++ get_tree_code_name (TREE_CODE (var))); ++ print_generic_expr (dump_file, var); ++ fprintf (dump_file, "\n"); ++ } ++ return false; ++ } ++} ++ ++/* Find all fc_refs of a SSA_NAME var through its use chain. */ ++ ++bool ++ipa_struct_reorg::find_fc_refs_ssa_name (fc_type_info *info, fc_array *array, ++ tree var, bool loop_back) ++{ ++ use_operand_p use_p; ++ imm_use_iterator iter; ++ FOR_EACH_IMM_USE_FAST (use_p, iter, var) ++ { ++ gimple *stmt = USE_STMT (use_p); ++ cgraph_node *cnode = current_function->node; ++ if (!is_stmt_before_fclose (info, stmt, cnode)) ++ { ++ gimple *def_stmt = SSA_NAME_DEF_STMT (var); ++ if (!is_stmt_before_fclose (info, def_stmt, cnode)) ++ continue; ++ ++ /* If a local ptr of compressed type is defined before start-point ++ and used after start-point, quit field compression. (Otherwise we ++ need to clone and version the ptr's define statement.) */ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Local usage not handled: "); ++ print_gimple_stmt (dump_file, stmt, 0); ++ fprintf (dump_file, " Defined at: "); ++ print_gimple_stmt (dump_file, def_stmt, 0); ++ } ++ ++ return false; ++ } ++ ++ tree lhs = gimple_get_lhs (stmt); ++ switch (gimple_code (stmt)) ++ { ++ case GIMPLE_ASSIGN: ++ /* Rule out: expr(var) = X. */ ++ if (walk_tree (&lhs, check_for_ssa, var, NULL) ++ || !fc_type_pointer_p (info, lhs)) ++ break; ++ if (!find_fc_refs_iterate (info, array, lhs, loop_back)) ++ return false; ++ break; ++ case GIMPLE_PHI: ++ { ++ /* Check if VAR is from back_edge. */ ++ bool loop_var = false; ++ gphi *phi = as_a (stmt); ++ for (unsigned i = 0; i < gimple_phi_num_args (phi); i++) ++ { ++ if (gimple_phi_arg_def (phi, i) != var) ++ continue; ++ edge e = gimple_phi_arg_edge (phi, i); ++ if (e->flags & EDGE_DFS_BACK) ++ { ++ loop_var = true; ++ break; ++ } ++ } ++ ++ if (!loop_var) ++ { ++ if (!find_fc_refs_iterate (info, array, lhs, loop_back)) ++ return false; ++ } ++ else if (loop_back) ++ { ++ if (!find_fc_refs_iterate (info, array, lhs, false)) ++ return false; ++ } ++ break; ++ } ++ case GIMPLE_DEBUG: ++ case GIMPLE_COND: ++ case GIMPLE_SWITCH: ++ case GIMPLE_NOP: ++ break; ++ default: ++ /* Cannot be sure how fc_array is used, like GIMPLE_CALL? */ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("fc_array usage not handled: "); ++ print_gimple_stmt (dump_file, stmt, 0); ++ } ++ return false; ++ } ++ } ++ return true; ++} ++ ++/* Find all fc_refs of a MEM_REF var through its base's def chain. */ ++ ++bool ++ipa_struct_reorg::find_fc_refs_mem_ref (fc_type_info *info, fc_array *array, ++ tree var) ++{ ++ if (!integer_zerop (TREE_OPERAND (var, 1))) ++ { ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("MEM_REF offset not handled: "); ++ print_generic_expr (dump_file, var); ++ fprintf (dump_file, "\n"); ++ } ++ return false; ++ } ++ ++ if (!fc_type_pointer_p (info, var)) ++ { ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Type not compatible: "); ++ print_generic_expr (dump_file, TREE_TYPE (var)); ++ fprintf (dump_file, "\n"); ++ } ++ return false; ++ } ++ ++ tree base = TREE_OPERAND (var, 0); ++ tree ref = get_ptr_decl (base); ++ if (!ref) ++ { ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Failed to get array decl from: "); ++ print_generic_expr (dump_file, base); ++ fprintf (dump_file, "\n"); ++ } ++ return false; ++ } ++ ++ return add_fc_ref (info, array, ref, NULL_TREE); ++} ++ ++/* Find fc_refs of a COMPONENT_REF var. */ ++ ++bool ++ipa_struct_reorg::find_fc_refs_component_ref (fc_type_info *info, ++ fc_array *array, tree var) ++{ ++ tree base = TREE_OPERAND (var, 0); ++ ++ if (TREE_CODE (base) == VAR_DECL) ++ return add_fc_ref (info, array, var, NULL_TREE); ++ else if (TREE_CODE (base) == MEM_REF) ++ base = TREE_OPERAND (base, 0); ++ ++ tree ref = get_ptr_decl (base); ++ if (!ref) ++ { ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Failed to get array decl from: "); ++ print_generic_expr (dump_file, base); ++ fprintf (dump_file, "\n"); ++ } ++ return false; ++ } ++ ++ tree field = TREE_OPERAND (var, 1); ++ return add_fc_ref (info, array, ref, field); ++} ++ ++/* Return the top level fc_type pointer tree node. */ ++ ++bool ++ipa_struct_reorg::fc_type_pointer_p (fc_type_info *info, tree t) ++{ ++ tree type = TREE_TYPE (t); ++ ++ return POINTER_TYPE_P (type) ++ && types_fc_compatible_p (TREE_TYPE (type), info->type->type); ++} ++ ++/* Add VAR as a fc_ref entry into INFO. ++ 1) VAR is a single pointer: fc_ref::size = NULL, fc_ref::field = NULL ++ 2) VAR is an array of pointers: fc_ref::size is the size of array, ++ fc_ref::field = NULL ++ 3) VAR is an array of records(e.g. struct {fc_type *p;}): ++ fc_ref::size is the size of array, fc_ref::field is p ++ */ ++ ++bool ++ipa_struct_reorg::add_fc_ref (fc_type_info *info, fc_array *array, tree var, ++ tree field) ++{ ++ /* The way we're searching for fc_refs, fc_array vars will also meet the ++ requirements. Rule out them. */ ++ for (auto *d : info->fc_arrays) ++ if (operand_equal_p (var, d->var, COMPARE_DECL_FLAGS)) ++ return true; ++ ++ tree type = NULL_TREE; ++ tree size_expr = NULL_TREE; ++ ++ /* Rule out duplicants. */ ++ switch (check_duplicative_ref (info, array, var, field, type, size_expr)) ++ { ++ case check_ref_result::NEW: break; ++ case check_ref_result::DUPLICATIVE: return true; ++ case check_ref_result::ERROR: return false; ++ } ++ ++ if (!type) ++ { ++ type = TREE_TYPE (var); ++ /* Use the "real" type for void*. */ ++ if (VOID_POINTER_P (type)) ++ { ++ srdecl *decl = find_decl (var); ++ if (!decl || !decl->orig_type || !POINTER_TYPE_P (decl->orig_type)) ++ return false; ++ type = decl->orig_type; ++ } ++ } ++ ++ /* If REF is an array, get the size it is allocated with. */ ++ if ((!size_expr) && POINTER_TYPE_P (type) ++ && !types_fc_compatible_p (TREE_TYPE (type), info->type->type)) ++ { ++ gimple *stmt = NULL; ++ if (TREE_CODE (var) == SSA_NAME) ++ stmt = SSA_NAME_DEF_STMT (var); ++ else ++ stmt = find_def_stmt_before_fclose (info, var); ++ ++ if (!get_allocate_size_iterate (TREE_TYPE (type), stmt, size_expr)) ++ return false; ++ } ++ ++ fc_ref *ref = new fc_ref (var, type, array, size_expr, field); ++ info->fc_refs.safe_push (ref); ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Add fc_ref: "); ++ ref->dump (dump_file); ++ } ++ ++ return true; ++} ++ ++/* Check if we have found another fc_ref with the same var. */ ++ ++check_ref_result ++ipa_struct_reorg::check_duplicative_ref (fc_type_info *info, fc_array *array, ++ tree var, tree field, ++ tree &type, tree &size_expr) ++{ ++ for (auto *ref : info->fc_refs) ++ { ++ if (!operand_equal_p (var, ref->var, COMPARE_DECL_FLAGS)) ++ continue; ++ ++ /* The var refers to multiple fc_array. */ ++ if (ref->source != array) ++ { ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Variable "); ++ print_generic_expr (dump_file, var); ++ fprintf (dump_file, " referring to multiple arrays: "); ++ print_generic_expr (dump_file, ref->source->var); ++ fprintf (dump_file, " and "); ++ print_generic_expr (dump_file, array->var); ++ fprintf (dump_file, "\n"); ++ } ++ return check_ref_result::ERROR; ++ } ++ ++ if (ref->field) ++ { ++ gcc_assert (field); ++ /* Different fields in an array of structures. */ ++ if (!operand_equal_p (field, ref->field, COMPARE_DECL_FLAGS)) ++ { ++ type = ref->orig_type ? ref->orig_type : TREE_TYPE (var); ++ size_expr = ref->size; ++ continue; ++ } ++ } ++ ++ return check_ref_result::DUPLICATIVE; ++ } ++ ++ return check_ref_result::NEW; ++} ++ ++/* Find the single defination stmt before start-point for a var. */ ++ ++gimple * ++ipa_struct_reorg::find_def_stmt_before_fclose (fc_type_info *info, tree var) ++{ ++ tree base = TREE_CODE (var) == COMPONENT_REF ? TREE_OPERAND (var, 0) : var; ++ if (TREE_CODE (base) != VAR_DECL) ++ return NULL; ++ ++ varpool_node *vnode = varpool_node::get (base); ++ /* Local array is not handled yet. */ ++ if (!vnode) ++ return NULL; ++ ++ gimple *def_stmt = NULL; ++ ipa_ref *ref = NULL; ++ for (unsigned i = 0; vnode->iterate_referring (i, ref); i++) ++ { ++ if (ref->use != IPA_REF_STORE) ++ continue; ++ ++ gimple *stmt = ref->stmt; ++ tree lhs = gimple_get_lhs (stmt); ++ if (!operand_equal_p (lhs, var, COMPARE_DECL_FLAGS) ++ || !is_stmt_before_fclose (info, stmt, ref->referring)) ++ continue; ++ ++ if (gimple_assign_single_p (stmt) ++ && integer_zerop (gimple_assign_rhs1 (stmt))) ++ continue; ++ ++ if (def_stmt) ++ { ++ FC_DUMP_MSG ("Multiple definations before start-point?\n"); ++ return NULL; ++ } ++ ++ def_stmt = stmt; ++ } ++ ++ return def_stmt; ++} ++ ++/* VAR is an ssa_name defined by some array + offset. ++ 1) For global variables, returns declaration of the array. ++ 2) For arrays locally allocated with recogized functions, returns the ++ ssa_name it is assigned with. ++ 3) Return NULL_TREE if cannot decide. */ ++ ++tree ++ipa_struct_reorg::get_ptr_decl (tree var) ++{ ++ if (TREE_CODE (var) != SSA_NAME) ++ return NULL_TREE; ++ ++ gimple *stmt = SSA_NAME_DEF_STMT (var); ++ tree var_type = TREE_TYPE (var); ++ ++ if (gimple_code (stmt) == GIMPLE_ASSIGN) ++ { ++ gassign *assign = as_a (stmt); ++ switch (gimple_assign_rhs_class (assign)) ++ { ++ case GIMPLE_BINARY_RHS: ++ { ++ if (gimple_assign_rhs_code (assign) != POINTER_PLUS_EXPR) ++ return NULL_TREE; ++ tree lhs = gimple_assign_rhs1 (assign); ++ if (types_fc_compatible_p (TREE_TYPE (lhs), var_type) ++ || VOID_POINTER_P (TREE_TYPE (lhs))) ++ return get_ptr_decl (lhs); ++ return NULL_TREE; ++ } ++ ++ case GIMPLE_UNARY_RHS: ++ case GIMPLE_SINGLE_RHS: ++ { ++ tree rhs = gimple_assign_rhs1 (stmt); ++ if (TREE_CODE (rhs) == SSA_NAME) ++ return get_ptr_decl (rhs); ++ else if (TREE_CODE (rhs) == VAR_DECL) ++ return rhs; ++ else if (TREE_CODE (rhs) == COMPONENT_REF) ++ { ++ tree base = TREE_OPERAND (rhs, 0); ++ return DECL_P (base) ? rhs : NULL_TREE; ++ } ++ else ++ return NULL_TREE; ++ } ++ default: ++ return NULL_TREE; ++ } ++ } ++ else if (gimple_code (stmt) == GIMPLE_CALL) ++ return handled_allocation_stmt (stmt) ? gimple_get_lhs (stmt) : NULL_TREE; ++ else ++ return NULL_TREE; ++ ++ /* TODO: GIMPLE_PHI can be supported (not affecting correctness). */ ++} ++ ++/* Search info->input_var backward using def/use chain until finding one of ++ the arrays we have found in find_fc_arrays. */ ++ ++bool ++ipa_struct_reorg::check_fc_array_uses (fc_type_info *info) ++{ ++ hash_set visited; ++ auto_vec worklist; ++ ++ visited.add (info->input_var); ++ worklist.safe_push (info->input_var); ++ ++ while (!worklist.is_empty ()) ++ { ++ tree t = worklist.pop (); ++ tree_code code = TREE_CODE (t); ++ if (code != SSA_NAME && code != VAR_DECL) ++ continue; ++ ++ for (auto *array : info->fc_arrays) ++ if (t == array->ssa_def || t == array->var) ++ return true; ++ ++ /* If we reach a global variable, it must match a fc_array. */ ++ if (code == VAR_DECL) ++ return false; ++ ++ gimple *stmt = SSA_NAME_DEF_STMT (t); ++ if (gimple_code (stmt) == GIMPLE_PHI) ++ { ++ for (unsigned i = 0; i < gimple_phi_num_args (stmt); ++i) ++ { ++ tree arg = gimple_phi_arg_def (stmt, i); ++ if (!visited.add (arg)) ++ worklist.safe_push (arg); ++ } ++ } ++ else if (gimple_assign_single_p (stmt) ++ || gimple_assign_rhs_code_p (stmt, POINTER_PLUS_EXPR)) ++ { ++ tree rhs = gimple_assign_rhs1 (stmt); ++ if (!visited.add (rhs)) ++ worklist.safe_push (rhs); ++ } ++ } ++ ++ return false; ++} ++ ++/* Calculate the reference count of all fc fields. */ ++ ++void ++ipa_struct_reorg::calc_fc_ref_count (fc_type_info *info) ++{ ++ for (auto *access : info->type->accesses) ++ { ++ if (!access->field) ++ continue; ++ ++ fc_field *fc_f = find_fc_field (info->dynamic_fc_fields, ++ access->field->fielddecl); ++ if (fc_f) ++ fc_f->ref_cnt++; ++ } ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Reference count:\n"); ++ for (auto *fc_f : info->dynamic_fc_fields) ++ { ++ print_generic_expr (dump_file, fc_f->field); ++ fprintf (dump_file, " : %d\n", fc_f->ref_cnt); ++ } ++ } ++} ++ ++/* For dynamic fields, we heuristically change data type. */ ++ ++bool ++ipa_struct_reorg::compress_fields_dynamic (fc_type_info *info) ++{ ++ const std::map precision_map{ ++ {64, 16}, {32, 16}, {16, 8} ++ }; ++ ++ for (auto *fc_f : info->dynamic_fc_fields) ++ { ++ tree old_type = TREE_TYPE (fc_f->field); ++ bool is_unsigned = TYPE_UNSIGNED (old_type); ++ gcc_assert (TREE_CODE (old_type) == INTEGER_TYPE); ++ ++ auto iter = precision_map.find (TYPE_PRECISION (old_type)); ++ if (iter == precision_map.cend ()) ++ return false; ++ ++ fc_f->new_type = get_integer_type_node (iter->second, is_unsigned); ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Change the type of "); ++ print_generic_expr (dump_file, fc_f->field); ++ fprintf (dump_file, " from (prec=%d) to ", TYPE_PRECISION (old_type)); ++ print_generic_expr (dump_file, fc_f->new_type); ++ fprintf (dump_file, "(prec=%d)\n", TYPE_PRECISION (fc_f->new_type)); ++ } ++ ++ info->record_cond (fc_f); ++ fc_f->cond->new_type = fc_f->new_type; ++ } ++ ++ return true; ++} ++ ++/* Tune the upper boundary for dynamic fields. The data field may be ++ assigned a constant that is larger than the maximum value. For this ++ case, we can reserve a special value for it, and then we can ++ compress/decompress it by creating a map between this reserved value ++ and the real big value. In the meantime, we will have to reduce the ++ upper boundary for this specific field. */ ++ ++bool ++ipa_struct_reorg::calc_dynamic_boundary (fc_type_info *info) ++{ ++ /* Initialize the low_bound and high_bound. */ ++ for (auto *cond : info->fc_conds) ++ { ++ tree ssa_type = TREE_TYPE (cond->fields[0]->input_ssa); ++ ++ /* Low bound is always zero. */ ++ cond->low_bound = fold_convert (ssa_type, integer_zero_node); ++ ++ /* High bound is the max value of the type. */ ++ unsigned bits = cond->bits ? cond->bits ++ : TYPE_PRECISION (cond->new_type); ++ unsigned max_value = wi::max_value (bits, UNSIGNED).to_uhwi (); ++ cond->high_bound = build_int_cst (ssa_type, max_value); ++ ++ auto_vec special_values; ++ /* Calculate upper bound. */ ++ for (auto *access : info->type->accesses) ++ { ++ gimple *stmt = access->stmt; ++ if (!gimple_assign_single_p (stmt)) ++ continue; ++ ++ /* Skip if it is not an assignment to fc field. */ ++ if (!fc_cond_field_p (gimple_assign_lhs (stmt), cond)) ++ continue; ++ ++ /* Skip if it is loaded from a fc field. */ ++ tree rhs = gimple_assign_rhs1 (stmt); ++ if (fc_field_load_p (rhs, cond)) ++ continue; ++ ++ /* Skip if it is from input_ssa. */ ++ if (fc_input_ssa_p (rhs, cond)) ++ continue; ++ ++ /* Make sure the assignment is a constant. If possible, we ++ try to find all possible costants by peephole. */ ++ HOST_WIDE_INT value; ++ if (fc_peephole_const_p (rhs, value)) ++ { ++ special_values.safe_push (value); ++ cond->field_class->closure.write_special_rhs.put (rhs, value); ++ continue; ++ } ++ } ++ ++ /* Execute multiple rounds cause we didn't sort the special_values. */ ++ while (true) ++ { ++ unsigned size = cond->special_values.length (); ++ for (auto value : special_values) ++ update_high_bound (cond, value); ++ if (size == cond->special_values.length ()) ++ break; ++ } ++ } ++ ++ return true; ++} ++ ++/* Return true if the VAR is a mem reference of fc_field in the fc_cond. */ ++ ++bool ++ipa_struct_reorg::fc_cond_field_p (tree var, const fc_cond *cond) ++{ ++ if (TREE_CODE (var) != COMPONENT_REF) ++ return false; ++ ++ /* Find the stmt assigning to the fc field. */ ++ tree field = TREE_OPERAND (var, 1); ++ return find_fc_field (cond->fields, field); ++} ++ ++/* Return true if var is one of cond's input_ssa. */ ++ ++bool ++ipa_struct_reorg::fc_input_ssa_p (tree var, const fc_cond *cond) ++{ ++ if (TREE_CODE (var) != SSA_NAME) ++ return false; ++ ++ for (auto *fc_f : cond->fields) ++ if (fc_f->input_ssa == var) ++ return true; ++ ++ return false; ++} ++ ++/* Return true the VAR is loaded from another fc field. */ ++ ++bool ++ipa_struct_reorg::fc_field_load_p (tree var, const fc_cond *cond) ++{ ++ if (TREE_CODE (var) != SSA_NAME) ++ return false; ++ ++ gimple *stmt = SSA_NAME_DEF_STMT (var); ++ return gimple_assign_load_p (stmt) ++ && fc_cond_field_p (gimple_assign_rhs1 (stmt), cond); ++} ++ ++/* Reduce the high_bound of COND by 1, if the value is larger ++ than the high_bound. */ ++ ++void ++ipa_struct_reorg::update_high_bound (fc_cond *cond, HOST_WIDE_INT value) ++{ ++ HOST_WIDE_INT high_bound = tree_to_uhwi (cond->high_bound); ++ if (value >= 0 && value <= high_bound) ++ return; ++ ++ if (cond->special_values.contains (value)) ++ return; ++ ++ high_bound--; ++ cond->high_bound = build_int_cst (TREE_TYPE (cond->high_bound), high_bound); ++ cond->special_values.safe_push (value); ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ FC_DUMP_MSG ("Found special value %ld, and reduce high_bound to 0x%lx\n", ++ value, high_bound); ++} ++ ++/* Check all data in fc_cond refer to a closure. */ ++bool ++ipa_struct_reorg::check_closure (fc_type_info *info) ++{ ++ for (auto *cond : info->fc_conds) ++ { ++ if (!check_closure (info, cond)) ++ { ++ FC_DUMP_MSG ("Fail checking closure\n"); ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++/* All write stmts could be ++ (1) unchange, i.e. optimize away compress/decompress ++ (2) change, i.e. use unoptimized compress ++ ++ For case (2), we may have the scenario like below, ++ ++ B = A->field; ++ ... = B; ++ C->field = B; ++ ++ We still can prove C->field is from A->field, so they are ++ in a closure, but we must decompress A->field and compress ++ C->field, because B may be used outside the clsoure, for ++ which we don't care about. */ ++ ++bool ++ipa_struct_reorg::check_closure (fc_type_info *info, fc_cond *cond) ++{ ++ fc_field_class *field_class = cond->field_class; ++ ++ for (auto *access : info->type->accesses) ++ { ++ if (!access->write_field_p () ++ || access->field->field_class != field_class ++ || !fc_cond_field_p (access->expr, cond)) ++ continue; ++ ++ SET_CFUN (access->function); ++ ++ gimple *stmt = access->stmt; ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Check closure: "); ++ print_gimple_stmt (dump_file, stmt, 0); ++ } ++ ++ /* Skip if we have already analyzed this stmt. */ ++ if (field_class->closure.write_unchange_p (stmt) ++ || field_class->closure.write_change_p (stmt)) ++ continue; ++ ++ tree rhs = gimple_assign_rhs1 (stmt); ++ HOST_WIDE_INT *value = field_class->closure.write_special_rhs.get (rhs); ++ if (fc_input_ssa_p (rhs, cond) ++ || (value && cond->special_values.contains (*value))) ++ { ++ /* Case (2) */ ++ field_class->closure.add_write_change (stmt); ++ FC_DUMP_MSG ("Need to change.\n"); ++ continue; ++ } ++ if (value && !cond->special_values.contains (*value)) ++ { ++ /* Case (2) */ ++ field_class->closure.add_write_unchange (stmt); ++ FC_DUMP_MSG ("No need to change.\n"); ++ continue; ++ } ++ ++ if (!gimple_assign_single_p (stmt) ++ || TREE_CODE (rhs) != SSA_NAME) ++ return false; ++ ++ gimple *def_stmt = SSA_NAME_DEF_STMT (rhs); ++ if (gimple_assign_single_p (def_stmt)) ++ { ++ /* Check if RHS is from the the same fc class. */ ++ srtype *type = NULL; ++ srfield *field = NULL; ++ tree base = NULL_TREE; ++ if (get_base_type (gimple_assign_rhs1 (def_stmt), base, type, field) ++ && field->field_class == field_class) ++ { ++ if (write_field_class_only_p (info, field_class, rhs)) ++ { ++ /* Case (1). */ ++ field_class->closure.add_write_unchange (stmt); ++ field_class->closure.add_read_unchange (def_stmt); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("No need to change: "); ++ print_gimple_stmt (dump_file, def_stmt, 0); ++ } ++ } ++ else ++ { ++ /* Case (2). */ ++ field_class->closure.add_write_change (stmt); ++ FC_DUMP_MSG ("Need to change. \n"); ++ } ++ ++ continue; ++ } ++ } ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Check closure fail: "); ++ print_gimple_stmt (dump_file, stmt, 0); ++ } ++ ++ return false; ++ } ++ ++ collect_closure_read_change (info, field_class); ++ ++ return true; ++} ++ ++/* Return true if all stmts using ssa_def are to write a fc field. */ ++ ++bool ++ipa_struct_reorg::write_field_class_only_p (fc_type_info *info, ++ fc_field_class *field_class, ++ tree ssa_def) ++{ ++ imm_use_iterator imm_iter; ++ gimple *stmt; ++ FOR_EACH_IMM_USE_STMT (stmt, imm_iter, ssa_def) ++ { ++ if (gimple_code (stmt) == GIMPLE_DEBUG) ++ continue; ++ ++ /* We don't know if it is PHI. */ ++ if (!is_gimple_assign (stmt)) ++ return false; ++ ++ srtype *type = NULL; ++ srfield *field = NULL; ++ tree base = NULL_TREE; ++ if (!get_base_type (gimple_assign_lhs (stmt), base, type, field) ++ || type != info->type ++ || !field_class->srfields.contains (field)) ++ return false; ++ } ++ ++ return true; ++} ++ ++/* Collect read_change. */ ++ ++void ++ipa_struct_reorg::collect_closure_read_change (fc_type_info *info, ++ fc_field_class *field_class) ++{ ++ for (auto *access : info->type->accesses) ++ { ++ if (!access->read_field_p () ++ || access->field->field_class != field_class) ++ continue; ++ ++ /* Skip statement that has been marked as unchanged. */ ++ if (field_class->closure.read_unchange_p (access->stmt)) ++ continue; ++ ++ field_class->closure.add_read_change (access->stmt); ++ } ++} ++ ++unsigned ++ipa_struct_reorg::execute_dynamic_field_compression () ++{ ++ if (current_fc_level != fc_level::DYNAMIC) ++ return 0; ++ ++ current_layout_opt_level = STRUCT_REORDER_FIELDS; ++ replace_type_map.empty (); ++ record_accesses (); ++ prune_escaped_types (); ++ check_and_prune_struct_for_field_compression (); ++ ++ return dynamic_fc_rewrite (); ++} ++ ++unsigned ++ipa_struct_reorg::dynamic_fc_rewrite () ++{ ++ if (!create_dynamic_fc_newtypes ()) ++ { ++ FC_DUMP_MSG ("Failed to create newtypes for dfc\n"); ++ return 0; ++ } ++ ++ for (auto *info : fc_infos) ++ { ++ if (!info->dynamic_fc_p) ++ continue; ++ create_dynamic_fc_convert_fn (info); ++ clone_dynamic_fc_path (info); ++ record_dfc_path_info (info); ++ if (flag_ipa_struct_dfc_shadow) ++ rewrite_dynamic_shadow_fields (info); ++ rewrite_dynamic_fc_path (); ++ add_dynamic_checking (info); ++ } ++ ++ return TODO_verify_all; ++} ++ ++bool ++ipa_struct_reorg::create_dynamic_fc_newtypes () ++{ ++ bool created = false; ++ for (auto *info : fc_infos) ++ { ++ if (!info->dynamic_fc_p) ++ continue; ++ ++ create_dynamic_fc_variant (info); ++ if (info->type->create_new_type ()) ++ created = true; ++ else ++ info->dynamic_fc_p = false; ++ } ++ ++ if (!created) ++ return false; ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "=========== all created newtypes ===========\n\n"); ++ dump_newtypes (dump_file); ++ } ++ ++ return true; ++} ++ ++/* Create a new fc_variant for the given fc_type in terms of fc_conds. */ ++ ++void ++ipa_struct_reorg::create_dynamic_fc_variant (fc_type_info *info) ++{ ++ create_global_var_dfc_path (info); ++ info->variant = new fc_variant (); ++} ++ ++/* Create a global variable to identify the current dynamic path. */ ++ ++void ++ipa_struct_reorg::create_global_var_dfc_path (fc_type_info *info) ++{ ++ tree name = get_identifier ("dfc.path"); ++ tree var = build_decl (BUILTINS_LOCATION, VAR_DECL, name, ++ boolean_type_node); ++ ++ TREE_PUBLIC (var) = 1; ++ TREE_STATIC (var) = 1; ++ DECL_IGNORED_P (var) = 1; ++ DECL_ARTIFICIAL (var) = 1; ++ DECL_INITIAL (var) = boolean_false_node; ++ SET_DECL_ASSEMBLER_NAME (var, name); ++ ++ varpool_node::finalize_decl (var); ++ info->dfc_path = var; ++} ++ ++/* Insert compress/decompress functions. */ ++ ++void ++ipa_struct_reorg::create_dynamic_fc_convert_fn (fc_type_info *info) ++{ ++ for (unsigned i = 0; i < info->fc_conds.length (); i++) ++ { ++ fc_cond *cond = info->fc_conds[i]; ++ cond->compress_fn = create_convert_fn (cond, i, false); ++ cond->decompress_fn = create_convert_fn (cond, i, true); ++ } ++} ++ ++/* Create function to further compress fields with special_values. ++ - DECOMP == 0: create function to compress the field. ++ - DECOMP != 0: create function to decompress the field. ++ IDX is an unique number for function name. ++ Return declaration of created function. */ ++ ++tree ++ipa_struct_reorg::create_convert_fn (fc_cond *fcond, unsigned idx, ++ bool decompress) ++{ ++ if (fcond->special_values.is_empty ()) ++ return NULL_TREE; ++ ++ push_cfun (NULL); ++ ++ /* Init declarations. */ ++ char fn_name[64]; ++ const char *name = decompress ? "dfc.decompress." : "dfc.compress."; ++ sprintf (fn_name, "%s%d", name, idx); ++ ++ tree arg_type = decompress ? fcond->new_type : fcond->old_type; ++ tree return_type = decompress ? fcond->old_type : fcond->new_type; ++ tree fn_decl = create_new_fn_decl (fn_name, 1, &arg_type, return_type); ++ ++ basic_block return_bb = init_lowered_empty_function ( ++ fn_decl, true, profile_count::uninitialized ()); ++ calculate_dominance_info (CDI_DOMINATORS); ++ ++ split_edge (single_pred_edge (return_bb)); ++ tree result = make_ssa_name (return_type); ++ create_phi_node (result, return_bb); ++ ++ /* Create compress/decompress function body. */ ++ edge exit_e = create_normal_part (fcond); ++ create_conversion_part (fcond, exit_e, decompress); ++ ++ /* Return stmt. */ ++ update_stmt (gsi_start_phis (return_bb).phi ()); ++ gimple *return_stmt = gimple_build_return (result); ++ gimple_stmt_iterator gsi = gsi_last_bb (return_bb); ++ gsi_insert_after (&gsi, return_stmt, GSI_NEW_STMT); ++ ++ free_dominance_info (CDI_DOMINATORS); ++ update_ssa (TODO_update_ssa); ++ ++ cgraph_node::create (fn_decl); ++ cgraph_node::add_new_function (fn_decl, true); ++ cgraph_edge::rebuild_edges (); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Create %s field function:\n", ++ decompress ? "decompress" : "compress"); ++ dump_function_to_file (cfun->decl, dump_file, dump_flags); ++ } ++ ++ pop_cfun (); ++ ++ return fn_decl; ++} ++ ++/* Insert code for values in the bound: ++ if (arg <= high_bound && arg >= low_bound) ++ return arg; ++ ++ Return the exit_edge of the if region, whose dest is the return block. ++ */ ++ ++edge ++ipa_struct_reorg::create_normal_part (fc_cond *fcond) ++{ ++ edge true_e = NULL; ++ edge false_e = NULL; ++ tree arg = DECL_ARGUMENTS (cfun->decl); ++ ++ /* Create 'arg <= high_bound'. */ ++ basic_block bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); ++ tree tmp_arg = fold_convert (TREE_TYPE (fcond->high_bound), arg); ++ tree cond = build2 (LE_EXPR, boolean_type_node, tmp_arg, fcond->high_bound); ++ edge exit_e = create_empty_if_region_on_edge (single_succ_edge (bb), cond); ++ extract_true_false_edges_from_block (single_succ (bb), &true_e, &false_e); ++ ++ /* Create 'arg >= low_bound'. */ ++ bb = true_e->dest; ++ tmp_arg = fold_convert (TREE_TYPE (fcond->low_bound), arg); ++ cond = build2 (GE_EXPR, boolean_type_node, tmp_arg, fcond->low_bound); ++ create_empty_if_region_on_edge (single_succ_edge (bb), cond); ++ extract_true_false_edges_from_block (single_succ (bb), &true_e, &false_e); ++ ++ /* Return the original value on true_edge. */ ++ bb = true_e->dest; ++ tree return_type = TREE_TYPE (TREE_TYPE (cfun->decl)); ++ tree val = make_ssa_name (return_type); ++ gimple_stmt_iterator gsi = gsi_last_bb (bb); ++ APPEND_GASSIGN_1 (gsi, val, NOP_EXPR, arg); ++ ++ basic_block return_bb = single_pred (EXIT_BLOCK_PTR_FOR_FN (cfun)); ++ redirect_edge_succ (single_succ_edge (bb), return_bb); ++ gphi *phi = gsi_start_phis (return_bb).phi (); ++ add_phi_arg (phi, val, single_succ_edge (bb), UNKNOWN_LOCATION); ++ ++ return exit_e; ++} ++ ++/* Insert conversion code to compress/decompress special values. */ ++ ++void ++ipa_struct_reorg::create_conversion_part (fc_cond *fcond, edge e, bool decomp) ++{ ++ edge exit_e = e; ++ basic_block return_bb = single_pred (EXIT_BLOCK_PTR_FOR_FN (cfun)); ++ HOST_WIDE_INT reserved_value = tree_to_uhwi (fcond->high_bound) + 1; ++ for (unsigned i = 0; i < fcond->special_values.length (); i++) ++ { ++ basic_block bb = exit_e->src; ++ tree special_cst = build_int_cst (signed_type_for (fcond->old_type), ++ fcond->special_values[i]); ++ tree compressed_cst = build_int_cst (fcond->new_type, reserved_value); ++ ++ if (i == fcond->special_values.length () - 1) ++ { ++ /* Omit condition check for the last special value. */ ++ redirect_edge_and_branch (single_succ_edge (bb), return_bb); ++ gphi *phi = gsi_start_phis (return_bb).phi (); ++ add_phi_arg (phi, decomp ? special_cst : compressed_cst, ++ single_succ_edge (bb), UNKNOWN_LOCATION); ++ } ++ else ++ { ++ tree arg = DECL_ARGUMENTS (cfun->decl); ++ tree cond = build2 (EQ_EXPR, boolean_type_node, arg, ++ decomp ? compressed_cst : special_cst); ++ exit_e = create_empty_if_region_on_edge (exit_e, cond); ++ edge true_e = NULL; ++ edge false_e = NULL; ++ extract_true_false_edges_from_block (single_succ (bb), &true_e, ++ &false_e); ++ redirect_edge_and_branch (single_succ_edge (true_e->dest), ++ return_bb); ++ gphi *phi = gsi_start_phis (return_bb).phi (); ++ add_phi_arg (phi, decomp ? special_cst : compressed_cst, ++ single_succ_edge (true_e->dest), UNKNOWN_LOCATION); ++ reserved_value++; ++ } ++ } ++} ++ ++void ++ipa_struct_reorg::clone_dynamic_fc_path (fc_type_info *info) ++{ ++ SET_DUMP_FILE (NULL, TDF_NONE); ++ for (auto *srfn : functions) ++ { ++ SET_CFUN (srfn); ++ ++ if (srfn->partial_clone_p ()) ++ clone_partial_func (info, srfn); ++ else ++ clone_whole_func (srfn); ++ } ++} ++ ++/* start_bb: if (dfc.path) ++ / \ ++ false true ++ / \ ++ origin-bbs clone-bbs ++ */ ++void ++ipa_struct_reorg::clone_partial_func (fc_type_info *info, srfunction *srfn) ++{ ++ calculate_dominance_info (CDI_DOMINATORS); ++ ++ fc_path_info &path = srfn->fc_path; ++ auto_vec &reach_bbs = path.reach_bbs; ++ gimple *start_stmt = path.start_stmt; ++ basic_block fclose_bb = reach_bbs[0]; ++ ++ gcc_assert (fclose_bb == gimple_bb (start_stmt)); ++ edge e = split_block (fclose_bb, start_stmt); ++ reach_bbs[0] = e->dest; ++ ++ unsigned n = reach_bbs.length (); ++ basic_block *origin_bbs = new basic_block[n]; ++ for (unsigned i = 0; i < reach_bbs.length (); i++) ++ origin_bbs[i] = reach_bbs[i]; ++ ++ /* 1. Clone blocks reachable from start point. */ ++ initialize_original_copy_tables (); ++ basic_block *cloned_bbs = new basic_block[n]; ++ copy_bbs (origin_bbs, n, cloned_bbs, NULL, 0, NULL, fclose_bb->loop_father, ++ fclose_bb, true); ++ delete[] origin_bbs; ++ ++ /* Add phis for edges from copied bbs. */ ++ add_phi_args_after_copy (cloned_bbs, n, NULL); ++ free_original_copy_tables (); ++ ++ path.cloned_bbs.reserve (n); ++ for (unsigned i = 0; i < n; i++) ++ path.cloned_bbs.safe_push (cloned_bbs[i]); ++ delete[] cloned_bbs; ++ ++ /* 2. Add if-else on dfc.path. */ ++ basic_block checking_bb = split_edge (e); ++ gimple_stmt_iterator gsi = gsi_last_bb (checking_bb); ++ tree dfc_path_ssa = make_ssa_name (info->dfc_path); ++ gassign *assign = gimple_build_assign (dfc_path_ssa, info->dfc_path); ++ gsi_insert_after (&gsi, assign, GSI_NEW_STMT); ++ gcond *dfc_path_cond = gimple_build_cond_from_tree (dfc_path_ssa, ++ NULL_TREE, NULL_TREE); ++ gsi_insert_after (&gsi, dfc_path_cond, GSI_NEW_STMT); ++ ++ e = single_succ_edge (checking_bb); ++ e->flags = (e->flags & ~EDGE_FALLTHRU) | EDGE_FALSE_VALUE; ++ ++ make_edge (checking_bb, path.cloned_bbs[0], EDGE_TRUE_VALUE); ++ ++ /* Necessary for visiting call stmts. */ ++ cgraph_edge::rebuild_edges (); ++ free_dominance_info (CDI_DOMINATORS); ++ ++ if (loops_state_satisfies_p (LOOPS_NEED_FIXUP)) ++ { ++ calculate_dominance_info (CDI_DOMINATORS); ++ fix_loop_structure (NULL); ++ } ++ ++ update_ssa (TODO_update_ssa); ++} ++ ++void ++ipa_struct_reorg::clone_whole_func (srfunction *srfn) ++{ ++ cgraph_node *new_node; ++ cgraph_node *node = srfn->node; ++ ++ statistics_counter_event (NULL, "Create new function", 1); ++ new_node = node->create_version_clone_with_body (vNULL, NULL, NULL, NULL, ++ NULL, "dfc"); ++ new_node->can_change_signature = node->can_change_signature; ++ new_node->make_local (); ++ ++ srfunction *new_srfn = new srfunction (new_node); ++ if (srfn->is_safe_func) ++ { ++ safe_functions.add (new_srfn->node); ++ new_srfn->is_safe_func = true; ++ } ++ ++ srfn->fc_path.cloned_func = new_srfn; ++} ++ ++/* Rewrite dynamic shadow fields in cloned path. */ ++ ++void ++ipa_struct_reorg::rewrite_dynamic_shadow_fields (fc_type_info *info) ++{ ++ if (info->dynamic_shadow_fields.is_empty ()) ++ return; ++ ++ for (auto *access : info->type->accesses) ++ { ++ srfield *srf = access->field; ++ if (!srf || !srf->fc_f || !srf->fc_f->original) ++ continue; ++ ++ /* Skip statements in original path. */ ++ srfunction *srfn = access->function; ++ gimple *stmt = access->stmt; ++ if (srfn->fc_path.cloned_func ++ || (srfn->partial_clone_p () ++ && !srfn->fc_path.cloned_bbs.contains (gimple_bb (stmt)))) ++ continue; ++ ++ SET_CFUN (srfn); ++ ++ if (access->write_p ()) ++ { ++ /* Remove stmt by replacing lhs by a dummy ssa. */ ++ tree lhs = gimple_assign_lhs (stmt); ++ tree dummy_ssa = make_ssa_name (TREE_TYPE (lhs)); ++ gimple_assign_set_lhs (stmt, dummy_ssa); ++ update_stmt (stmt); ++ } ++ else if (access->read_p ()) ++ modify_shadow_read (stmt, access->index, srf->fc_f, access->base); ++ else ++ gcc_unreachable (); ++ } ++} ++ ++/* Rewrite functions either partially or wholely. */ ++ ++void ++ipa_struct_reorg::rewrite_dynamic_fc_path () ++{ ++ for (auto *srfn : functions) ++ { ++ if (srfn->partial_clone_p ()) ++ { ++ SET_CFUN (srfn); ++ ++ /* 2.1 rewrite the original function for each path. */ ++ rewrite_partial_func (srfn); ++ clean_func_after_rewrite (srfn); ++ } ++ else ++ { ++ /* 2.2 rewrite the cloned function for each path. */ ++ srfunction *cloned_func = srfn->fc_path.cloned_func; ++ SET_CFUN (cloned_func); ++ ++ rewrite_whole_func (cloned_func); ++ clean_func_after_rewrite (cloned_func); ++ } ++ } ++} ++ ++void ++ipa_struct_reorg::record_dfc_path_info (fc_type_info *info) ++{ ++ /* 1. record accesse info for cloned stmts. */ ++ for (auto *srfn : functions) ++ { ++ SET_DUMP_FILE (NULL, TDF_NONE); ++ if (srfn->partial_clone_p ()) ++ { ++ record_function (srfn->node, srfn); ++ } ++ else ++ { ++ srfunction *cloned_srfn = srfn->fc_path.cloned_func; ++ record_function (cloned_srfn->node, cloned_srfn); ++ prune_function (cloned_srfn); ++ } ++ } ++ ++ prune_globals (); ++ gcc_assert (!info->type->has_escaped ()); ++ ++ /* 2. collect closure info for cloned paths. */ ++ collect_closure_info_dynamic (info); ++} ++ ++/* Collect the closure info for all dynamic-fc cloned paths. */ ++ ++void ++ipa_struct_reorg::collect_closure_info_dynamic (fc_type_info *info) ++{ ++ for (auto *cond : info->fc_conds) ++ { ++ fc_field_class *field_class = cond->field_class; ++ for (auto *srfn : functions) ++ { ++ if (srfn->partial_clone_p ()) ++ collect_closure_info_partial (srfn, &field_class->closure); ++ else ++ collect_closure_info_whole (srfn, &field_class->closure); ++ } ++ } ++} ++ ++/* Collect closure info for partially cloned function SRFN in dynamic fc. */ ++ ++void ++ipa_struct_reorg::collect_closure_info_partial (srfunction *srfn, ++ fc_closure *cinfo) ++{ ++ closure_helper helper (cinfo); ++ ++ for (auto *bb : srfn->fc_path.reach_bbs) ++ helper.record_origin_closure (bb); ++ ++ helper.reset_uid (); ++ ++ for (unsigned i = 0; i < srfn->fc_path.reach_bbs.length (); i++) ++ helper.add_cloned_closure (srfn->fc_path.cloned_bbs[i]); ++} ++ ++/* Collect closure info for wholely cloned function SRFN in dfc. */ ++ ++void ++ipa_struct_reorg::collect_closure_info_whole (srfunction *srfn, ++ fc_closure *cinfo) ++{ ++ closure_helper helper (cinfo); ++ ++ basic_block bb = NULL; ++ FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (srfn->node->decl)) ++ helper.record_origin_closure (bb); ++ ++ helper.reset_uid (); ++ ++ srfunction *cloned_srfn = srfn->fc_path.cloned_func; ++ FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (cloned_srfn->node->decl)) ++ helper.add_cloned_closure (bb); ++} ++ ++void ++ipa_struct_reorg::rewrite_partial_func (srfunction *srfn) ++{ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Before rewrite: %s\n", srfn->node->name ()); ++ dump_function_to_file (current_function_decl, dump_file, ++ dump_flags | TDF_VOPS); ++ FC_DUMP_MSG ("Start to rewrite: %s\n", srfn->node->name ()); ++ fprintf (dump_file, "\n\n"); ++ } ++ ++ srfn->create_new_decls (); ++ ++ /* Rewrite each related stmts in the current path. */ ++ for (unsigned i = 0; i < srfn->fc_path.reach_bbs.length (); i++) ++ rewrite_block (srfn->fc_path.cloned_bbs[i]); ++} ++ ++void ++ipa_struct_reorg::rewrite_whole_func (srfunction *srfn) ++{ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Before rewrite: %s\n", srfn->node->name ()); ++ dump_function_to_file (current_function_decl, dump_file, ++ dump_flags | TDF_VOPS); ++ FC_DUMP_MSG ("Start to rewrite: %s\n", srfn->node->name ()); ++ } ++ ++ create_new_args (srfn->node); ++ srfn->create_new_decls (); ++ ++ basic_block bb = NULL; ++ FOR_EACH_BB_FN (bb, cfun) ++ rewrite_block (bb); ++} ++ ++void ++ipa_struct_reorg::clean_func_after_rewrite (srfunction *srfn) ++{ ++ if (!srfn->partial_clone_p ()) ++ for (auto *srd : srfn->args) ++ release_srdecl_ssa_name (srd); ++ ++ for (auto *srd : srfn->decls) ++ release_srdecl_ssa_name (srd); ++ ++ { ++ SET_DUMP_FILE (NULL, TDF_NONE); ++ update_ssa (TODO_update_ssa_only_virtuals); ++ ++ unsigned i; ++ tree ssa_name; ++ FOR_EACH_SSA_NAME (i, ssa_name, cfun) ++ { ++ if (SSA_NAME_IN_FREE_LIST (ssa_name)) ++ continue; ++ ++ gimple *stmt = SSA_NAME_DEF_STMT (ssa_name); ++ ++ if (!stmt || (!SSA_NAME_IS_DEFAULT_DEF (ssa_name) ++ && !gimple_bb (stmt))) ++ release_ssa_name (ssa_name); ++ } ++ ++ if (flag_tree_pta) ++ compute_may_aliases (); ++ ++ remove_unused_locals (); ++ cgraph_edge::rebuild_edges (); ++ free_dominance_info (CDI_DOMINATORS); ++ } ++ ++ if (dump_file) ++ { ++ FC_DUMP_MSG ("After rewrite: %s\n", srfn->node->name ()); ++ dump_function_to_file (current_function_decl, dump_file, ++ dump_flags | TDF_VOPS); ++ fprintf (dump_file, "\n\n"); ++ } ++} ++ ++void ++ipa_struct_reorg::dynamic_fc_rewrite_assign (gimple *stmt, tree rhs, ++ tree &newlhs, tree &newrhs) ++{ ++ fc_closure *closure = cur_srfd->get_closure (); ++ if (closure->write_change_p (stmt)) ++ { ++ /* For a write stmt _0->fld = rhs, should only rewrite lhs. */ ++ gcc_assert (newrhs == NULL_TREE); ++ tree compress_fn = cur_srfd->fc_f->cond->compress_fn; ++ if (compress_fn) ++ newrhs = closure->convert_rhs (rhs, compress_fn); ++ } ++ else if (closure->read_change_p (stmt)) ++ { ++ /* For a read stmt lhs = _0->fld, should only rewrite rhs. */ ++ gcc_assert (newlhs == NULL_TREE); ++ tree decompress_fn = cur_srfd->fc_f->cond->decompress_fn; ++ if (decompress_fn) ++ newrhs = closure->convert_rhs (newrhs, decompress_fn); ++ } ++ else if (!closure->unchange_p (stmt)) ++ gcc_unreachable (); ++} ++ ++/* Add code for dynamic checking and data compressing. */ ++ ++void ++ipa_struct_reorg::add_dynamic_checking (fc_type_info *info) ++{ ++ basic_block bb = gimple_bb (info->fclose_stmt); ++ gcc_assert (single_succ_p (bb)); ++ ++ SET_CFUN (info->start_srfn); ++ ++ insert_code_calc_dfc_path (info); ++ insert_code_compress_data (info, single_succ_edge (bb)); ++ ++ SET_DUMP_FILE (NULL, TDF_NONE); ++ cgraph_edge::rebuild_edges (); ++ update_ssa (TODO_update_ssa_only_virtuals); ++} ++ ++/* Insert dynamic checking code to calculate info->dfc_path. */ ++ ++void ++ipa_struct_reorg::insert_code_calc_dfc_path (fc_type_info *info) ++{ ++ insert_code_calc_max_min_val (info); ++ gimple_stmt_iterator gsi = gsi_for_stmt (info->fclose_stmt); ++ tree dfc_path = insert_code_calc_cond (info, &gsi); ++ insert_code_check_init_const (info, &gsi, dfc_path); ++ ++ /* Store dfc_path to global var. */ ++ gimple *dfc_path_stmt = gimple_build_assign (info->dfc_path, dfc_path); ++ gsi_insert_after (&gsi, dfc_path_stmt, GSI_NEW_STMT); ++ ++ basic_block bb = gimple_bb (info->fclose_stmt); ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ fprintf (dump_file, "\n"); ++ FC_DUMP_MSG ("Insert code to calculate dfc.path\n"); ++ dump_bb (dump_file, bb, 0, TDF_DETAILS); ++ } ++} ++ ++/* Insert code to calculate min and max after input_ssa inside the loop. */ ++ ++void ++ipa_struct_reorg::insert_code_calc_max_min_val (fc_type_info *info) ++{ ++ basic_block bb = gimple_bb (info->input_stmt); ++ class loop *loop = bb->loop_father; ++ edge latch_edge = loop_latch_edge (loop); ++ for (unsigned i = 0; i < info->fc_conds.length (); i++) ++ { ++ fc_cond *cond = info->fc_conds[i]; ++ /* Use the old type for min and max value, as they will be used to ++ compare with the input ssa, which is with old type. */ ++ tree ssa_type = TREE_TYPE (cond->fields[0]->input_ssa); ++ char *min_name = append_suffix ("dfc.min_cond.", i); ++ char *max_name = append_suffix ("dfc.max_cond.", i); ++ cond->min_val = make_temp_ssa_name (ssa_type, NULL, min_name); ++ cond->max_val = make_temp_ssa_name (ssa_type, NULL, max_name); ++ ++ /* Insert phi for min and max in loop header. */ ++ gphi *min_phi = create_phi_node (cond->min_val, loop->header); ++ gphi *max_phi = create_phi_node (cond->max_val, loop->header); ++ ++ /* For the input_ssa of each fc fields, we calculate min and max. ++ Assume all of the fc_fields have been sorted in terms of the ++ position of input_ssa. We should always access an input_ssa in ++ forward direction. This way, all fields' input will be used to ++ update min_val and max_val in order. */ ++ tree min_val = cond->min_val; ++ tree max_val = cond->max_val; ++ hash_set input_ssa; ++ for (auto *fc_f : cond->fields) ++ { ++ /* We handle the same input_ssa only once. */ ++ if (input_ssa.contains (fc_f->input_ssa)) ++ continue; ++ ++ input_ssa.add (fc_f->input_ssa); ++ gcc_assert (TREE_TYPE (fc_f->input_ssa) == ssa_type); ++ ++ /* Insert new stmt immediately after input_ssa. */ ++ gimple *def_stmt = SSA_NAME_DEF_STMT (fc_f->input_ssa); ++ gimple_stmt_iterator input_gsi = gsi_for_stmt (def_stmt); ++ bb = gimple_bb (def_stmt); ++ ++ /* min = (input < min) ? input : min_phi */ ++ tree min_cmp = fold_build2 (LT_EXPR, boolean_type_node, ++ fc_f->input_ssa, min_val); ++ tree input_min_rhs = build_cond_expr (min_cmp, fc_f->input_ssa, ++ min_val); ++ min_val = make_temp_ssa_name (ssa_type, NULL, min_name); ++ gimple *min_stmt = gimple_build_assign (min_val, input_min_rhs); ++ gsi_insert_after (&input_gsi, min_stmt, GSI_NEW_STMT); ++ ++ /* max = (input < max) ? input : max_phi */ ++ tree max_cmp = fold_build2 (GT_EXPR, boolean_type_node, ++ fc_f->input_ssa, max_val); ++ tree input_max_rhs = build_cond_expr (max_cmp, fc_f->input_ssa, ++ max_val); ++ max_val = make_temp_ssa_name (ssa_type, NULL, max_name); ++ gimple *max_stmt = gimple_build_assign (max_val, input_max_rhs); ++ gsi_insert_after (&input_gsi, max_stmt, GSI_NEW_STMT); ++ } ++ free (min_name); ++ free (max_name); ++ ++ /* Add input_min_rhs and input_max_rhs phis. */ ++ add_phi_arg (min_phi, min_val, latch_edge, UNKNOWN_LOCATION); ++ add_phi_arg (max_phi, max_val, latch_edge, UNKNOWN_LOCATION); ++ edge entry_edge = NULL; ++ edge_iterator ei; ++ FOR_EACH_EDGE (entry_edge, ei, loop->header->preds) ++ { ++ if (entry_edge == latch_edge) ++ continue; ++ add_phi_arg (min_phi, build_zero_cst (ssa_type), entry_edge, ++ UNKNOWN_LOCATION); ++ add_phi_arg (max_phi, build_zero_cst (ssa_type), entry_edge, ++ UNKNOWN_LOCATION); ++ } ++ } ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Insert min/max calculation\n"); ++ dump_bb (dump_file, loop->header, 0, TDF_DETAILS); ++ dump_bb (dump_file, bb, 0, TDF_DETAILS); ++ } ++} ++ ++/* Insert code to calculate fc_cond after fclose. */ ++ ++tree ++ipa_struct_reorg::insert_code_calc_cond (fc_type_info *info, ++ gimple_stmt_iterator *gsi) ++{ ++ tree dfc_path = boolean_true_node; ++ for (auto *cond : info->fc_conds) ++ { ++ /* min >= low_bound */ ++ tree cmp_min = fold_build2 (GE_EXPR, boolean_type_node, ++ cond->min_val, cond->low_bound); ++ ++ /* max <= high_bound */ ++ tree cmp_max = fold_build2 (LE_EXPR, boolean_type_node, ++ cond->max_val, cond->high_bound); ++ ++ /* ret = ((min >= low_bound) && (max <= high_bound)) */ ++ tree cmp_ret = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, ++ cmp_min, cmp_max); ++ ++ /* dfc.path.tmp = dfc.path.tmp && ret */ ++ tree tmp = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, dfc_path, ++ cmp_ret); ++ dfc_path = force_gimple_operand_gsi (gsi, tmp, true, NULL, false, ++ GSI_CONTINUE_LINKING); ++ } ++ ++ return dfc_path; ++} ++ ++/* Insert code to check init_const for dynamic shadow fields. */ ++ ++void ++ipa_struct_reorg::insert_code_check_init_const (fc_type_info *info, ++ gimple_stmt_iterator *gsi, ++ tree &dfc_path) ++{ ++ basic_block bb = gimple_bb (info->input_stmt); ++ class loop *loop = bb->loop_father; ++ edge latch_edge = loop_latch_edge (loop); ++ ++ for (auto *fc_f : info->dynamic_shadow_fields) ++ { ++ gcc_assert (fc_f->init_const); ++ ++ /* Skip a init_const that is in special_values, because the boundary ++ check for fc_cond should have cover that. */ ++ tree init_const = fc_f->init_const; ++ if (fc_f->input_field->cond->special_values.contains ( ++ tree_to_uhwi (init_const))) ++ continue; ++ ++ tree shadow_valid = make_temp_ssa_name (boolean_type_node, NULL, ++ "dfc.shadow_valid"); ++ gphi *shadow_valid_phi = create_phi_node (shadow_valid, loop->header); ++ ++ auto input_gsi = gsi_for_stmt (SSA_NAME_DEF_STMT (fc_f->input_ssa)); ++ /* input != init_const */ ++ tree ne_ret = fold_build2 (NE_EXPR, boolean_type_node, fc_f->input_ssa, ++ init_const); ++ tree ne_tmp = force_gimple_operand_gsi (&input_gsi, ne_ret, true, NULL, ++ false, GSI_CONTINUE_LINKING); ++ ++ /* shadow_valid = shadow_valid && (input != init_const) */ ++ tree and_ret = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, ++ shadow_valid, ne_tmp); ++ tree and_tmp = force_gimple_operand_gsi (&input_gsi, and_ret, true, ++ NULL, false, ++ GSI_CONTINUE_LINKING); ++ ++ /* Insert phi for shadow_valid in loop header. */ ++ add_phi_arg (shadow_valid_phi, and_tmp, latch_edge, UNKNOWN_LOCATION); ++ edge entry_edge = NULL; ++ edge_iterator ei; ++ FOR_EACH_EDGE (entry_edge, ei, loop->header->preds) ++ { ++ if (entry_edge == latch_edge) ++ continue; ++ add_phi_arg (shadow_valid_phi, boolean_true_node, entry_edge, ++ UNKNOWN_LOCATION); ++ } ++ ++ /* dfc.path.tmp = dfc.path.tmp && shadow_valid */ ++ tree tmp = fold_build2 (TRUTH_AND_EXPR, boolean_type_node, dfc_path, ++ shadow_valid); ++ dfc_path = force_gimple_operand_gsi (gsi, tmp, true, NULL, false, ++ GSI_CONTINUE_LINKING); ++ } ++} ++ ++/* Split edge E and insert code to compress data. */ ++ ++void ++ipa_struct_reorg::insert_code_compress_data (fc_type_info *info, edge e) ++{ ++ if (!dom_info_available_p (CDI_DOMINATORS)) ++ calculate_dominance_info (CDI_DOMINATORS); ++ if (!loops_state_satisfies_p (LOOPS_HAVE_PREHEADERS)) ++ loop_optimizer_init (LOOPS_HAVE_PREHEADERS); ++ record_loop_exits (); ++ ++ auto_vec array_names; ++ auto_vec size_names; ++ basic_block bb = e->src; ++ gimple_stmt_iterator gsi = gsi_last_bb (bb); ++ ++ /* Generate SSA_NAMEs for fc_array, if they are global addresses. */ ++ for (auto *array : info->fc_arrays) ++ { ++ array_names.safe_push (generate_ssa_name (array->var, &gsi)); ++ size_names.safe_push (generate_ssa_name (array->size, &gsi)); ++ } ++ ++ insert_code_compress_variant (info, bb, array_names, size_names); ++} ++ ++/* Insert code tot compress hot data for a fc_variant. */ ++ ++void ++ipa_struct_reorg::insert_code_compress_variant ( ++ fc_type_info *info, basic_block bb, const auto_vec &array_names, ++ const auto_vec &size_names) ++{ ++ edge true_e = NULL; ++ edge false_e = NULL; ++ edge entry_e = single_succ_edge (bb); ++ create_empty_if_region_on_edge (entry_e, info->dfc_path); ++ extract_true_false_edges_from_block (entry_e->dest, &true_e, &false_e); ++ ++ /* Create function decl and node for compressed single object. */ ++ create_compress_object_fn (info); ++ ++ edge current_e = true_e; ++ insert_code_compress_array (info, current_e, array_names, size_names); ++ insert_code_modify_refs (info, current_e); ++} ++ ++/* For each variable(array) to compress, insert loop like: ++ : ++ _1 = ARRAY_BASE; ++ _2 = ARRAY_SIZE; ++ goto ; ++ ++ : ++ # i_1 = PHI <0, i_2(THEN)> ++ if (i_1 < _2) ++ goto ; ++ else ++ goto ; ++ ++ : ++ dfc.compress_obj (_1, i_1); ++ i_2 = i_1 + 1; ++ ++ : ++ */ ++ ++void ++ipa_struct_reorg::insert_code_compress_array ( ++ fc_type_info *info, edge &e, const auto_vec &data_names, ++ const auto_vec &size_names) ++{ ++ loop_p outer_loop = gimple_bb (info->fclose_stmt)->loop_father; ++ for (size_t i = 0; i < size_names.length (); ++i) ++ { ++ tree iv_before, iv_after; ++ tree size_type = TREE_TYPE (size_names[i]); ++ tree iv = create_tmp_reg (size_type, "dfc.compress_idx"); ++ loop_p loop ++ = create_empty_loop_on_edge (e, build_zero_cst (size_type), ++ build_int_cst (size_type, 1), ++ size_names[i], iv, &iv_before, ++ &iv_after, outer_loop); ++ ++ /* Build call statement to compress a single object. */ ++ basic_block latch_bb = loop->latch; ++ auto gsi = gsi_last_bb (latch_bb); ++ tree fndecl = info->variant->compress_object_fn; ++ gcall *call = gimple_build_call (fndecl, 2, data_names[i], iv_before); ++ gsi_insert_after (&gsi, call, GSI_NEW_STMT); ++ cgraph_node *node = cgraph_node::get (current_function_decl); ++ cgraph_node *new_node = cgraph_node::get (fndecl); ++ node->create_edge (new_node, call, latch_bb->count); ++ ++ e = single_exit (loop); ++ } ++} ++ ++/* Insert code to modify all fc_refs. */ ++ ++void ++ipa_struct_reorg::insert_code_modify_refs (fc_type_info *info, edge current_e) ++{ ++ fc_variant *variant = info->variant; ++ loop_p outer_loop = gimple_bb (info->fclose_stmt)->loop_father; ++ for (auto *dr : info->fc_refs) ++ { ++ if (!dr->size) ++ { ++ /* 1) fc_ref is a single ptr. */ ++ current_e = insert_code_modify_single_ref ( ++ current_e, dr->var, dr->source, ++ TYPE_SIZE_UNIT (info->type->type), ++ TYPE_SIZE_UNIT (variant->new_type)); ++ continue; ++ } ++ ++ /* 2) fc_ref is an array, create a loop. */ ++ tree iv_before, iv_after; ++ tree ptr_type = dr->orig_type ? dr->orig_type : TREE_TYPE (dr->var); ++ tree size_type = TREE_TYPE (dr->size); ++ loop_p loop = create_empty_loop_on_edge ( ++ current_e, build_zero_cst (size_type), ++ build_int_cst (size_type, 1), dr->size, ++ create_tmp_reg (size_type, NULL), &iv_before, ++ &iv_after, outer_loop); ++ /* Fetch array element. */ ++ auto gsi = gsi_last_bb (loop->latch); ++ tree var1 = make_ssa_name (ptr_type); ++ gsi_insert_after (&gsi, gimple_build_assign (var1, dr->var), ++ GSI_NEW_STMT); ++ tree var_mul = make_ssa_name (long_unsigned_type_node); ++ APPEND_GASSIGN_2 (gsi, var_mul, MULT_EXPR, iv_before, ++ TYPE_SIZE_UNIT (TREE_TYPE (ptr_type))); ++ tree var_plus = make_ssa_name (ptr_type); ++ APPEND_GASSIGN_2 (gsi, var_plus, POINTER_PLUS_EXPR, var1, var_mul); ++ tree ref_expr = build2 (MEM_REF, TREE_TYPE (ptr_type), var_plus, ++ build_int_cst (ptr_type, 0)); ++ if (dr->field) ++ ref_expr = build3 (COMPONENT_REF, TREE_TYPE (dr->field), ref_expr, ++ dr->field, NULL_TREE); ++ /* Modify the ref's value. */ ++ insert_code_modify_single_ref (single_succ_edge (loop->latch), ++ ref_expr, dr->source, ++ TYPE_SIZE_UNIT (info->type->type), ++ TYPE_SIZE_UNIT (variant->new_type)); ++ current_e = single_exit (loop); ++ } ++} ++ ++/* Create function to compress a single object. Return function decl. */ ++ ++void ++ipa_struct_reorg::create_compress_object_fn (fc_type_info *info) ++{ ++ /* Function declairation. */ ++ tree orig_struct_type = info->type->type; ++ tree orig_struct_size = TYPE_SIZE_UNIT (orig_struct_type); ++ tree orig_ptr_type = build_pointer_type (orig_struct_type); ++ tree size_type = TREE_TYPE (orig_struct_size); ++ tree arg_types[2] = {orig_ptr_type, size_type}; ++ char fn_name[32]; ++ sprintf (fn_name, "%s", "dfc.compress_obj"); ++ tree fndecl = create_new_fn_decl (fn_name, 2, arg_types, void_type_node); ++ ++ /* Function arguments. */ ++ tree struct_array = DECL_ARGUMENTS (fndecl); ++ tree idx = TREE_CHAIN (struct_array); ++ ++ /* Push NULL cfun. */ ++ push_cfun (NULL); ++ basic_block bb = init_lowered_empty_function ( ++ fndecl, true, profile_count::uninitialized ()); ++ ++ /* Function body. */ ++ /* Use a temporary struct to avoid overlapping. */ ++ tree tmp_obj = create_tmp_var (orig_struct_type, "tmp"); ++ /* tmp = start[i]; ++ => ++ idx_1 = (long unsigned int) idx; ++ _2 = idx_1 * sizeof (orig_struct); ++ _3 = start + _2; ++ tmp = *_3; ++ */ ++ gimple_stmt_iterator gsi = gsi_last_bb (bb); ++ tree offset = make_ssa_name (long_unsigned_type_node); ++ APPEND_GASSIGN_2 (gsi, offset, MULT_EXPR, idx, orig_struct_size); ++ tree address = make_ssa_name (orig_ptr_type); ++ APPEND_GASSIGN_2 (gsi, address, POINTER_PLUS_EXPR, struct_array, offset); ++ tree rhs = build2 (MEM_REF, orig_struct_type, address, ++ build_int_cst (orig_ptr_type, 0)); ++ APPEND_GASSIGN_1 (gsi, tmp_obj, MEM_REF, rhs); ++ ++ /* Init: new_struct* ptr = start + idx_1 * sizeof (new_struct) */ ++ fc_variant *variant = info->variant; ++ tree new_type = variant->new_type; ++ tree new_ptr_type = build_pointer_type (new_type); ++ tree new_ptr = create_tmp_var (new_ptr_type, "ptr"); ++ offset = make_ssa_name (long_unsigned_type_node); ++ APPEND_GASSIGN_2 (gsi, offset, MULT_EXPR, idx, TYPE_SIZE_UNIT (new_type)); ++ APPEND_GASSIGN_2 (gsi, new_ptr, POINTER_PLUS_EXPR, struct_array, offset); ++ tree ref = build2 (MEM_REF, new_type, new_ptr, ++ build_int_cst (new_ptr_type, 0)); ++ ++ /* Compress and assign the fields. */ ++ for (auto *field : info->type->fields) ++ { ++ /* Skip shadow fields. */ ++ if (field->fc_f && field->fc_f->original) ++ continue; ++ ++ tree old_field = field->fielddecl; ++ tree old_field_type = field->fieldtype; ++ tree new_field = field->newfield[0] ? field->newfield[0] : old_field; ++ tree new_field_type = TREE_TYPE (new_field); ++ ++ tree var = make_ssa_name (old_field_type); ++ tree rhs = build3 (COMPONENT_REF, old_field_type, tmp_obj, old_field, ++ NULL_TREE); ++ APPEND_GASSIGN_1 (gsi, var, COMPONENT_REF, rhs); ++ if (new_field_type != old_field_type) ++ { ++ fc_cond *cond = field->fc_f->cond; ++ if (cond && cond->compress_fn) ++ { ++ /* Need compressing. */ ++ /* As we may have bitfield, so cond->new_type and new_type ++ can be different. */ ++ tree compressed_var = make_ssa_name (cond->new_type); ++ gcall *stmt = gimple_build_call (cond->compress_fn, 1, var); ++ gimple_call_set_lhs (stmt, compressed_var); ++ gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); ++ var = compressed_var; ++ } ++ tree converted_var = make_ssa_name (new_field_type); ++ APPEND_GASSIGN_1 (gsi, converted_var, NOP_EXPR, var); ++ var = converted_var; ++ } ++ tree lhs = build3 (COMPONENT_REF, new_field_type, ref, new_field, ++ NULL_TREE); ++ APPEND_GASSIGN_1 (gsi, lhs, MEM_REF, var); ++ } ++ ++ /* Clobber and return. */ ++ tree clobber = build_clobber (orig_struct_type); ++ gimple *stmt = gimple_build_assign (tmp_obj, clobber); ++ gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); ++ stmt = gimple_build_return (NULL); ++ gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); ++ ++ { ++ SET_DUMP_FILE (NULL, TDF_NONE); ++ update_ssa (TODO_update_ssa); ++ } ++ ++ cgraph_node::create (fndecl); ++ cgraph_node::add_new_function (fndecl, true); ++ cgraph_edge::rebuild_edges (); ++ ++ if (dump_file && (dump_flags & TDF_DETAILS)) ++ { ++ FC_DUMP_MSG ("Create compress object function:\n"); ++ dump_function_to_file (cfun->decl, dump_file, dump_flags); ++ } ++ pop_cfun (); ++ ++ info->variant->compress_object_fn = fndecl; ++} ++ ++/* Split edge E and insert codes to modify a single fc_ref expression. ++ Return the exit edge of created codes. */ ++ ++edge ++ipa_struct_reorg::insert_code_modify_single_ref (edge e, tree ref, ++ fc_array *array, ++ tree orig_size, ++ tree new_size) ++{ ++ /* For each fc_ref, create code like: ++ if (REF) ++ REF = (long) ARRAY + ((long) REF - (long) ARRAY) ++ / sizeof(old_type) * sizeof(new_type); ++ */ ++ ++ /* 1) Create ssa_name for fc_ref. */ ++ tree ref_ssa_name = create_tmp_reg (TREE_TYPE (ref)); ++ gimple *stmt = gimple_build_assign (ref_ssa_name, unshare_expr (ref)); ++ basic_block bb = split_edge (e); ++ gimple_stmt_iterator gsi = gsi_last_bb (bb); ++ gsi_insert_after (&gsi, stmt, GSI_NEW_STMT); ++ ++ /* 2) Create the if-else structure. */ ++ tree cmp = build2 (EQ_EXPR, boolean_type_node, ref_ssa_name, ++ null_pointer_node); ++ edge exit_e = create_empty_if_region_on_edge (single_succ_edge (bb), cmp); ++ edge true_e = NULL; ++ edge false_e = NULL; ++ extract_true_false_edges_from_block (single_succ (bb), &true_e, &false_e); ++ gsi = gsi_last_bb (false_e->dest); ++ ++ /* 3) Create conversion codes. */ ++ tree ssa_def = array->ssa_def; ++ tree sub_var = make_ssa_name (ptrdiff_type_node); ++ APPEND_GASSIGN_2 (gsi, sub_var, POINTER_DIFF_EXPR, ref_ssa_name, ssa_def); ++ tree div_var = make_ssa_name (ptrdiff_type_node); ++ APPEND_GASSIGN_2 (gsi, div_var, TRUNC_DIV_EXPR, sub_var, ++ fold_convert (ptrdiff_type_node, orig_size)); ++ tree mul_var = make_ssa_name (ptrdiff_type_node); ++ APPEND_GASSIGN_2 (gsi, mul_var, MULT_EXPR, div_var, ++ fold_convert (ptrdiff_type_node, new_size)); ++ tree mul_var2 = make_ssa_name (size_type_node); ++ APPEND_GASSIGN_1 (gsi, mul_var2, NOP_EXPR, mul_var); ++ tree add_var = make_ssa_name (TREE_TYPE (ssa_def)); ++ APPEND_GASSIGN_2 (gsi, add_var, POINTER_PLUS_EXPR, ssa_def, mul_var2); ++ ++ /* 4) Store. */ ++ gsi_insert_after (&gsi, gimple_build_assign (unshare_expr (ref), add_var), ++ GSI_NEW_STMT); ++ return exit_e; ++} ++ ++/* Init pointer size from parameter param_pointer_compression_size. */ ++ ++static void ++init_pointer_size_for_pointer_compression (void) ++{ ++ switch (param_pointer_compression_size) ++ { ++ case 8: ++ // FALLTHRU ++ case 16: ++ // FALLTHRU ++ case 32: compressed_size = param_pointer_compression_size; break; ++ default: ++ error ("Invalid pointer compression size, using the following param: " ++ "\"--param compressed-pointer-size=[8,16,32]\""); ++ } ++} ++ ++unsigned int ++ipa_struct_reorg::execute (unsigned int opt) ++{ ++ unsigned int ret = 0; ++ ++ if (dump_file) ++ fprintf (dump_file, "\n\n====== ipa_struct_reorg level %d ======\n\n", ++ opt); ++ ++ if (opt != COMPLETE_STRUCT_RELAYOUT) ++ { ++ current_layout_opt_level = opt; ++ /* If there is a top-level inline-asm, ++ the pass immediately returns. */ ++ if (symtab->first_asm_symbol ()) ++ return 0; ++ record_accesses (); ++ prune_escaped_types (); ++ if (current_layout_opt_level == STRUCT_SPLIT) ++ analyze_types (); ++ ++ if (opt >= POINTER_COMPRESSION_SAFE) ++ check_and_prune_struct_for_pointer_compression (); ++ if (opt >= SEMI_RELAYOUT) ++ check_and_prune_struct_for_semi_relayout (); ++ /* Avoid doing static field compression in STRUCT_SPLIT. */ ++ if (opt >= STRUCT_REORDER_FIELDS ++ && 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 (); ++ ++ ret = execute_struct_relayout (); ++ } ++ ++ return ret; ++} ++ ++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 ++}; ++ ++class pass_ipa_struct_reorg : public simple_ipa_opt_pass ++{ ++public: ++ pass_ipa_struct_reorg (gcc::context *ctxt) ++ : simple_ipa_opt_pass (pass_data_ipa_struct_reorg, ctxt) ++ {} ++ ++ /* opt_pass methods: */ ++ virtual bool gate (function *); ++ virtual unsigned int execute (function *) ++ { ++ unsigned int ret = 0; ++ unsigned int ret_reorg = 0; ++ unsigned int level = 0; ++ switch (struct_layout_optimize_level) ++ { ++ case 6: level |= SEMI_RELAYOUT; ++ // FALLTHRU ++ case 5: level |= POINTER_COMPRESSION_UNSAFE; ++ // FALLTHRU ++ case 4: level |= POINTER_COMPRESSION_SAFE; ++ // FALLTHRU ++ case 3: level |= DEAD_FIELD_ELIMINATION; ++ // FALLTHRU ++ case 2: level |= STRUCT_REORDER_FIELDS; ++ // FALLTHRU ++ case 1: ++ level |= COMPLETE_STRUCT_RELAYOUT; ++ level |= STRUCT_SPLIT; ++ break; ++ case 0: break; ++ default: gcc_unreachable (); ++ } ++ ++ if (level & POINTER_COMPRESSION_SAFE) ++ init_pointer_size_for_pointer_compression (); ++ ++ if (level & SEMI_RELAYOUT) ++ { ++ semi_relayout_align = semi_relayout_level; ++ 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) + ret = ipa_struct_reorg ().execute (STRUCT_REORDER_FIELDS); +@@ -10159,6 +13783,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); + +@@ -10172,6 +13799,13 @@ public: + if (!ret_reorg) + ret_reorg = ipa_struct_reorg ().execute (COMPLETE_STRUCT_RELAYOUT); + } ++ ++ if (ret && flag_ipa_struct_dfc) ++ { ++ current_fc_level = fc_level::DYNAMIC; ++ ret = ipa_struct_reorg ().execute_dynamic_field_compression (); ++ } ++ + return ret | ret_reorg; + } + +diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.h b/gcc/ipa-struct-reorg/ipa-struct-reorg.h +index 2ab6444d6..c7c6b7433 100644 +--- a/gcc/ipa-struct-reorg/ipa-struct-reorg.h ++++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.h +@@ -59,14 +59,56 @@ const char *escape_type_string[escape_max_escape - 1] = + #include "escapes.def" + }; + ++enum class check_ref_result ++{ ++ NEW, ++ DUPLICATIVE, ++ ERROR, ++}; ++ + struct srfield; + struct srtype; + struct sraccess; + struct srdecl; + struct srfunction; ++class fc_closure; + class fc_type_info; + class fc_field; + class fc_field_class; ++class fc_cond; ++ ++class fc_path_info ++{ ++public: ++ enum direction ++ { ++ PRED, ++ SUCC ++ }; ++ ++public: ++ /* The start stmt to clone blocks. If it is NULL, the whole function is ++ cloned (i.e. versioning). */ ++ gimple *start_stmt = NULL; ++ ++ /* Blocks reachable from the start_stmt. */ ++ auto_vec reach_bbs; ++ ++ /* Blocks that can reach the start_stmt. */ ++ auto_vec pre_bbs; ++ ++ /* Cloned basic blocks of reach_bbs. */ ++ auto_vec cloned_bbs; ++ ++ /* Cloned whole function versions. */ ++ srfunction *cloned_func = NULL; ++ ++ fc_path_info () ++ {} ++ ~fc_path_info (); ++ ++ bool collect_blocks (gimple *, direction); ++}; + + struct srfunction + { +@@ -82,6 +124,8 @@ struct srfunction + + bool is_safe_func; + ++ fc_path_info fc_path; ++ + // Constructors + srfunction (cgraph_node *n); + +@@ -93,6 +137,9 @@ struct srfunction + bool check_args (void); + void create_new_decls (void); + srdecl *find_decl (tree); ++ ++ bool partial_clone_p (); ++ bool entry_function_p (); + }; + + struct srglobal : private srfunction +@@ -155,6 +202,7 @@ public: + void add_field_site (srfield *); + + srfield *find_field (unsigned HOST_WIDE_INT offset); ++ srfield *find_field_by_decl (tree); + + bool create_new_type (void); + void analyze (void); +@@ -164,10 +212,8 @@ public: + unsigned calculate_bucket_size (); + bool has_recursive_field_type (); + void check_fc_fields (); +- bool has_escaped (void) +- { +- return escapes != does_not_escape; +- } ++ bool reorg_name_p (); ++ bool has_escaped (void); + const char *escape_reason (void) + { + if (!has_escaped ()) +@@ -203,7 +249,7 @@ struct srfield + tree newfield[max_split]; + unsigned field_access; /* FIELD_DECL -> bitflag (use for dfe). */ + +- fc_field *static_fc_field; ++ fc_field *fc_f; + fc_field_class *field_class; + + // Constructors +@@ -222,6 +268,8 @@ struct srfield + tree newfields[max_split], + tree newlast[max_split]); + bool dead_field_p (); ++ bool dfc_type_change_p (); ++ fc_closure *get_closure (); + }; + + struct sraccess +@@ -243,9 +291,10 @@ struct sraccess + + // Methods + void dump (FILE *file) const; +- bool write_type_p (tree) const; +- bool write_field_p (tree) const; +- bool read_field_p (tree) const; ++ bool write_field_p (tree = NULL_TREE) const; ++ bool read_field_p (tree = NULL_TREE) const; ++ bool write_p () const; ++ bool read_p () const; + }; + + struct srdecl +@@ -276,17 +325,76 @@ struct srdecl + } + }; + ++/* Describe stmt closure to help rewrite. The closure could be either array ++ pointers for the same memory space, or normal data without calculation. */ ++ ++class fc_closure ++{ ++public: ++ /* The stmts for read/write of the fc field. For read/write_change, we need ++ to add convert function for read and write respectively. */ ++ hash_set read_unchange_set; ++ hash_set read_change_set; ++ hash_set write_unchange_set; ++ hash_set write_change_set; ++ ++ /* Record the known special rhs assigned to this fc field. */ ++ hash_map write_special_rhs; ++ ++ void add_read_change (gimple *); ++ bool read_change_p (gimple *); ++ void add_read_unchange (gimple *); ++ bool read_unchange_p (gimple *); ++ void add_write_change (gimple *); ++ bool write_change_p (gimple *); ++ void add_write_unchange (gimple *); ++ bool write_unchange_p (gimple *); ++ bool change_p (gimple *); ++ bool unchange_p (gimple *); ++ ++ /* Call compress/decompress function FN for RHS. */ ++ tree convert_rhs (tree, tree); ++}; ++ ++class closure_helper ++{ ++private: ++ /* The unique id for assign stmts used in collecting closure info. */ ++ int uid; ++ fc_closure *cinfo; ++ ++ auto_bitmap read_change_map; ++ auto_bitmap write_change_map; ++ auto_bitmap read_unchange_map; ++ auto_bitmap write_unchange_map; ++ ++public: ++ closure_helper (fc_closure *cinfo) ++ : uid (0), cinfo (cinfo) ++ {} ++ ++ void record_origin_closure (basic_block); ++ void add_cloned_closure (basic_block); ++ void reset_uid (); ++}; ++ + /* All fields belong to this class should have the same type. */ + + class fc_field_class + { + public: + /* The same type for all of the fields in the class. */ +- tree fieldtype; ++ tree fieldtype = NULL_TREE; + + /* The fields with the same type are in the same element of this vector. */ + auto_vec srfields; + ++ /* Back reference to corresponding fc_cond. */ ++ fc_cond *cond = NULL; ++ ++ /* Record all info related if the class is an identified closure. */ ++ fc_closure closure; ++ + fc_field_class (tree fieldtype) + : fieldtype (fieldtype) + {} +@@ -296,23 +404,80 @@ public: + int get_field_index (srfield *) const; + }; + ++/* The fc condition for a specified data type. Multiple vars with the same ++ data type can map to the same fc_cond object. */ ++ ++class fc_cond ++{ ++public: ++ /* The old field data type for this condition. */ ++ tree old_type = NULL_TREE; ++ ++ /* The new field data type for this condition. */ ++ tree new_type = NULL_TREE; ++ ++ /* The bit width of the new_type if it is a bit field. */ ++ unsigned bits = 0; ++ ++ /* The type class to which all of fc_fields in this condition belongs. */ ++ fc_field_class *field_class = NULL; ++ ++ /* May have multiple fields mapping to this condition, as they have the ++ same data type. */ ++ auto_vec fields; ++ ++ /* The condition variable we want to check. */ ++ tree cond_var = NULL_TREE; ++ ++ /* The vars to hold the min and max input. */ ++ tree min_val = NULL_TREE; ++ tree max_val = NULL_TREE; ++ ++ /* The constant value we need to check at run-time. */ ++ tree low_bound = NULL_TREE; ++ tree high_bound = NULL_TREE; ++ ++ /* Hold all special constant values for this condition type. */ ++ auto_vec special_values; ++ ++ /* Compress and decompress function decls, if there're special values. */ ++ tree compress_fn = NULL_TREE; ++ tree decompress_fn = NULL_TREE; ++ ++ fc_cond (tree old_type = NULL_TREE) ++ : old_type (old_type) ++ {} ++ ~fc_cond () ++ {} ++}; ++ + /* The field for field compression. */ + + class fc_field + { + public: +- tree field; +- tree new_type; ++ tree field = NULL_TREE; ++ tree new_type = NULL_TREE; + + /* This field's max value we can know at compile time. If it is 0, it means + the max value cannot be determined at compile time. */ +- HOST_WIDE_INT max_value; ++ HOST_WIDE_INT max_value = 0; + + /* The bit width of the field if it is not zero. */ +- unsigned bits; ++ unsigned bits = 0; ++ ++ /* The total number of static reference count. The bigger, the smaller ++ size for dynamic field compression. */ ++ unsigned ref_cnt = 0; + + /* The original field of a shadow field if it is not NULL. */ +- srfield *original; ++ srfield *original = NULL; ++ ++ /* A dynamic shadow field must have a input fc_field counter part. */ ++ fc_field *input_field = NULL; ++ ++ /* Init constants of the original srfield. */ ++ tree init_const = NULL_TREE; + + /* All assignments that need to be optimized as shadow. */ + auto_vec shadow_stmts; +@@ -321,10 +486,23 @@ public: + stmt belongs to. */ + auto_vec shadow_stmts_func; + ++ /* The input var that is read from a file, and assigned to this fc_field. */ ++ tree input_var = NULL_TREE; ++ ++ /* The ssa for the input_var. */ ++ tree input_ssa = NULL_TREE; ++ ++ /* The condition var descriptor for this field. */ ++ fc_cond *cond = NULL; ++ + /* For static field compression. */ + fc_field (tree field, HOST_WIDE_INT max_value, srfield *original) +- : field (field), new_type (NULL_TREE), max_value (max_value), +- bits (0), original (original) ++ : field (field), max_value (max_value), original (original) ++ {} ++ ++ /* For dynamic field compression. */ ++ fc_field (tree field, tree input_var, tree input_ssa) ++ : field (field), input_var (input_var), input_ssa (input_ssa) + {} + + unsigned get_bits (void) const +@@ -333,32 +511,138 @@ public: + } + }; + ++/* A hot array that needs to be cached. */ ++ ++class fc_array ++{ ++public: ++ /* The variable declaration that holds the data to be cached. */ ++ tree var = NULL_TREE; ++ ++ /* The size expr to help data initialization. */ ++ tree size = NULL_TREE; ++ ++ /* If fc_array is allocated in start-function, record the ssa_name of ++ allocated ptr, we may need this to create fc_refs. */ ++ tree ssa_def = NULL_TREE; ++ ++ /* varpool_node for a global fc_array variable. We may need this to search ++ for fc_refs. */ ++ varpool_node *vnode = NULL; ++ ++ fc_array (tree var, tree size, tree ssa_def, varpool_node *vnode) ++ : var (var), size (size), ssa_def (ssa_def), vnode (vnode) ++ {} ++ ~fc_array () ++ {} ++}; ++ ++/* A variable that needs to be modified according to the caching data. */ ++ ++class fc_ref ++{ ++public: ++ /* The variable's declaration. */ ++ tree var = NULL_TREE; ++ ++ /* "real" type, for void*. */ ++ tree orig_type = NULL_TREE; ++ ++ /* fc_array referred by this variable. */ ++ fc_array *source = NULL; ++ ++ /* Number of elements, if this variable is an array. */ ++ tree size = NULL_TREE; ++ ++ /* For array of records, this is the field to be modified. */ ++ tree field = NULL_TREE; ++ ++ fc_ref (tree var, tree orig_type, fc_array *source, ++ tree size, tree field) ++ : var (var), orig_type (orig_type), source (source), ++ size (size), field (field) ++ {} ++ ~fc_ref () ++ {} ++ ++ void dump (FILE *) const; ++}; ++ ++/* Variants for different dynamic checking condition combinations. */ ++class fc_variant ++{ ++public: ++ /* New structure type. */ ++ tree new_type = NULL_TREE; ++ ++ /* The function to compress a single object. */ ++ tree compress_object_fn = NULL_TREE; ++}; ++ + /* The class to hold field compression type information. + A single info object is only for one structure type. */ + + class fc_type_info + { + public: +- srtype *type; ++ srtype *type = NULL; + + /* The flag to control whether the type can do static field compression. */ + bool static_fc_p = false; ++ bool dynamic_fc_p = false; + + /* Multiple fields of the data struct for static compression. */ + auto_delete_vec static_fc_fields; + ++ /* Multiple fields of the data struct for dynamic compression. */ ++ auto_delete_vec dynamic_fc_fields; ++ ++ /* Multiple fields of the data struct for dynamic shadow. */ ++ auto_delete_vec dynamic_shadow_fields; ++ ++ /* The stmt that read data from file. */ ++ gimple *input_stmt = NULL; ++ ++ /* The variable into which the data read from input stmt is assigned. */ ++ tree input_var = NULL_TREE; ++ ++ /* The file handler of data file. */ ++ tree input_file_handler = NULL_TREE; ++ ++ /* The fclose stmt of the data file. */ ++ gimple *fclose_stmt = NULL; ++ ++ /* The function with start point. */ ++ srfunction *start_srfn = NULL; ++ ++ /* All fc_array variables need to be compressed. */ ++ auto_delete_vec fc_arrays; ++ ++ /* All variables to be modified according to compressed data. */ ++ auto_delete_vec fc_refs; ++ ++ /* All indivisual fc conditions. */ ++ auto_delete_vec fc_conds; ++ ++ /* The variant of data type after dfc. Now we only support one variant. */ ++ fc_variant *variant = NULL; ++ ++ /* The flag to indicate which path to run. */ ++ tree dfc_path = NULL_TREE; ++ + /* The field classes classified by field type. */ + auto_delete_vec field_classes; + + fc_type_info (srtype *type) + : type (type) + {} +- fc_type_info () +- : type (NULL) +- {} ++ ~fc_type_info (); + + fc_field_class *find_field_class_by_type (tree) const; + fc_field_class *record_field_class (srfield *); ++ fc_cond *find_cond (tree) const; ++ fc_cond *create_cond (tree); ++ void record_cond (fc_field *); + }; + + /* The structure to hold necessary information for field shadow. */ +diff --git a/gcc/optimizer.fdata b/gcc/optimizer.fdata +index ae0e584be..adc2f5399 100644 +--- a/gcc/optimizer.fdata ++++ b/gcc/optimizer.fdata +@@ -1 +1 @@ +-656137356462666463346364333361623035396139323366643262383764363763323530613631653861653634666630333030316562323662346133633566313233326432366139383465346338376266393132363438333765656463366235613461313434346139333334396265306163333731646537376430643834323664623863366163343363643130313435636565623834363361316133393230363937653835653762353534626439663133633538623062353439646237616630333237666136663433386334626639643465303163653832333062643863333664336630376231643964316231663933656333386338656262303734376137313565643963396535653131303763646533393234333735613333633132353061393531333935623539643834373266303861633739373862366663376365383233326139383939363566373061373361613939336537366631353334346563313061373365663635633332663437653136383235343635623234366430373330366336363237623962656465373233346131343264313137653838643334616430346339363732613237623866636364313232613934343261643231386531356430343965303330306332326266336634626163333461643139653962326566303064343333623037313762303934626336363537616339343637306633633066333231613063623339333539376461316632653234353938616133616463623534356232346135666261616339646638373031356633306161626465643665633066616264623965656138613233353331303236363565616133323131653935643363353832366663633434626236376663386335666364333530336433353234383031636264353761616638663031613263353738326438656265623236653338323232386565626464393034633962373835363264656664616439353336623462376139333134656662373033626135336138333136643032636430653334303861616439333736306363383862306439623962646435383931613161653334623666313236366366373962356536656434396231303338646265323666386461366430396262353536313433636132653466623061346164303635636162336536383062306637306438626232646636393462353563366437386531316463383239373361643230643566333736663330656538643461313161306163666361663064383962373736636162323565383865336630333461343939336231366437386265323439626332336166376262623837353163376533353066636339313233323761613766333633343432623331373530376432376534623831333339383964633439653966303663303439623739346133626330646333373831393930316439326233646565623761646664356230336233363230383833616266643463626536336133346664656433373630343738343262373863646131373633653939636430616439393731306435313664323166313530636464393664613738643461356437333564343036316262353462313336663335313763323661363564343330623965363866616534323163616337613964356465333333663739313835623363316462346539643539636435303166306664366135313063333630336531336532346134306234626632313565333739356139613430643630613834353666623762363363326431316538313730316132333165383561666434623564623831616263316464353664323731373332656430323435663836393162646335646164333437366436663633353630373762316161316436333461393763343130396235396237333534376639343063316463386432316330663138373338386632643361386565636665653766643836373930326664346163333162613163396664663531626161326134333762666330393261316637333265326138663036363736333734326230373730613665323665333266326265356534376133373330393466336639623431383863383433643563346265613561326134616334626262646637666163376365333962323036323537333737313534313066323364623937303534623665623237306566636366623763623431373666383236383134326365613136653238623231646339353938373030656631646238653961643434653765313834316231346231323563636633356131376538636666613866303638323362643436636638353665346166323633623861616639363862613835306234643961666662656334393366613066653061383333653965343633653837613263636530633863303861313363636361346132613431363962336539613463313366633934633761363761623039396365393263633638656134376162616631363838616238613137323032663864613035313363353335396432373530363233663234343136346339663435333834656630366537353336306137366434646264303466303633663630386363636337383066316631323836353135393134646139373035323637346164303965323536666335393864636364346433383564616435383862366464326130336536313934636139386438373462333162366230623165323533336263313430386430643661386261633061313631613639313232313734383136336464636231323130336231613131373336636238656635353635626666303535663331363332353338313363396334313631333034656133666365353561643830356565633137346638643739636136376432303761633436666465336232356236653164353163346461656165333038653266336161383966633961323462376262386430363536323932373263343731313562356139336265383765336139643837333966623265386131666561366161333138353261663139303338393733346335313934363761393137633266616431663436343236613231663865636236346133616662623761373633663830623231393063616534633032316538626436633731643261313866666339353836373133363939623966636239333637373764323863663964376134323134613133346335326162363334363334386666396666336534646565616264363966356566323465346538613762623864303766646338666264643466666537303263623162326539653435643130313061386235623631306232303139633962653661303038376162323666343263356465313035393535313531373665623537363237373934366266303434333463303562646535623762313565326631393061643033613838363963333933396162363834393636613136613036656435356339616665636134313034663335343465646335333738663063616330353634316466336438373835326638633037656234396239303730613338336332613839323837643561333235393366323531303032666436616435313137313166383433396531626137353932616538333330653164326438656166343339363262366264326632376564396434396333356565343733383137666333386462313164333630303936336137323863313634613031393931613164363237643262353162376133643935373036306336346161376563383862316365613139376535626535626234666331363163303835336362393535613530636234643633626364386566396132333232373733633230393865663664636334393131643133383736373833653261383661623632343237653937366164386564633433396130313166306430316134653864643263343835653438383687144d4395a52c41e3842b41b03f3743f207264565084e41e0fbdf3bd429b13da15eb33d7e47b93b309daa3890d2d93d0000803f04a59dba191d513e33226b3acb4c80be39e785bb0bb9f63c65cdfb38ac15eebd303d01bb4419f23a55cf06bc7ff1453cfd78433b0bf6febd9013433b3b6da33db673a03b2b9491bd7ac8e03b25dbe63b684e0dbcbb0a3ebdc44800bdbcc493bd93e601bd4b6484bd0a60f4bc60dceb3d70e9debcf709e2bcfbb3e83bbdf4023e0ae182bcd48370bd6b8bb5bc431600be698b0abde7c93bbd7d1e83bcc37df5bc6bc82abc8cb32c3d068300bdd32812be02a169bc1040c7be90c1b2bb666f10becbb08fbcf74b17bc1b1602baf138b43d5224843abfb0d4bd60cdbe3a82a4993c8ae4a63a8c9fa8bdc8a86b3afcabb83a4f68c93e8af795bed2d6ff3e5dc599bec525003f902e14bf7e73e93eea321d3e3bf3d13eb012e03e90fde23e313b7bbeef3fe23ec1eb88be7e3eea3e4d0431bfff85d93eaf825c3e333dc33e4d1ad93e44b8cf3e02d697bec0bbf93e84ba9cbe99cbea3ebef11dbf493be73e1640303d18f7ce3e3e59e63e4f3c0a3f3f3e73be1d3cdb3eda1a7dbe56e3d83e8f333abf6d5fcb3e7268383e67b9b13e156ac73e78c507be519081be4deef33e857f81be2959073fb3003ebfd134eb3ee775bf3e5ce8d63e5cf3e63ead860fbef58193bec791043ffaf29cbe689e063fe7772ebf8659fc3ecac0a33e0be3df3ea97afa3ee85f82bdaf0398be529dfe3ef1a39cbee6cdfa3e616738bf1314f43e2425023e5e10d23efe12ed3e3069b8be5e9d7abe7bc2f23ee6b27fbe498d143f2bf545bf934f013f53442a3f23dfe13ec02fe13e48d3cbbd07bc62be8851013f064a90be5f340e3f2a8f42bfa2bcf63e60cdef3ec14dde3e41c9de3ebc90113f286f3cbe86e5db3ed02474bef976ec3e177231bfd8f5e43e9cd5d93ee0fdc23e3c93cf3e6035c03e176199bedfb0f43ee0f39bbe9c8fe73eded525bf8412e33e7485393d99acca3ec677e13e8a5bc83e5ac5a1be2528fb3ec1b9a2be4bd1f03ed92417bf2ae7ea3e7ebcc63c3b37d23edcfee73eb381c6bb8e48d73b5463ca3abdb1b13a9904c23b8f9948393f7ecb3aea64a0bde43c883bde8ce03afdab793cb6d945bc09d0a33c5bd255bc5c04a03c14a8243d153aaa3c3cd991bc6a70913cc9a38a3ca956d6b9ddfa903c944948bbbb53903ce48761bb6e0a0abca78a8dbb728827bd1d7904bc7ea513bc9f89cfbbdab6b23c30c117bc6dbbae3c19b1d5badb1d90bbb6d427bcd329ddbc3f8f1dbc879137bcb972d63a89bc4c3b9619283b9b4b133a8c65e13b3901103d9c7e643bbf128fbdef865d3b7f258b3b033a123be896e9baee8caa3c4efb683c7998c33c4aed8b3d50a2ad3c7aff60bd678b973c45cca63c73d7c6baf325473b210f1c3c0ec3173bba9d463cf2b3583df487003ced3709bdbb3eeb3b5210f63b41eb303b9665293c7953a73bf34d5c3cab738b3c1f0cf03cd77a813b1d6947bdbf322c3b25a7c6397325e93acb3c953a2266d33ba8ea28bbf4e2d53b3da9383d0bcfd63b2cfa20bd251aa03b2a8ab53b03529c3cbbbdc9bc0e1b053d7c27c9bc955f0c3dd4f3323e0edd023d9f9a0bbd506e003d347a073d4c951bbb7989343c8fedaf3bf6a1233c00eeaf3b63dba43cfc7e533ac83606bdb09646bab2af50ba072e9dbb2548b93c7737debbb6e0a43c5b1e02bcff8d3fbd274eacbba96c3dbd3e8ceabbf3fb13bc5714203cc98936bc98db9d3c83d268bc0148a83c9736483dc820a43c9de221bdb90e983c2fe3943c3a5fd4ba81ef3b3c751c973b23fadd3b4f8b333b37b4b53cd044e53a88a81abd4a7aa7b99dc501bad8cda93aa7c572bcfba0d63cc1da86bc4d3bd73cf09e943c827ccd3c1b8956bd3883c23c8637bf3cab889fbbb5c2d13acb99fb3b4295273cc41dfc3b65be0e3d1dddd53becc235bdf19ca43bbbdec43bd1b8b23b4ac6afba5fd6943c218509bb2bc0763c48c8f73c62c3813c565f25bd1374443c26cb633c1fda053ddebcf13cd9f829bce620e43cf20079bbe584b7bb3ade37bc40043dbd140f3fbc183f64bcb6b97fbc4640e33cbeff89bc9ecce53c627f3bbc92a8a0bde11326bc9058073c296f68bc939699bc7bed253b46d30cba6da4803b995d9aba7292253ca565a43cb7b8d03bd50711bd21df8f3b9a02803b3f98f83933cbb1bb98a95c3ca09600bc14c1753c3184703d7760523cf2f390bdb6663a3c91c9433cc137633b6be27d3c997ecab9060da63c1600cd3bafce23bdae80dbba1ccb88bc058da2bb467e21bce8aab63edd089dbe4f78fa3e1fef9fbe16a1f23ee01a18bfc853ec3ed3a3433d9ce5d03e226ce63ed83484be140466be9106fa3eb0117dbeb6c3133f0fb33bbf9787053fe44c0c3fa01cdd3e379ce73ec6904b3e0ba483be3ef5f53eb7fd87be03c1053f8d4c2dbf6257f73e6866ba3efd92cd3e0945e23e59a1323e199391be37c4023f598595be7f81f63ebe6c2ebfc3e3f33e33fa323e77f7d43ed851f13e7b92053fde4a37be0ba8cb3e5e0d53be7047da3ef3a24bbfddabd73edf0dce3e3c6aac3e8810c63e1a6f9f3ef0b686be44eae93e0b2685bec4bfe73e164c3abf6067e33ec99f4f3ec58ebf3e6252d03ec3eff43ea926a7beb70f063fd659aabed093fd3e73c6cabe1c69fb3e3f861d3d4d3ce13e0a74fa3e4c35063f755379be2f12f43e7d848ebe2060043f9d9103bfaf5af83ef9b1043f8e7ac83ef721d53eaeff133fb67d2dbead28cc3efc3668befd33ea3ee1b941bfed13dd3ea8940e3fcfb9ab3e82fbc33e38e7e33ee37590be93efe83e41ac91be6fe2df3e66f21ebfe293d83e6b91573de8eabf3e3abcd63ea5db103f5b217cbe49c9d13ef7ac80beaa60d13eeaa93ebff204cd3efe974e3eac14b23ef978c33ec984cd3ec2d8a2be4c3dfc3e149ba5bec9f1f03e00f911bf3f4deb3ee781273b690cd33e1ccfe83e7ed9ce3bf7dd993be26ca43aa8abba3b4c8a1b3b2357313d7eb9d73a64e851bdaa101e3b6a432a3a1b5457bb665899bc7993fb3c83c150bc13abec3c749d863df737e23c2f3b11bd8b92d03c1dacdb3c072077bb17e3d1ba1ad1323c254dd93a8bd12e3c5ea3213d4f9b263c158d73bd8c5d013c0cde153cbed5d0bb31ff0d3c59f8ff3a5b2df23b29da083bc4561b3d0491673be76542bc74fd963960539eba288b47ba9f2eca3aa558a73bd043abba8049c33bd32eb63ce799e73b4296ccbccda17c3bd5eec53bfddd8f3c66733bbc6a5e943c11bd53bc14dda13c7fae023dd800a23c034b55bdc354943c0c158b3c9c59a4bb82b70f3ccb7c783b7df2123c6d74be3b126bdf3c3ac9463aeeab5abd4df224bb11bdc1389b6322bb232aa8391f7c863cf42d3ebbbb0f803c049f493da7cf3f3c6c9924bd33876e3ca5165b3c972233ba2a880bbc8a30893cb66825bc1a729e3c6624933d492f883ccd7243bd00c5733cf019833c0308c93bc72d3b3b88a5c43bb947243bf435f23b3916afbbdb18df3bd7685ebde8eeba3bdf2ba03b223b07bcea544fbc0669fd3c81fe8bbc1275f73c5e02723df0b1cb3c51ae5cbd1d3fe33c8980de3c71bd48bba6c40b3dfa96ce3c08c5663d3ccf6e3cc800143d2a08933c0cd93f3b508e9d3ce864d33ccbf2adbbc124033c9a36233bf554ed3b5729123bc4b9a4bbcd83b53ac16774bd3cb33a3afa121c3a7ad068bb01c4603ca49e9b397efa6c3c0395df3b0cea40bb160a703be9dad9bcca8f593b66b085bb8d35a2bbae80203ccd53a5bb30d02a3c432e18bb9795e63c58c982bb874772bd99d64fbb118faabb5d1820bc388044baea1e3d3c7837df3b2164373c1f98673df94f183cfc0d45bdf80b223c2b146a3c8cd5d739622b99bb01a62f3ca437eabbc703323c1cc9e83c66002b3c451f67bd9e4f1e3c61a0233c797bf8bb9576c33ae97c8a3cc5ac933b991b553c41f85e3dedd8523ccc9946bd22335f3cbfec273c24fb0bbbaa53023b8c62623c3f8dd3baa4fe833cf033123d0131633c92ef79bd43a24d3c61be0c3c8f4de1b94e9ed1ba1e811f3cf46e85bb7e115a3cf3d5ae3ceca73e3c3c0c41bd57df253cdcd2213c263b3ebc345d733c81be6f3c8aadee3c8a5d393cdf08703b7361813c7d5222bdde9ba83b5465c138af778e3b2e20b83c9a0c69bcfef7be3c44b00bbc850599bcf7406cbc56d64fbd1cc44ebc73e483bc1f71883e25ab7ebe1807003fd1c286beef68043fe1ad19bfd38af93eb4a0c43ed240dc3efb15e63e487f4c3e640b7fbe4907fd3e871b81befe66173fe60e1ebf5886fd3e6022093f8c18e23ebb31003f63cfe83e27805bbe22b0e83e84c37cbeb6d1f03ee10634bfa8a4da3e3d3fbf3e372dc53e6920cf3ed1e1703e1f888fbebced023f8e4692be6bf0043ff0b121bf525ef13e6ed0763ecddfd23e197eeb3efe3d8abd61ab74be3ce8023fb0d484bead74073fca504ebfad57e73ebd250f3fa009c83ef804dc3e00864abd678da3be815f083f71d7a9bea740083f6d2e1ebff19dff3e8968d23d832ee23eeba4fb3eb51bfb3d7b298fbeacc2fa3e6b4d91bef1b7f03e642645bfe625ea3e460e0a3e602ccf3e7c86e23e7754f7bd306785be01ad033fcc7297bec0ab093f2aa034bfa508033fe0570b3f4acdd73e4e7afb3efd2fb53e5b688ebe8e41f63e8fb791be0563f13e9af428bf8858e63e54962f3e22bec83eb4c5e03e184e94bd78de98be707c073fb7f19fbe0ecb043f9c5d33bf7316fa3efcba313eb5eddd3e5283f53e2e92ff3ec37d61be00ebe43e06528cbe7949fa3e66ab29bf108de43e9b95ef3e8eb3cb3e7a2cd03e26bb71bd334794be2725063f18a097bec754053ff6d52fbfa253ff3e6f3d8a3e9d30dd3e5391f23e376e12bbd12e833ce1a93d3b3c98593cf3155d3bdc9895bbd4d0b73a64e731bdd2869b3bbf23bdbb14175fbbeca5a03c6921c7bbdcd9db3ca82787bbae172cbd01edc4bb4409c2bc296076bbc0953cbc70712d3cfafa62bc047fb93c822e8cbcb852c33c62d2833da0b2bf3c085509bd7573ac3c61cdb13cbef44e3c54518b3c2d402abcfb4a8a3c4f8a1abcceead03c35e13bbce83d47bd35ec3cbca71f32bc3d365c3a71c946bc48e8cb3cae9072bc6483c53c7441ad3dc78ebc3c05a12ebd49a4b13cd1ebb93cb7e544bb8d0f963c7216a0bbba6ec13cffad77bba48636bd1dd90bbc591d06bd82f1d5ba9d3114bc55807b393f0345bbb84e4b3c4788cdbb1e8a423cd5c7873d8ff43b3c4e5a51bd1e82293c0147383ccfc8163bb605d53b54eb323c2549cc3bd62c3c3caf80223d556e1e3cb95c42bd3f9d413c7dd2243c6b08c03af82effb91db7853c0378a9ba0802793c2e6b783d346e5f3c093b80bdd148663c0d6e5e3cfc6844b9f2e1cf3a7d18613b3b8734b953bb413bbdbc943cf6a8323b88962dbda482133be6971d3b12ad30bbac4f143cd94ca43b87984c3cdbf16a3b9dc7b13ce8e6633a286b77bdf5c7373b162fa73bce6c1a3c33e88cbb4363093c1c049fbbc6b8373c95c3953ca57d343c76441ebdbfbb0f3c3f56013ccae5863a4ddc20bcfcc7c73cbafe29bc10149b3cee14873d03ae953cfa1e36bd2aa5863c7b79903c5c5125bceebc103c7d270f3b0b70023c42fc8b3ab278eb3c84d4fcb816012abdbfd66639ea10e4b8cc1e11bbed1c073b1c3b803bd21e9a3ac633b53b2f52083d9c50713becd382bd6714543bc395063b79e0ae3b7b9e4ebb2d0d253c44b83abb4d6a393c23814b3d6e861f3ce06cb6bcfe8e0a3c511f173ccfa56c3b873eec3a1c34e53b4381d0394e17d73b2c49ec3cd516cc3bb9c42bbdd3fa933bf49e793b20936a3b5043823961cf3e3ccc562cbaed9e403cbf96063d9c28353ccb3f5bbdf558163cdfc9fb3b5bf28d3cd425acbc618d163de421c1bc0a86163d09ec2e3edc5e083d3d898cbdbec3113dc213043d3d38c4b9466a44ba1efb2f3cce0f9c3bfaf92a3c5388693d991d113c7ce746bd74ed1b3c08d21a3c382fb03c5592a0bbccf6563c24ee8bbb5908563cd779163d06b5503c0580fdbc91d9423cc241493c356d15ba05fd523ca5611cbbc379493c66e7f33b0c1f073dc62fe0bbd3d455bd42683fbb482ec2bbf0d0a83ee99383be0eda033fa74082be4c841c3fe391f7beb6a2063fb386103f5f2feb3e6bafec3e53dfbd3e983a56be1528eb3e1f3085beb3fdf33e12fe3dbfed47dc3ed362e53eb34fc33e0b0ad33ef34e0dbefeac85becdbdf43e75bf95be249c133f67db35bf21d8013faf1a133fd964da3e7c4afb3e8408823e7fdb8bbe77f7013f8f1595be6804053fa32217bf4881fe3e6341823e55beda3ed8fff53e0002db3e85dd6bbe93cedf3e11ee68be5e26d43eb9f24cbff431cf3eca59693ed2afaf3e60d4c43e81bc54bd13eaa5be9661053f5ab0aabefc59023ffba229bf847c003f92e1f53d31bae03e5082f73e9e01b63e759a8ebe7133e73ea36590be9342db3e680d37bf872fd63e7d461d3d8813be3e0816d43e5d82d53efaca83be81e5e23e01ad87beb5c6d53e2bd731bf007cd03eca1deb3dc447b93e4013cf3e154eff3ea8fc75be08b8e23e116189be36dafd3e4dde22bf2458d63e2faead3e25c6c73e0a54d83ed91bf23e02e993be25bfec3eaf6e96be0defec3e48a621bfc4c4e43ef7ad213e4974ca3ecda6df3ea114383fdaf953beb966d23ef24e4dbe291ce93edef430bf7100c63eb01ccb3e95b4b93eda29b93e8a49ca3ecc00a4bedfa8fc3e2bb1a5be2fe1f03ec66f16bfd2b2eb3ee1e29d3c966ed33e198de93e1904c63a3ae604bcfeff653c5d0bfbbb80629b3c76985c3d60918a3cd08057bdd35c933ca83e6b3cb9d066bbb36d47bb05eb803c4bb4a1bb48c3823c9bcc033d9e47763c32a143bd3cb3613c960d683c8189d4ba9301293cbd3f263bef5a323cbab8a63a3d59e83b56dc643b234648bd7674be38ef94443a9df5abbbc5be603c02d2df3accaa403cb260ca3b856f0e3d2191ceb9dacf39bd1bf821bb5766d73bb84111bccc86c93aacf4613c8371e5ba12d1533cf7936e3d66c8023cadd91abd5d2e313ce4e92f3c223b19bc5ac9183b1caf983b0cc0a73a64a1153cd31c0c3d1b5d923bfa1f24bdbff1a33b73bd7b3b8bd9693cd6ba7bbc2d09db3c758c83bc3085003dd3330b3eda22d93cf0ae09bdc729d63caf83d93c05610cb9a04e24bc8d2d9a3c82923cbcaadca33cb17a873d11cb9c3cf6b041bd449f913c7196923ce771feba39f6edba18b3143c1d3807395c21113cd00b253dfff4303c962c50bde69c1e3c5710de3bdb3c8a3b096e2ebaec6b373cece9383b9de8433c3b4ce13cdad3143cce5928bd2b30253c4de61b3c3e5925bc0d53e5398ee9f83b50c71b3a51bde63bd55e813dfbd6bf3b938257bdb3e9943b152ab03bc25b1cbc98f59c3b75ed303b8ec63c3b464e713bbe07743dd60e2a3b004d1bbd36ec863bd63c1c3b6bdf53bb3c309539a8c8743b973cad39656aa33bbda3aa3cc2e08b3b98d705bdc680563bfab04a3bf73b01bcd76e913c604527bcaec1813c9c9ae3bbba0438bd285216bc94cc29bdfc310fbcaf4c42bc138f3cbc7c9d66ba60164a3cdf580bbbb5af403c7261863dcabb2f3cec056abdf8051e3c83f1283cb600b9ba0b9fb4baba50213c095a5039ac32513c8fb07e3cb9371f3c50a40cbdf843383cd969f33b1c34fbba0cff823cf9d281bb7aeb6c3ca95f09bbc72cc5bcb93379ba67f4d9bc132aa2bb94a6afbbc79f74baf9082b3c7c42053b4adac33b969a483b1dd779bce885103befd8c9bc60d7f13a8a93db3a7055c6bcf0cbd03cc67c75bcc196ce3cdfda89bc85e3a2bd8aaa95bc66897dbc339896bcc08298bcb27911bb4fe4b23b3c89513cd8bb8e3bc0df4a3c58980a3d549f0b3cc3ed54bd565c563b3c35843bef71a33ab4b026381dab4f3c8822eb3b9f5b2e3cee511d3d170a113ccff352bd2d41ee3b321aee3b6479c43cab76bb3c2a3315bc329aca3c2df32ebcfef9fdbb244a8cbcde3444bd365a57bc33fc79bcfe524f3eabe282bea97ffe3ee0ea87bed85a043f4be633bf908bf23eec11043fd4d8d53e307fe93e2094803e506d83bedcbde23e1b2f87bef844dc3ec77244bf0c1ad53e572e163e5088b83e047dce3e05f512bd894199bec01e053f30039bbed405013ff1cd35bf4e51fa3efd5b213ea77edc3e2ca4f33ef42bd93ef78591be6cf5f03e362e93be94a5e43ee31d24bf596be03e801dd13d9d6fc63ef969dd3ea28ec23e365070be2d14de3e2e1076bea661da3e850350bf606dcf3ef091723eaf36b53ee0aac93e73e2033f75f074bee398e83ed00681bedd7bfa3e5c7817bf1ec2ee3e2915ac3ecdbdc93eff25d93e57c8a13ed2bc91bec6adfa3e5af895be74e5fa3e9fe712bf1f7af83e8a3c543e818ed33e749dec3e1dfe373e30b09abe2467053f98de9bbee5e8f73e1c4120bf6ba9f43ecfc0c83dc7e6d73e8d40f43ecf2395bdb64287bec2c2003f7f378cbe3318063fc8053bbf5738f13e54fdb33ef711cf3e374fe33ee988ff3e3e2284be68f1e53e0abe89bee926e73e1ba927bf55aae23e50a6183e6633c33ee12bcf3e0df5713f4249f9bddd1fb53e513604be7ca4d73e8c1049bf475fd33eaac3353f74008d3e15f7913e0404a73e064991be01fa063fd0c293bedde4023f7f460abf9c81f93e9408353e1f18dc3e3858f73efcf3d8381165bb3a1ed4f13b66faadba6c05063c7346ec3cafeddc3b45fe41bdf10bd43bdc3fba3b9fbaa6badb92df3a26b8523cfb88553aaf90453c8a984e3d6898463c298d1fbd98062c3c9bfc163c1cba5ebb8ec0eb3b5b56e33ba93de83b953c6f3c42588d3b1f74293c44d641bdd26cf13b2f2f9c3b8eeacbb949b7b2bb1a118c3cad7ffdbb8701843c78c8823d01cd843cd9b152bd70f3683ce2ad813c7212ccbaec4bf7b9ca32633c53adf2ba37bf673ce368703dbf3ef93bc5774ebd734c0e3c2eafd43b1d1166ba9ac4243befb80c3cc95518380381fa3b0947f93c49f6f73b44c628bdd475d03b6e0df33b43ecf4bb5077083c5064b9bacd2e153c9732e3baf237133d999cdfba632885bd1b7666bb7f95a9baeec6babbe3e49e3b3735093c8ae9883b28a7ac3b56fd633b33ccbb3b570d0cbd10d3453b81ea883b829da43c0180e23c1dd790bc5b77dc3c925e67bcf0fbccbc8dd6b9bcfc2463bdf94980bce22cafbc902ba4bb28a314bb5766383ccac575bacba2123ce74f463ddd9c153cc43136bd77f4013c9d15143ca77fa6bb6cc6243b4b2b443b7c95ed3a29e8043c6fe59a3cf516b03bafeb2ebdae27933b25d6153b8d463aba796e2cbadb1b533c1c11c4bb5a7d803c19972a3d7808623c1fe637bd6fad573c4b5b2b3c6eb3733bdfd29cbb02e38c3c6401e0bb72e1973c02ab743da4fd933c87250dbd83eb5f3cb8d6773c7e4514bbe5a1113c1e4f013c77e4543cc590453b93870f3c78a337ba301f52bd5af99c3a853e65bb63e106bb8b42eb3bdbc01f3b0c81e83bfa8de0b91447893dd66b2b3a928a4dbd9da10cbbec6f0abb9e397abccf5c173c0f0c2f3be3d7f53b16b0293b53d0cb3c95db6f3ab36af1bca4769a3a01d4a0baad745a3b260265bcc3b99d3c7b6549bca87fa23c147e453df66d9f3cd7ba39bdd2049d3cfb239c3cc7bb673c45b3a1bc36a3313d580191bc70291a3d41f8493e28d1143dc25c08bd013f2a3d05af213d61f820bb113eaa3b27c4c03b56e3d53bbdbbe33bb0dc203bf4e3d03b491469bdfe07123bddd1063b179a0abb86a584bbb240a63c599fd5bb495f8b3c464f443db62c793cd51220bd4ccc823c5af24f3cbcef4fbb83b4cebb9c92663cb6c7fabbefac6c3ce308783d2d5c533c905d2cbd78ab3e3c3fbe4f3cfaea4dbb49ee5fbb25c5233cef6b833b531c3f3c66ab2f3d5edf4e3cbb9210bd9fbf0b3ccf49193c51fafe3e90c756be715adb3eed0d49be25c5ed3e64503abf318bd73e99b8e73e6b79af3e420fba3ee950be3e07008cbef683e93e99c88dbee548dc3eed4a3dbfacaad73e77d18a3df25bbf3e9445d63e5d96123eec65aabeec39073fe313acbed47c023fb3ef17bfa431fd3ed00cdf3cf490e03e6703f93e004bcb3ed5c6a1beb268fa3ef2aca3be1898ee3eb1f111bf6baee93e84cd133c8a79d13ecd3de73effab5bbd2daf95be81f8063f874ca1bebfdd083f8c9c25bf21f6023f4e4e653e042ae83e537ef43e40d452be2d4e56be66c0fa3e7ad486becfca143f22f03ebfcb79003f73ab523fb6d8d13eeacbf33ef57611bdec8a96be17ad033fc25a98be4623003f61133abff137f83e56b62b3e9665da3eab4cf13ea1339d3d765973be63fd003fa5225ebe73a4023f223e46bf07c0fa3e1742153f6e6ad33ea276e13e5393c33e41a99ebe5eeefb3e879fa0be8360ed3e320016bfef4ce93ece150c3d34b9d03e157ae83e7cc1313f72685dbec4c4d43e7bb05ebe9657f63e2b2d23bf46f4e73eea41023f2d3db33e3de6c33ec32ef93e79894cbec235dd3efa0c80beff1b023faf442fbf7df7da3ee041f53e6556ca3ece48d23ece5906be323b93bef7bd013fd4e195be642a053fc2c439bf42e8ff3e86ea6e3e9581d73ead4ced3ee3ffa53c7981c4bc2fe9fe3c2ca2d8bcd059093dc6c3193e81fa023df8b6b7bc09f2033d79b8ff3c49f32a39562805ba7df3053c165180bbbcd0123c40d1e33cb49be63b564903bda22ae43bf8dcd43b28edc73a4a1e723c347001bba98a5a3cc3bb9a3941c603bceecf38bbe8d652bd101354bbb677d0bb93ac88b9e093f03b2fe2a13ba98afa3b5485cc3b48564d3ce6af1c3b989844bd6033003b5990803bd374b63b69edd7bb278c9e3ce958ecbb98c2d53c827201bc0f529e3c174b88bdad15833c416b893c4daf89bbbb16193bab8f993b9dfc253a4927f73bcafa553d0de1d73b59cb48bd2bccdd3bf5f0803b81f4bbbb480843bb8bfe6b3c10542fbb2ca88f3cbf488b3de92c8d3ca05422bd97e1813c92924b3c9b449abba9f73e3cb3f69cb90173763c750248baaed0b7bc915b12bb4e7c54bd70120eba414ebbbb13b6993b035b9f3b9b30003cf3fd003cfe37133cd137a23cc222e73b4bdf9cbc3907bc3ba4779b3b15cbb83aebfbd0b92e3de83b42e793b910661a3cc1bdee3c18b10e3c708314bdc7bcb33b64f1c83be5300a3c7965a93b7086d53b11fb083b321bd33b4aa3073daa59923b88101abd06ea0e3b1fc62b3b2d4f58ba12d5ab3ba506cf3bd5bb263c3877c83bf6ba753d244e8f3bf1813abd39185d3bd2e57d3b8276f2ba6709933a02406b3b62d8003ccd922f3c6b14913cdfb9a23b101c0bbdc016893bdf0e533bc2afae3b475ceebb6daf9f3c1898fcbbcf1e963ca4eab23ced34983c43a94fbdf4b57e3c859b903cedb0c93b6632dcbb6a74473c1d207ebb69dc803cb0c53a3d2edb573cd72a26bd2792533c0be43b3c22f1a7bbcc598f3b4580393ccc597fbab1ad323c0d642d3db418fe3bcdbe1abd144c0b3c251e043c6ff7c038b3fc19bcf1758f3c170f13bc55fa873c7cb63e3d4697913c162a2abdd9b3793c797f703c46f85f3b6ca3ccbb876b933cbd24f1bbca59923cb7e1be3db0a5993c757365bd3f1a723c19e48e3c98150d3d5ce97d3c696698bbb0af533c1d515cbbccc11e3ce72cc6bb2b555ebdadf9c3bb1574dfbb3211dcba1085913c4c0e38bc0aff843caaf59ebb773309bd361dc0bbd991abbcc9284fbce6b04ebcb78f543b472d26bbe403883c90229aba79c2893c44025e3d09d63f3c71a104bd8af2273cf4af483c94822bbc54b8cc3bcfe0e739245baa3b7380fd3aefd9803dee35a638e80d49bd70861939e25276b9b3746b3df0cf99be4a85013fbc149cbeb2c9fa3e395836bf5a68ef3e65b7d33d6180d63e70b0ea3e84046d3e665891bee293f63eab5895bedc01ff3efa2324bf050bf33e3af05c3e5277dc3efa8eee3ead753b3ff22e34be1f33ce3e5ddc5abe523ce83e9a032fbf8f88cd3e50ebef3efc00bb3ed07dbf3ef885133f7b5159beb055e23e1aa26fbeb22fed3e1a3e2ebf16a2cd3e2a13b53e0f88b73e619dc53e6681f63e14e591be1b05fd3e1c5b92bef153f33ed4570abf29cee43e011e063ed901cd3e4469e43e2b3cf03ea30a66be5616d33e6ed568bea2f0de3e4bda41bf3da6d03ef1108a3e726eb93e0556bf3e1cddcc3ea1669abe0ef1f63e7bf39cbe8e07e83e905422bfe4c0e43eb492a13d2010cd3ec405e43ef1d1ae3dfa3394be6873093f6e5598be8ed40a3f25161bbfb5ab063f2bb9db3d4cf1ea3ee06ff43ea95a20beb6ad9dbeab13043fdc539abeaafa0a3fe7272fbf9e84f83e6e168d3e7781e53eece7f73ec8ce2abebc067bbee90ef93ed53186bea243093fbbe73fbf7e21f53eb51dff3e3b3adf3e6ec8e13e8c3b82bdb42460beab37f43ebfad94bed1b10f3f0c213cbf2383ed3eb8671a3fd463ec3e0cacf63eae35cfbdfc817bbe1854043fb85f94be2348163f22ea30bf9e4a023faf214c3f3f85fe3e6c9ef73e355a28ba4c46e53b017e753c1c87c03bb5cd5a3c2838803d14e5343c6861c3bcbc67133cc47ed33b35432f3be20f213c9bff823cdd53483c42cb7d3cd7ecc13c147a113cdcac96bdf93e433c01fb103cf2ec67b8032b1fbc76d99f3cabc747bc2974993c1376ac3da7358f3cafe934bd3fdb803c6c62943cbb5343bb2b6496bb8ec43b3c6236cbbbd0c62a3c28b4833d90e7243c73e35fbd115c123c48e9283c1a7d903b70063bbc1933853c7a7d1dbce9ad8b3c4fd20f3dc7d9813ca69f20bdbf14803c57ab7f3c9e6d093b7a2d093b4302ea3b71b3afba6f23163c5642c93c1d8bd13b0f0004bdba12ce3be107a73b50cd62bb92904b3bda05f33bf1b6e53a94a5033c7894953c81b8943b58933dbd97f2a43b48ce4b3bdcd3593ce3e559bb69ffcf3c759780bb8354c63c51a8f93d48128c3cfc8107bd7246b63cab7c943c5f9ddaba8bfc18bc714da03c576349bcf85d9b3ce84e9c3d2d09933cea4c18bd8afe8e3c2cea923c57ef41390ba48bba674fba3cb1264ebbce30be3c3bbaec3c138bd53c6f5990bd9be4c93ceac7883c02898cbae2e441ba0e1abd3bb2f5d83ae905473c4056493c4e330f3c28c50cbdd2b7283c5bfaed3b1f638c3abd2cb53bed0ee33b4416b63bef75073c7a38753cbd91b53b954528bd3e33883b5456a53beb300bbb350e223be1e5cd3b23ddb53a281be93b6eac4b3d0f77cb3b2d1da4bc19348d3b71add33b25c33abaff77a9bbb1ab9c3c42fa05bc597d893cd1002d3d5a22853c90a4dfbc0d71853ca7f58a3c6eb77d39743fb93bbbda9f3b16e8943be58d9b3b6730fb3ceab25c3b229453bd9aca833b4aa1a43b96f507bb02dadabb1dd25b3c73ad04bcb1f24c3c9b72693dab604a3ce904cdbc8ab0333cd192473c502a5eb932c9143c9b6c013cdd07263c16cf513c3848c03c8d37f23b10fe15bdfc5e6d3c77369e3b7d18ce3a9601d2bc8c6e113d1ef3d5bc8be0183db487b03d05ba0e3d330441bd93390a3d05830a3d7df54abb3c1eec39ffe3823b19d4643b0ca3993b4201613c87817a3be0aa11bd68463c3bee21413b569fd43acb4351bb32323e3c1130b6bba088403c54cb133d71f2583c650a7dbd366e293c536a023c74d3b7bb9c6d163c51feceb9dfeac93bc622dc3a1c951a3d5e7681bad78f20bdaf9843bae03ad3ba6f648bbb7558213c8c35003bacf80f3c9d37633adab56a3b47c2e8ba429780bd8bf9b53a6d12a4b995b337be64908ebe376e053fc7a194be0502013f392b41bfc7fdf13ee94f873e230fd93ea502ed3e751cfa3e23fc84be96cbe43e558f87becf28d63e7a5d37bf8666d23e3e12cd3da892ba3e2ecad13ef3b3e0bd481a99be552a0a3f85999bbec033043f8f732ebfdc82fd3eb766413ec4cee13e152afa3ed5fbc9bdf7a87cbea07f023f212e98be70b60c3f99f531bfe83c023f65bbea3e76d5e33ea863f63e10c643bdcae24bbece820a3feed88dbeb8a60e3f20903bbf721ff53eaff42b3fe856d93e8b80ff3e4c3d6c3e2ae494be964a023f5b0ba1be9672043f47cc11bf8f93fa3ee0cc2a3e33dada3e301eea3e83ade5bdd7eb92be2233f93e43ec95be8e2af33e1b4445bf0ddaea3ebdac2d3ef7d0ce3e1f51e73edee566be687674bea12d0b3f26429abe4c2a0e3fd4a73abfd5daf23e32f0c83e924def3e2284fe3ea24716bedacc96bed509083fde4e96bea6430a3f6d9a28bf534fff3ea56e833e002fe03ef2c5013f8a6cee3eb0698fbeb11df13ecbaf90be5aa5e03e8b8824bfa06fdd3eb118fb3daec7c53ee528df3eeebf65bed1b58bbebacd063f7390a2be63ab0a3f5ca425bf4a3d013fd665c13ec5fbe23e1b66033f7013da3ec0e782be5132e63e12f686bef829de3e8b6d2fbf4b60d63ef2c00f3e659ebd3ef7e6d13ee10ed03bc67f0f3c45741d3bb0daf33b5e39df3b5ea9953be727893ba42d13bd2afdae3b0be0f93bb1a2223c6d45e43c17da55bcd75a0e3d3c3315bcc99f73bdf2b34dbcadb347bd863263bc4a6e89bc65b616bc4244fd3b32023f3b1d43d13bef37a53b98fb123d971b1a3bf6eadabc30f15c3b3ace153b58fa5e3b7a05a4bb52f3323cc3bd98bb9696313c5b19993dd413293c2e094bbd4e47113cdadf233cafd7603b4c1ed6b8dc521f3c63c984bb2103193c7aae9c3d50640e3ced2c4ebdb62fed3b1a9b0e3c219f31394773703bfbc3bf3b7ef7813bfaafed3b08ff4f3c1a058e3b8a7247bdf9f8a73bf284e93afd4eca3b7be13b3c7d27bfbbfcc3413c4cf90dbb59ecb0bce6f836bb338985bd3d3a69bbb96002bc4396583adeb88e3babe9f53a68c2a83b90d7433be703523c6c20dc3ad1f31fbd4769b63a3f9c4b39cd774bb8cae544bc5967cb3c06f107bcd6d0d53cd829333d41a6b93cf5f544bd1104bd3c12a1c53c72fd99ba591fc43a7149c53b53dd633bf1c42d3c6eaf003d6568a83bf17a49bdca33c53bdc83f73b32946e39579123bb74dd2b3c8bc165bb2f85a23cfb398f3d7e6c2a3c843b5dbde50f0b3c67941a3ca7533abc59089d3c36c3babbc0f7a53c898721bc100554bdb9474abc4f55c7ba098932bcc25b5fbc6748a13c343f8bbc081a2a3d8a2591bc6225273d8a724b3ec527363d916a51bd51a1213d32c6133ddd415638a4aafcbbd6b8933c8247e3bbe3c0a13cd7cc673dfbb0663c007a6bbd67177f3cf1ad763c427fd03b7768a6bbba29a73ca4c80dbc9809b43c10f9783da206b63cd5b072bd0066893cb03c933c7f80143b1ac112bba4cc573c177680bb84c6723c20c1ef3cb47e433c100043bd006b2f3cbc46403cc9ffbf3ab881703b80e7f13bb2c8863b27b70d3cf15c393d0375343b1fe28dbdcab2cf3b4103e63b15fbe6399b7a28bcc41a9c3ce82757bc40cc913c12ca8d3de1278f3cb9e22dbd6ac1833c5c308d3c2693b2b9146a553bc2da3f3ca5c9b4ba5377503c22ac393d17be303ce29c5cbdb02e213c9cd6fc3b6307d33b70b6a1bb10d6843cd567acbbdba95c3c5583423d8129633ceadfbcbc3d8c4c3c02ee513c71f21d3ba86b813b0ff95b3ca70c603b11131b3c4dbddc3bc8ecfc3bc7910bbd621dc43b5b669b3bdd3236ba1c6a22bcc7db8d3c52901ebccc0d933c6402343d20a3803cd9a321bdbf9e6d3cd0b8783c6ff7febdd72f8fbeb78f093f69ac9bbe0b810c3f90f323bf600c003fcd33bf3e9193e83e0d03003f72ace63eaa6c84be1f57e53eefd186bee796de3e844839bff336d23e99aa073e2f92ba3e47c2d03edf4bcb3e5cd19abe3ee8f53eada49cbeeeaae83e374019bf233ee43e4746553db2d1cb3e233fe33e8726e93eeee48dbe66b8f03ea2bc8fbee768e13e522b26bf6932dd3e52e3b83dc097c43e93acdb3ed827c43e6d8d99bef31df53eb44e9cbec848e93e8dd125bf457fe33e54d1693d7e8fcc3ebf9ce13e4dea28be3b2f83beeca6033f080f8fbe69fb133f473d39bfb70afd3ef5fa323fd174d63e7598ea3e46f87cbe51e663bee731013f1c40a3bee01a083f662637bf387f063fa480163f9417da3ede77fa3e4d7ee43ecca487be8528f23efe458abe3bb1e33e7e172abfe295e03e86b4053e7d58c33ee5a1dd3ea22de53e812d8fbe8a61f13e0ac292be3492e63ee25826bf714ae03ec7a1eb3d285ec83e21a0db3efdb0db3edb2980be5ee4db3e3b6f83becb59dc3e61d743bf9e39d03e0572563ed04fb53e7303cd3ed31b053f1a1461be3b24ed3edb938dbe66d4f53e67e320bfa5f7dc3ebea2963ecf62c13e3c6bd43ef01eb43eb41c5dbecd55e83e46ee89be0a1a013f087d27bfd2fafa3eaca30b3f476cd63ecab1d93e7e9725bc0aa6923b86da413c0203193b5e7e673c7d50253df6e9363ca01e4fbd3481453cef6ad83b800e913cd1685d3c107bbdbb18eb313c57317fbb8e55233debb5bdbb863567bd2850bcbbad13d4bb915a783b3cd9aebb70534f3c4e62d13bafe06f3cc4473d3dd4005d3caa5922bdde91503c94ce323c6c72c9ba0449b33b0e88c93b4cb2603b033fe73bb935dd3c42e8e03b53f621bd5561983b2efc753bf430caba702d4bbb00841f3cfb49713acf192e3cac562e3d260b0f3ceabc30bd7b25f53ba67a013c87a3e2bbaaf2263c37417a3c7484b63c0fd47d3c83f36f3df7c9d53bdb981dbdf112963b2295473ccdf5c1bb65a4423c3e59caba46581c3c9d6edebad1f5babc6cce85bba5329abddd9e61bb5af783bbf23a0d3b800ec9bbcf327c3cfb47c0bb7adc703c76c75f3d7a92623c127c1cbdc5e2503cebc76d3c65cde53bae1696bc308ee23c4855aebcb7a5dd3c1be8ab3d7244da3cc1c500bd7f44ce3c439bdb3cd47314bc56d26fbcc8d1c83c2ec16fbcd1b9d63c500b953da937be3ca49477bdfddbb53cfacab83ceca2813aa65c283b462b443b001b303b5219883b9b2c69bcd1fa3b3b6a4157bd25cff33a86d1733b7638723ca39df8bc31f0463d30040cbdb2063c3d17f85e3e7fff3f3dc244cdbc03b6353d22542e3d1d7dafba2e2490ba394d413c37a186ba2f70383c4d72613d5156d73ba180f4bcc1d2143c78010d3c4a3125bbe706003df3418dbca9bd033d0efa2fbc06d36cbd8f558ebceb3734bd801486bc0cddb4bc406612bc4227823c76d9b2bb53197d3c3df981390c2a883cc7d35fbbce8969bd179ad8ba4928cbbbe034ec3ae66c83bba9e3313c9215cabbcacb4f3ceed13c3dda43483c768817bd7f9e373cde071f3c98165cbb49c9e23b0661753be990bd372c4fe93bdf4acb3c5385823b24b868bdfa2d5c3b1ed5863b0019fabcfbc00c3d48f179bcfe80fe3c86da8dbc39f5a3bdc936abbc0d3891bcd834b3bc837dc8bcd4ed0b39513a9cbbec06a13c93209cbb59daad3cf298bc3df1e5a73cecabb7bc324f923c1ba7a33c6b6d19393bc7093c3050a33a3b71bb3bbd4aaf3b9742ad3aeb0eea3a193e37bd3377a73b8e7ec53ae00107bbd15c83ba0c6ff63b3b5e7ebace75e43be425843d61f4d73b05e2e6bce263a93bc565dd3b5a00b0bb6f1083bcedb9ef3c5a57a1bc699eee3cdff4443d633de73c655e39bddcd2d83c8d39dc3cc398ca3e30f799be5f55f33e35739cbe2ad7e73ecbdf1ebf6f40e23ee2a67e3d5f78ca3e55f2df3e843761bece365dbefb42003f64bc77bef20d103f607440bff1b4f53eca680b3f8539df3e0234f43e937096bec00968be8efa083f20d277beea60163fddf130bf06b8fe3e82a3183fd38aee3e92c0f83e3bd408bef2924dbec2e4013f19ec5cbe02d80a3f419c52bff40afe3e84dc143fe2fdd83e29a5cf3ef803ca3e1fab9dbe42c6f83e51c09ebe1e74eb3ef50b12bf7624e73e8676123d7719cf3ecab8e53e5eb7c63e7f5d98be361cf83e2bd69bbe5d9fec3e885722bf52cfe53eda77783db381cc3e28c6e23e3374eb3e21d688bef36ff73e7ef38abe84aaf13e6be51fbfa438e73ecc10703e2cb9c53e5b45db3eac07f73ef8af6cbe561dea3eccd584bef612fd3e5af929bf0f33e13e1156af3ef905cd3e3e60d23e461ac53e17749bbe00ebf23efe389dbe19e5e73eeded1fbf2cebe23e24843d3d12e3ca3e9fa3e03ef894283e365371be2621e63e7c137cbeb5bb053ffb7c39bf5e23f13e8ffb1f3fba81c33e1a22d63e2c2152be8ad160bea9c9093fb7d78abe9693143f72bd2ebf65d9033fca360f3f29dee43e622edb3ed02b52be21f498bea13f063f359c9dbe4282013fedee39bf9a96f93ee155693e017ed83e3bc5f43e3c7cd23b157c01bc7e4d513c87f017bc8a814a3c50586f3d79f9433ca6121fbd1f83353c3356403c558cbc3a889492bbd9f87c3ceedacabb514c6c3c1c71883dfd31693cd94165bd84e1563c9c2c633c99e71d3c623cf63c0ff7fdbbdf43023d0695ebbbb0c997bdb63d0cbc5f589cbc72303abc9db459bc5bafa03bad5e10bb5660f73bdcc52abbeeef133c1acf313dcaa2f03b21c216bd7d90e73b8bf3fb3b22b61c3cc686603d2e8789bc1d2a543defbdd5bc95240dbec970debc8b5c97bc527ad5bce64df7bc0242da3a826aa5bb7dcf4c3c1f06bbbbed98533c0837513dd80e463c641a39bd29263c3c2701403cce511d3a4960b53a0770df3b01385d3bbcc5d63bffc119b880c9b63bd29235bd6979a53b089fa23bcb94933b23be4abb0bed663c91f3873bd52a493cf181853d41b23d3ca3f032bd3bca323c79ae443c160176bbdc7390b90c90ca3b9ed10cbbd19afc3ba719f13cda02be3b8d495bbdd26fcb3b986ab93b1ee9fe3aafca30bb53910f3c6571cbb894f9443c1a9b583d53d2233cba7468bd2218353cb656043c33f7823b443281395f5f713ceb62153ad8ba4d3cd4e85f3d2a3b2b3c583b81bd26e3e33b301c1c3ce4f8be3b592629bb97fa283cef297dbb3aea443c0713373d7b733e3c7a7d06bd943d173c1d9d123ccacd4fbb259aef3cac93523c7170c73c8fff193c819dc93c9f3aef3bd2ef42bd71560d3c9fcb3cbaa084ea3cf5eb293d313ea83ab2a47e3d4ea4713c639f9ebc53d7383cc9f23abc912fa6b9c37fbfba2f5c9c3cf2435bbce756093d8dba75bc7472133d8371153e8f580e3df2a9efbc49b90a3dc2dcda3c89499cba2e1a35bb57957f3c8f919bbbd3a7803ce376303d2a46583c823910bdd59b4b3c41db153ca3245c3b5846893cd9bdd33aed3a8a3cea89583bfc1ba3bc04d0e3b9daf306bd3ad9fdba4bd896bb9dcd6a3bd53faabb5a18553c33e7adbbb04c4a3ce84a643dfe4c3b3cdceb0cbd9119273c58862f3c070a24bb0d62643c955fc1bb466b4e3c78d2a5bb6c98b53c5af18bbb2d2c4fbd14b3d8bb4af60abcd7999f3be7722bbb5628843c9ef3c2bbca4aa23ce11ba43d9de27e3c3ecc54bd07aa7f3c3b3c753c50ed473d1cc8b03ca4ae6fbca0e0ab3c6f8e68bc0b89c2bc709672bc8e96efbcad4884bc197e8cbc88ad0abb1e84a2bbfa37913c4fed96bb095b813c7cdd893d585a803c22fd07bdb11f593c44bf623c8126713fe675cabf69320240baa8d3bf09da0e407fa3a3bfffd00340ba7c15c0ed5ef73ffc72f73f7e93713f0e20cabff9a00540fd5fbfbfd2331040ad5f9dbf1a5f0540e7b31cc07341fa3fccadfc3fbf516bbe49806b3f7352b2bf8ed8843fb13fdabf85b205306eefc8bfd21f6c3f5901abbf05c0a6bf7c29adbe5b6f7c3f1ec0d3bfb1228b3f86f0dfbf7abe103ea357d4bf9bf1743fea1da1bf818ca8bf62c7b7bd4cde883fe0f3dcbf631c903fda1f03c05974bb2e3a86dabf7ef1793f5253abbfc846c1bf90be6cbea4208d3f01acdabf48ff983f7c3f06c0a786392e77e1d3bf4598853f2839adbf0c11c8bf800f5cbe6546673f99f4b3bf94f88a3f65a8e3bf60833a30ce94ccbf7344703fb92d8dbfa461a3bf7c3cb0be6d96353f448baabf220c4a3f9221b7bf1d15b2306e6f9cbf8e9b3d3f29fe84bf77d187bf0ca1c2be4005593faa5dcabf4de9683f0d7dd0bf0e34ae304cdcbebfe07d543f528799bfaa14b0bf8e2dcebefab07d3f7cafd3bffcd1833f5f70efbf1131902ed667bebfbdae763f5632a8bfcf1aa9bfc9989040e9b06dc0f01eb6401a0570c03bf6ad40c896d7c03634aa40b06a80c00ab49840326aa840b13f71bf87ec15401fb2f0bf0caf3040caf509c018e9f63f3cb8f8bf8e1ded3f0322ebbf6ba5e3bf28a371bf +\ No newline at end of file ++363632656234393365653936383862396662623134303865623332333461363066633935643264653466326132626331353831633763636239616630353032653233326432366139383465346338376266393132363438333765656463366235613461313434346139333334396265306163333731646537376430643834323664623863366163343363643130313435636565623834363361316133393230363937653835653762353534626439663133633538623062353439646237616630333237666136663433386334626639643465303163653832333062643863333664336630376231643964316231663933656333386338656262303734376137313565643963396535653131303763646533393234333735613333633132353061393531333935623539643834373266303861633739373862366663376365383233326139383939363566373061373361613939336537366631353334346563313061373365663635633332663437653136383235343635623234366430373330366336363237623962656465373233346131343264313137653838643334616430346339363732613237623866636364313232613934343261643231386531356430343965303330306332326266336634626163333461643139653962326566303064343333623037313762303934626336363537616339343637306633633066333231613063623339333539376461316632653234353938616133616463623534356232346135666261616339646638373031356633306161626465643665633066616264623965656138613233353331303236363565616133323131653935643363353832366663633434626236376663386335666364333530336433353234383031636264353761616638663031613263353738326438656265623236653338323232386565626464393034633962373835363264656664616439353336623462376139333134656662373033626135336138333136643032636430653334303861616439333736306363383862306439623962646435383931613161656137356462666463346364333361623035396139323366643262383764363763323530613631653861653634666630333030316562323662346133633566316533346236663132363663663739623565366564343962313033386462653236663864613664303962623535363134336361326534666230613461643036356361623365363830623066373064386262326466363934623535633664373865313164633832393733616432306435663337366633306565386434613131613061636663616630643839623737366361623235653838653366303334613439393362313664373862653234396263323361663762626238373531633765333530666363393132333237616137663336333434326233313735303764323765346238313333393839646334396539663036633034396237393461336263306463333738313939303164393262336465656237616466643562303362333632303838336162666434636265363361333466646564333736303437383432623738636461313736336539396364306164393937313064353136643231663135306364643936646137386434613564373335643430363162623534623133366633353137633236613635643433306239653638666165343231636163376139643564653333336637393138356233633164623465396435396364353031663066643661353130633336303365313365323461343062346266323135653337393561396134306436306138343536666237623633633264313165383137303161323331653835616664346235646238316162633164643536643237313733326564303234356638363931626463356461643334373664366636333536303737623161613164363334613937633431303962353962373335343766393430633164633864323163306631383733383866326433613865656366656537666438363739303266643461633331626131633966646635316261613261343337626663303932613166373332653261386630363637363337343262303737306136653236653332663262653565343761333733303934663366396234313838633834336435633462656135613261346163346262626466376661633763653339623230363235373337373135343130663233646239373035346236656232373065666363666237636234313736663832363831343263656131366532386232316463393539383730306566316462386539616434346537653138343162313462313235636366333561313765386366666138663036383233626434366366383536653461663236336238616166393638626138353062346439616666626563343933666130666530613833336539653436336538376132636365306338633038613133636363613461326134313639623365396134633133666339346337613637616230393963653932636336386561343761626166313638386162386131373230326638646130353133633533353964323735303632336632343431363463396634353338346566303665373533363061373664346462643034663036336636303863636363373830663166313238363531353931346461393730353236373461643039653235366663353938646363643464333835646164353838623664643261303365363139346361393864383734623331623662306231653235333362633134303864306436613862616330613136316136393132323137343831363364646362313231303362316131313733366362386566353536356266663035356633313633323533383133633963343136313330346561336663653535616438303565656331373466386437396361363764323037616334366664653362323562366531643531633464616561653330386532663361613839666339613234623762623864303635363239323732633437313135623561393362653837653361396438373339666232653861316665613661613331383532616631393033383937333463353139343637613931376332666164316634363432366132316638656362363461336166626237613736336638306232313930636165346330323165386264366337316432613138666663393538363731333639396239666362393336373737643238636639643761343231346131333463353261623633343633343866663966663365346237313164613933626232343636366137616430363063643632626237306335353536396233353566633932393964346162636636376563656530396161306664656561626436396635656632346534653861376262386430376664633866626464346666653730326362316232653965343564313031306138623562363130623230313963396265366130303837616232366634326335646531303539353531353137366562353736323737393436626630343433346330356264653562376231356532663139306164303361383836396333393339616236383439363661313661303665643535633961666563613431303466333534346564633533373866306361633035363431646633643837383532663863303765623439623930373061333833633261383932383764356133323539336632353130303266643661643531313731316638343339653162613735393261653833333065316432643865616634333936326236626432663237656439643439633335656534373338313766633338646231316433363030393633613732386331363461303139393161316436323764326235316237613364393537303630633634616137656338386231636561313937653562653562623466633136316330383533636239353561353063623464363362636438656639613233323237373363323039386566366463587754430153274178ab274197be33430e5f1545710254412d9ade3b12f1c13df163bf3d6fc4c73bf7cbb4385e4ed83d0000803f2cb90e3da388cd3cc0d6913df226b13b37ea123df90a1fbdd201113df1081dbcfb240f3d5f280f3dfeddc33cb6dbcc3ca32f563d8f1439bd8f63c43c80ad8abb5381bd3ce8fd2bbc269fcd3cb97ec33c6ec41a3dc386f53c3d5d4d3d6cc298bb9f961a3db4aab0bc95381e3d5729653d44681d3dee941a3d90433fbc70e5923b3d7dbf3cff8520be91bf6dbc27c7c83b5d16f3bcca7df0bc45588fbccd02babc4734773bdea39e3b21d98cbc0efb3bbf915e7f3cbd8a693e81270f3df4800ebcfe88c53c1388723d8111eb3cc873ea3cd5229d3de385e33b7b1ff13c1f0716bde8d4ef3c7d7995bd3a59ef3c01ebed3ca01d9d3e56e4903efb8d87bf0ef454be0ec09f3e0fc3ba3d45b5853e4e5451be51c2933effca853e2089ba3e3c499f3eeca07abfd7e185bed66aba3e803e993c803c9f3e59ad82be5a3aad3e19029e3e40b19a3efa1c8f3e7f218ebf3d244abee2fa9b3e4c9ded3dafba803e967047be2b3c8e3e2442803ee242b13ec290993e4ed57dbf85b277beef3cb13e21ed283de6d4963eb12075be9ca4a43e7c86963e169ca43edf468d3e344288bf0a635fbe2d8da43e240a9a3dfe158b3e4e8862be131a983ede6d8a3e35359e3e6468843e6c3789bf41f853bea7ae9d3e4a86ec3d5b5a843e487c52be8037913e3569843e136ba13eb5b08c3e032d8cbfc8205abe124da13e52e7a33d2958873e76d758bed14e943eef50863e5b649a3e64bd9b3e616e88bfee8256be1afa993ed799e23de378833e478047be97698f3e063d833eefbea03e19e48e3ee8b784bf3e7294be1e05a03e55c5c83dbad5863e94de53bea379943e7209873ef5f9b23e1d759a3ea97e80bf32fb77becb04b33eb3e42b3d4049983e3eab73be7e12a63e9740973ecb00c43eb4cdd7be03fa8fbf8ad246be78a2b63ea3944e3e917d8d3e408b3bbeb9edad3e2c76933ee961ad3e6df49d3ec32481bf59c66ebe9b25ac3e42056c3df161933e17306ebe2feca03e2276923ea226ac3e6864953e869380bfca3a6fbed434ac3e93cf713dc559913e261366be5dcb9e3ecf8c8f3e8308303d1fa8f73c29b77a3d304de5bcc204303dd90991bd8200233dcf8b04bdfd2a2a3d3a5e223da196eb3cd8857e3cae62133d43e191bc3cc4eb3c88b65bbd0ec7d23c208ca8bc9c4ddf3c123fd23c388fd23c0543df3b72d31e3dd47550bc9099d23cb8078fbd9f0aba3c27607fbca930c73cbef0b83c4792f73c5ac3653ccce6d93cbd6898bcc4a1f73c17056dbd3133de3c99d0afbc2a31eb3cc8bfdc3cd64dc63c3223893c15f9053dcda1ceba5355c63cf64b8dbdd8bcac3c21517ebc3385ba3c287dab3cdf90923c831a0c3cc103253d785cd6bb6985923c6b0005bddf89743cc32410bce514873c708b813cd98bfcbbc6f28fbcc4494dbcd18fc53c7a33fcbb31fcc6bb639330bc1387963c4b7115bcbe7831bc34a0ca3cf3eaaa3b8c70263d7d688ebcd3baca3ced9ab2bd1af8bd3ca95b8abcc5dfc03c4e19c33c3e57c33c6d7e153c88ce473dc2c661bd4149c33cec8e39bdbbb4a83c79665bbcb664b63c9306a73ca846bc3c5e51bf3a57a7143d803c5d3b311dbc3c632858bd48fc9d3cdbf91fbcdca2b03c3ff0a33c5ee8143d5b98953c26eba93ce07bc33c9536153d64a087bd8fe1083dffb8dfbc21610f3dd624083d3cca21bcc31ee3bc7851873df730ebbdd9c821bce66076bb12fd52bc0b40943c48e339bc50e855bc7f51a63cf3c6fd3b80c6103d93bb3fbc0340a63cf3e489bdc830913c236b5dbc1a3f9d3ca1b3903cf7b4b23c1cdf853bd0845c3dd0bd8abc58a0b23c3aab2abdf0a89c3c348e66bcdf4ea93cf25a9b3caae50b3d52dd443c2cffa93ce424c03c51d10b3d8acab3bdeb00013d3a51d0bc84f9063d065f003d4303a73c7df79d3bc4fc0b3d804fbd3b0a15a73c82906cbdfd808b3cb7842bbccd80993cbed8893ce798d43c2d2b863cd80e133d73b586bc5ca8d43c09f402bd32ecbc3c383f84bcf163ca3c89e1bc3cb1dad63cd182ed3bdcc55a3de3801ebc1072d63ccba849bd60c8b83cfb299bbcc59fc73caeaeb63c6acc833cc716c9bbef34493d4061b83a43ea833c0617b0bde43c503c90da2cbb06d46e3cb3b3513c783b063d3d5b38bbe97b9d3dc22094bceb19063d1c62c6bdad65f33c0a63bdbc4fe8ff3cacc4f13c930c5d3c8bfe113b84e0443d81407dbb78d25c3c206262bde4282d3c823e5bbbe042483ce69a2b3c3651013d9acd873c07ac0d3d5ba1323bbc47013db7acaebd5359ec3c7a63bbbc53a4f73c704aeb3ce846aa3e5bc5933e155c81bf15296abef633aa3ea748863d5e938f3e088a67be4e479d3e0e278e3e2bb19d3e9d73c43ecbae8cbfd56543be6633a03eb6180b3e27f68c3e98f639be73119c3e9841873e0105a03eae559e3e0be287bfb3ef5dbe5700a03ec1a5a33db862863ed8b558be0436933e5b2d853e25d0b83eb7c09d3e264674bf8ae981beabadb83e10c62b3cf36e9e3ea3a07ebe7779ac3e22e59e3e8849bd3e8fdda23e94197abf5a3b87be853abd3e4d05443c55cea13ec2b984be82e5af3e3a40a03ea7f0bb3eccadb1bde73988bf9c1b6ebe252eb93edc77e13d325e9f3ed03970beefb1ae3e9cf59e3e94a6bb3e6bcaa03eda1f76bf1a6d85bef1a1bb3e080c223c9007a03e8a9482be525dae3e36e99e3eae5f973e822790be10f892bf8f0233be833ca33e112c503e59c27f3e6d2933bea41b943e81d07f3e3a75ac3ef97c943e349b82bfaa346fbe7775ac3e049e3c3d55fe903ed9ba69be0e689f3e9451913e601fa03e984e9d3ecb5988bf948b6cbebd049a3e0f9bd53d10d1853ee89d4ebef969913eefae813ea184923e6f74dc3e613992bf1d5ef2bdb8d27f3e1098493e190a703e826ad7bdf025803ee45d7b3e4e7e9d3ed220883e5c208bbf56964ebe564a9d3e5c20903d36f6823e33b747be0584903e647a813ee3f0a43e3c67903ee40d84bf1f6796becae5a43e7c49963d06228b3e1fbc5fbe1ea6983e68ec893ea15acf3c2013803c957fc13cdb357cbcdb90cf3cce0829bd492fbd3c34ba92bc9e80c63cf106bc3c6b5df43c91ab813c30a6e03c1888a53c5246f43c8d535dbd6205dc3c530ca2bc0127e83c1688da3c2dcef23cc81c2e3c6e022d3dbccf30bc35e3f23c4535a2bd5c38da3ce452a3bcd8bbe73cdc2ed93c5461db3c592fa43c05e8fb3c297c91bca84fdb3c29090fbd0d14c63c39b0a4bc8274d13c64bec43c2556523ddde19c3c3ef5113dc426283dc24c523dd0d6d1bda03f453df5df2fbd2ce44b3dc474443de402d63c672c6e3a61ec333d449869bceef7d53cbde897bd36cbc23cbdc581bc735ecc3c6624c03cdecd76bb47bcac3b9691a33c510366bdc19e7abbd27a2ebc1e2c92bb7c2b45bd98b587bb2bd290bb5c3e963c45a9fd3b00fa2c3d438d63bbaa3d963c3adc24bd32a3783c9c1915bcd75d893cbc5d7b3c52f5a83c1af84a3c82710b3d91790fbc7ae7a83c338218bd88a28f3cef2737bc4d559c3c05508e3c3866d33c6f19a33c670c023d73a51bbb62b1d33c563756bd3d50bb3c8cdf3fbc7f15c83ca09bba3c22478f3ca39fb33b0baa2f3d68addbbb83538f3c1c751bbd3885703c7a6a0abc67ac843ca718723cb056acbce32bd5bc32f9023d116e57bd5083acbcf553963c867cbdbc79a0ff3cb7a2b3bc58bebebca86a783ca906db3bd6f40a3d025f813b8583793cab2f3cbd6e654e3cde6424bbaa50653c6e454c3c17dac43b6bded03a16284f3c34b671bcb01bd73b072529bdd84eae3b97b5f2ba0d11bd3bc523ac3bf743df3ca411a03c7685bb3cc426863c6e17df3cf8c583bdf4aac93c1c18a5bc4531d53c0d47c83c9deec73cd80c4a3cf40eff3c036e8bbc3ccdc73cb3e72fbd17acb13ce69587bc08b4bd3cdf4bb03c175ebd3cd36b493c7c316b3d30ad47bcb786bd3c3f509dbdf735a73cb05d77bc3fa3b23c1b0ca63cc85ce43c75c93e3cf0aa063d232389bcbc4ce43c529f0dbd1305ce3cb8c49ebc869cd93c6eaccc3c4338ae3c12f5883c8666ff3c6addb43c7b2fae3c74fd67bd75b4963cf9f24dbca51ca33ccfcf953cbac4c83cb966b93bcb04103db8775bbc0cb1c83c4ccd56bdf87fb33cb37785bc7b74be3cb042b23cdfd73f3cba0f253bbf531d3d9cb5bcbcc90e403c934dd2bc109c123c63d911bae344293cee1e103c2c318a3c83aed33b4d92203d132343bb04718a3c566e48bdf281683c41df13bc96017f3c14bf653c7dac8f3e97c0763e2cee8dbf6d172cbe18778f3e01e5153ed9746a3e15a32bbe8f9e803e1879633e5496b23ebbcaa63e0f997fbf953778beb389b23ee506f73c824a973e89596fbe1648a53e0a41963ebf0ab63ec04c9b3ef9817abf6bdb7fbe53f6b53eaa16273d362e9c3ee6d079be157aa93e29f49a3ed4eb9c3e83ed9e3e529387bf3d0753be9d199e3e9e24d73d3e05843e214348bedeed8f3e69ef803e0ece983ec72a943e50528abf40573dbe1410983e779c1c3e0c19793e123b3bbe7cb28e3e5067743ecbd5a83e29af8f3e01b983bfa0ac68be68c3a83e1bf84c3d790e8e3efd7763be7adb9b3ea3a28c3ee9f0b93e46afa13eb4917cbfa22386be1cecb93ece69a93c6b029f3e3fb382beace9ac3e66ed9d3e40daae3e8b2ca03e5c6e80bf8af574be0f6eaf3e79c2903d73bd943ea09b70beba6ea13e3b3f933eb147a03e71c4893eadba89bf273f53be558aa03e3acc9f3d8cd3853e94344dbe1402943e1493853eb1f7a53e4927a13ed9e185bf65828ebe7b1fa73eb289bd3d6fb38b3e676860be2248983e7a998b3e7ac0a93e8f79963ed42987bfe8696bbe90e9a93e9c4a823d92e18f3e644868be26679c3e8bd68e3eea77903eeca0ba3e378590bf48673abe1194993eac2f1d3e1581713e01da3cbec9b68a3e15aa783ef257923e07d87c3ebc7e8dbf8e5d3ebef293923eb380cd3dfead6f3e96c53abe4c59853e01616d3e6c22c03ced16bc3b1a9b153dff7a20bdd42ac03c2b694cbd15c4a53cc91a4dbc1a32b33c21d8a33cbd319e3cb3918c3a7c041d3de002593c981a9e3c9854fdbd8432823c08e217bc5a3b913cbac1813cfdc58a3c94e459bb88d9143d612b2ebc45bb8a3c4ac952bd2ef5673c182717bcef037f3cd6356b3c2c2af83c3bd5053c20910d3da175abbc6a37f83c375461bd65abde3c962caebc6132ec3c9511dd3c9a7a8b3c93bcb13b6c3f2c3d4bf800bca5678b3c82b6febc872e683c97be20bccf39803c5689653cebc3cd3c39f40eba331c003dac9987bbc1a5cd3ca3d265bd6ec1b73ccd7b57bc7204c33c9f5bbc3c069fc1bbc6c4ebbc23a0793dd889f2bdbadfc1bbe438d8ba71950fbc4ef8943c1713efbb47f110bca31dc13cd28d1b3cee964f3dbfd759bb3c28c13c699ca0bda0dca43c6a6b3cbcf84fb33c701fa33ca7864c3ddb04fd3c777ef23bdae15a3d689a4c3d447f92bd9ff63f3dddbc2cbddad9463d26963f3dc42a9a3c19e7823b5f281e3d01d30dbc14ab9a3c23d31ebdf063843c9f9e49bc48938f3cdaeb823c5504b63c32e63bbca2e93a3d0c9b29bc4f9fb53cd414b1bde2869b3ca37b46bcf4c7a93c7ca69a3cb914d13cf389f23b3aac033d85e269bc2013d13c031c61bdd747ba3c36e690bc64dec53c3eefb83cac0b933c311910bcd170083dd00ec6ba9022933c069453bd95a1753cafc30fbc3d8c873c0e24763c6f0a2b3c03f36d3b9cb5413daa6320bd12242d3c627eb7bc5996153c05fc58bd2ac5243cc50e133c6d8ea43c0a10ce3a172d2f3d30c81e3cbd80a43c0a997abd28dd8a3ce1fd44bc4e12983c287b893c47c1113d8e2c9f3ce295123d15bde43cbda6113dfec5c1bd9516023d5e06bebce9700a3dde4a013d9886c43c6e20273b3c5ff53cc30e60bc4fc1c43cdf7fe8bcf370ae3c391a7ebca88eba3c1daaad3c6b86903ce290163b03360e3da759fbbbb857903ce37414bd2d0d753c11921cbce514863cdbc7733c3523923cd22736bb60f70c3dbd9dbabb621c923cad34f2bce147703ce156f3bbce1c863cb9796e3ce7da993c4782053c7415213d99f8c9bb2ff6993cbab564bd718f813c0d3f24bc513d8e3cbfd4893c30e5033da286243ca09b733dc912b6bcdce5033d3e2cafbdd645ee3ca92abdbc2c6cfb3ce2d7ec3c22b7ef3c6a973b3ce0d1d63c558b6fbcecdeef3c880274bda28ad73c0d07adbc9fc1e33ccbfcd53cc9f9ae3ede4e893eeeb685bf989567be5e7aae3e0fcb2b3df540933e825563be0dfca13e0951923e36c2ac3e6958b63d81ee89bf51ce64be8d0fad3ed4cfc23da1fb8e3ecd7a5fbe26639e3eda248d3ea1b0a63ee3288f3e21bb82bf69f961bea282a63ec633753dc6108b3e31915cbe40f6983e69b9893e1b37a33e7df58a3e70fa88bfadc060bea237a33eab35993d2582883e808c5abec00c963ebdda873e7cca9f3ea7e08b3efb3e88bfe92457be91ac9f3e76048c3d8794843e740451bed5a3923e9eec833ec662933e9a4d983e07be8dbf040249befff5933e342efa3dbcb2783e430243be8a388a3ed773783e8ff49a3e00aca53e190c8fbfaea851be838c9a3ef21a093efa2d823ef71732be9e66863e835e7b3e0128b23edb7d0bbea3638ebf347061be70deb03ee88ce93d87d7933ec9fb61bec783a23e244c943e7515a73ea5728f3e2e8387bf187f68be690ca73e140d7d3d22ab8c3ed07462be70219a3ece628b3e65eeb03eaf5a923e1df27fbf7e716fbee9b1b03e4371593d10f4953eab676fbe8305a33e6ef8933e4062a13e12cfab3ef2a385bf43fc91bec2e6a03e049bd33de41c8d3e4a1456be3908963e6cc5863e64d2bd3ef2d7a33e33b17dbfd26c88be0fd5bd3ea6e5823c22f1a23e4fc485beb13bb13ea079a23e40e7bb3e08eea03e2d2176bfd80286be2bdabb3ebaf34f3c6e95a13e998e82be5f3eaf3e0456a03ef02fbe3cb7835f3a6190183d0f6801bc7275be3cc390afbd1caca53c2f1975bc5d59b23c7928a73c087b393d96d11b3dc91e893c54fe903dee79393d4d4a9bbd5a392d3df75c16bdd48a333d63962c3d2210b93c7fdf463c94bf383dfa52fcbb3c0ab93c98a223bd39509e3c12295cbc7238ac3c5ab89c3c0a60a93cce1edb3b3cc6fe3cba1932bcd757a93cf7964abd6d40923cef4757bc78229e3cadd3903cf651de3cfee98e3c25dd503d8f2f9dbcc39cde3c76a96ebdeaaac63c995a9ebc5275d33c4531c73cb2150b3d7baf813c0c61903d2eb094bc541b0b3d006468bdd820fc3cabe0c7bc17b2043db0affa3c925b9e3cf9dd063c7c24593d9e9ae9bbb94c9e3c2fbf78bdbdad863cb47a26bcada1923c754c853c409eb63c83d7aa3b1a461b3d947342bc7ec7b63c7d5546bdee109e3cb2176cbc95eaaa3c08ca9c3c5892af3c78e1453c99010f3dc3a163bd22f9af3c00afd6bc4f1f9a3cbd0b50bcf6bba53cb989993ce353983c14be3e3cd90e2a3dff8d8ebdd685983cf087adbc208f833cb53383bdae108e3c97cf823c9607a43ca1a6933bad801b3d7c8894bb1f2aa43c4a9620bd2f8d8d3cbe9a38bc8bdf993c59f28b3c917fc63cbb0e173c7b66153d2a9bf13c2ceec63c2dfca8bd4532ae3c091f82bc2fadbb3cc07bad3c003bc83c5a9fa0bb9b0a183d73412ebcecacc63c6a48adbd0b78ac3c262250bc6f03bc3cf6e2a93ceb04d7bbf183bbbc4b88683dc410d1bdbe01d7bb6aabecbbe56619bcd335793cd9c801bc5b0d1cbc6e5c1a3ddb1ebf3c81c2863cb2ad113dac1b1a3d7d4ba3bde0e50e3dea50d5bc61b9143d43380e3d77d5cf3cdb7d3c3c41bf243d959285bc0ebbcf3ce85ed6bced6bb73c68d185bc4811c53c1d49b73cb3efd13c7733923c3594083d038a81bc98fbd13c502e40bdc842ba3c2b6194bceb3fc63cb3ebb83cc09bdc3c1ccc883c3e62043d22fb39bcc460dc3c6b2086bd90dbc03cd72655bc05e6ce3cfa2dbf3c2c2f8e3cea09d83b275b2f3d0211a8bbc4d88d3c110545bda4d16b3c3016f2bb1a25823c74a9683c341cbe3cd4740f3c67428a3c26b39f3a6c46c23c95a125bd282baf3c5f5b4fbbd274ba3cb679af3cc7829c3ce375df3bab4e313daad3d13a6a119c3c52c75fbd927b7d3c35b519bce6c88d3ca5c6793ca755983ce850843b5dd4043dd22b273c6592973c2fae55bd4b77853c3db180bb75c78e3cb3c4883cc96f9a3e96bc9c3ef0f887bf04d349be18239f3eb887be3d0219833eba374dbe2c38913e64c0843e4995aa3efc07923e30eb80bf49f16cbe5699aa3e489d823d1816903e748b67be8e729d3ee1ed8e3e07f4bb3e40b0a13e69fd7abfd56086be39d8bb3e2915803c10d9a03ee4d083be02ceae3e30e19f3e4797a73e87788f3edf1088bf87ae68bea18fa73e38c07c3dafec8c3eadd362beadfd9a3ec5838d3e38ceb23ea3b6993e14177ebfb09e7bbe3be2b23ed6bcee3cdb4c973e732075be889fa53e4d72963ef6dbb03eb8e29b3e2b527dbf121d8bbe1d24b13ebc1fc33c1b04963ec6b963be597ca33e8872953e8c37a53e42a5983e4e3988bfc6b669be1affa43e0fdfbc3d60978b3eebe361be9a42993e7ba18b3e5a289b3e2a96a33e4d3b87bffbb33ebe81ba973e08d6f63d38a5833e3e3137be80d98b3ea600773e60abab3e33f79a3e72ae86bf3b4b66be418bab3e82ab9b3dde9e8f3ec17767be7bb69c3ee3e68f3e20aaa93eb7a79a3ede4883bf95146bbe393da93e16324a3da3a18e3e003d65beb93c9c3ec4ec8c3ebc33973eee7aa23e73678abfeda138be796d973e070e1d3e66f77a3e644836be89ba8f3e75d6813ee98e9a3e95fa943ef28b87bfe66d4bbe38fc9e3e7f54fe3d5e8a823ea9e045be2fb3913e2676863e2e3c8c3e302aac3eb30b8fbf4df01ebece99833eb8b22c3e41e6593ed6b021be33d77c3eebe4653e64b6dd3c23c1213c08edd43cbf7d66bcddcbdd3c993b3ebdb445c43c3dc492bcf061d13cc981c33cb82ee93c1b7e5b3c7db3f83c503552bac311e93cce7b63bd788fd23ce40aa9bc9c33df3ceabdd03cdef0d43ca2f657b9c07b263d3f6a80bcaed9d43c201f63bd9787b83c338d74bc74d9c63c46b5b63c494aa03c443c7b3a3332fc3c2eecffbb0413a03c8e271ebd661b8b3c8bb044bc8610963c45d7893ce14ff33c1bbfa53a921bfd3c163974bcbafbf23c328f73bde18ada3c3e854abc3701e73cbf0cd93cbb66bd3c159717bb7d640e3d71bd79bbbb68bd3cfb1794bd1dd9a03c7eba3ebc6426af3cc50c9f3ced8b7d3ce15e453a9441f33cf626dcbb35747d3c65b892bda7a6533cbd8407bc7cdf693c950a523cbcc1be3c355c983a62b8443dcfc051bc8ba8be3cc388adbded13a53c791b67bcbf2fb23c7e82a33cc7e3083d3100063c956a093db14284bc4cc8083d18a252bd297cf43cd8a1bdbcbfaf013dc0bdf23c21e9cb3c1da5ea3b4b5b1b3d787a97ba6db7cb3c225c81bd42dbb33c232762bc26dcc23c75f3b13ccb47e5bba1c7c9bc7369843d6fb8d2bd3f8de5bbc2ac69bc6a9627bca1b5823cc3cc0cbc5db226bc39bb36b913854cbc64e94ebbf9b6963c26f937b9761305bce65e56bb1870903cea0de0baf6b063bb33920f3d7c73583c82d3733d0c4cc3bc548d0f3d195d90bdb191023d3f78d4bc301c093db6c5013d7f318a3c2fb774ba2c981e3d6f2a9e3cb4228a3c7c67b6bd21ff613cba810ebcda667b3c59435f3c3015483cab130c39dfff533d03441dbd5a5e473c6233b4bc348b1e3c24773abd800c353c9c1d1f3cf7a3673cc1903ab9f985323d79ba9d3a514a683c45a14abd3c8a393cfa88d3bb6bd5523c7193363c801d9f3c86f2fa3be68d1b3d29ec05bbf9759f3c595b65bd62ca853ccbeb19bcfd31933cf691843ce622953c589be53b8752243d0f6c32bc3536953c2d8242bce09f7c3cac121abc19248b3c75057b3ca00fa83ce060d83a64c5223da97a8abbc459a83c43becfbca5838d3c7c3f07bc22ad9a3c16d88b3c9927b03c4339ab3bea03f83c5f2122bcd312b03c608389bd06f6963cb44f3dbc3bd5a33c263b983cb7e5ce3cc5bb25bcba13393d3ced51bc2a61ce3c3d2781bd3121b83c2d8483bca0f9c33c7fbab63ccec6123d9c6f913cd42fa43c0d5d4e3d43b0123dc4ab98bd2095073d8e66e8bc32490d3dee28073d820c9c3ee5e7883e21d58bbf115e41be6998a63e8851ed3d4fa3823eb1323bbe3b03973eb62b833e9a929a3e9a719b3ef6b091bfd34d19bed30c8d3e09a5383ea0ec683e812e21befbcf8e3ec5e07c3ed040a33e91ef8b3e5e9b84bf75778bbee639a33ee8b2ac3d1d35893e7d095bbe6b60963ececc873e97d3a53ead48913e532387bfd3e364beed95a53e7944933dc5ce8b3e9c415fbe34e1983ed2ad8a3e1927b03ee5cc983e371882bfc65b78be4824b03e1f154b3df0ce953e79de72bea0a8a33e2d9f943e3e56c03ef31ca43e1b6972bf83bf88be0e48c03edc8e833ba500a43ef16385be98c7b23e34f9a23e544ca63e32dc7e3eb13a85bfafc55fbe54e6a53e1712b23d84678b3e234a5dbe45a5983e97808a3e42a3a83ebc55a23e10d084bf46ce64be9d7aa83e1b14a43d60648f3e808565be19669c3e62108e3e56d6a13eb755893e75b487bf84315dbe1bc7a13e3cde703d574f873ea04b56be5aeb943e5df0853e223d933edf64b73e54898bbf43cc20beb317943e6760343e0e13753eb3c526beccd9853efe237c3e591bad3e081d643ecd7b86bf56b86dbeab68ad3e41b2583df740913eaace67be689e9f3e288a8f3e93cea73e83a9923e6b6483bf176e69be48aca73e617c983d43498e3e029265be25fb9a3e87ae8c3e32d8a23ebf44843ef16887bfb02450be4c5fa33e7fecc33d8c718b3e782054be3aed963efd5c893ed197083dcaba823c6113003d1cae0f3cacb5083dd46f9fbda316f83cdbe4ccbc0474023d09acf63cf8a6a83c5da90d3ac2861d3dfc1c47bad185a83cebcf9abd2455903c090e47bc1b9f9c3c726e8f3cad5dbe3ba90d22bb26f5cbbac2f77ebbf18ebe3b8fbe61bccbda5d3b5457a93aa94f9b3b58725c3b8563c53cea9e0f3cdd7cec3c546664bc054cc53caf5188bdbf3caf3cd0847abc8750bb3c382cae3c96589c3c4c5e353bd5f21e3d3ec868bc097d9c3cdd365fbd631c863cb8fa28bc6aca923c92dc863c38d4023d838a433c574ec93c22538e3c5bcc023de06d7dbd2e20e93c2f91b5bc52f4f73c71cce73c4f17753c9c991dbbbba1763d53f09fbd9d567a3c9d630cbc6c514a3cb4e034bdee6c563c1e974c3ccdfd873cb2e7b4bb7f3f483da5fca1bbf09c873c78507bbdd2295f3c5f6b0ebc2687783c5f4c633c704d5c3c304cb33a9edd243d3909ac3add755c3cc30a80bdfd9e2b3c4af28bbb8f92453c8e9e293c339ba23c4f2696bbaa0e013d8d6e8fbbd490a23ce0cb93bd10dd893cd1033fbcebb0953cc9c6933cd4cfbe3c3d45ef3bd61b503d5fa76dbcd091be3cd92976bd2c77a73c6d477ebc56edb33c1d5aa63c3ae62a3d0a49a63c9efba73c13784c3d7a9f2a3d4e4ca7bdcad11e3db19005bd80fb243dde7b213db4ecc33c6c3e6f3bb1b8113d2bb68ebcd3b2c33cdf003fbd2ca4ac3ccd8e73bc81f4b83c7849ab3cd2a2c03cfc709339a2e0043d9f8a02bc674cc03c0e9944bd126bab3cb92c62bc6320b93c78eaaa3cca0b2cbc73e0e2bccd76913d2c9cedbd9d162cbc8d589dbbc6f962bc9c75a43c0cda46bc6fdb65bc15af763c68bd443b2e02223d90eb9c3cfa48763c99de89bd36dc4c3c1a0adfbb84da673ce7a74f3ceefdd93cfa98193cab141c3d4446613a71c4d93c1d159ebd08c2c03c465f82bc8be7ce3cd619bf3c282a963c5267223b7598253d5f769dbba94d963caddeeebc2d6b7d3c791924bc711f8b3c3272853ca4c5283b893cacbb26eeadbcb0603b3c27ad293ba5c885bcef5a083ae687fa3b27d5c93af2ad903a4cc7bc3c922b3d3c99ed353d0af795bb3addbc3c1dee89bd5326a13c16fa39bc0d1faf3cb1729f3cd0c2963cdb520439c743163daface8bb63d8963c72d358bdc0457b3cf4bdd0bb57548a3c9f707b3ce93eb03c516d2f3be5ea483d377824bcfc9ab03c24e597bd7440973cd88540bc90d1a43c7fce953c730ab03e7ed7a03e649b7fbfea7277bef776b23ee665563de5b2973ea9a975be64cea43e75dc963e1318963e01e98d3e0a908abf14b24abe0ada973e6d38cf3dfc3f7c3eab5145be6db48d3e8a187d3ea856aa3e3eeb943e098885bf636f6dbe9845aa3ec25c833d759c903e90d668be010d9e3ec46c8f3edf6ba13e098b973ee31087bf41665cbed9aba03e21aab53d048f863ed47554bed218943e8e2e863e7252a13e0b488c3e1ef985bf1e7a52bed3a0a13e0f0a923dc2ef863e4b774dbe0c93943e5c35863eb193a83e6f21973e641e85bff43869be96d4a83ecd648c3da6e68e3e92f464bec6209c3e80b58d3ea3f1ad3eb81e7e3ec19082bfa91d59be150bb53e0be5be3dc248943ee22866be46f7a23ee39b933e183ca03e868b873e1f0e89bfa7ef57becf28a03e59f58a3db348853eb2f651bed0f7923e290c843e66aaaa3ec9f78b3efe8886bf3ea95dbeadb1aa3e10eca03df3848e3eb8f563be7c409c3e33188e3e5086b83e91d987bedd0c8dbfdd9944be8ad8b13ee3c0353e5f5a953e190947be5457aa3ec113923e6b8d983ed309843e1c9a8cbfb46768be3855983e9869ec3db8227f3e0c164cbed8718c3e141e7e3e6b12be3ed1c9a23e500277bfa39387befff0bd3e2217f63bee7aa23e6d3784be1cdcb03ef3e2a03efebe9f3ec75c873e660a88bf546053be14b69f3e2d29853dfa5e843e830a51beb669923ee522833ed5f47d3c76dc8f3bab3a1f3ded72fdbb3be77d3cf4e503bd9edc4f3c1cf6debbaa59683cf778523c00b3ea3cd168373c2a152d3dc6670e3c4babea3c54a297bd60ffcc3c7c7996bc8041dc3ccf46cd3cd515a23c5d27983b1db9543dd402bcbb583ea23c993a54bd696e873cb54f1bbb634e953cb9c1853c112cf43ca1daba3ce697123d55674dbda931f53c184700bd0ea9dc3c3ca4b5bc630be93c6b22db3c428f083d9b33ca3c588b9b3dc59ebebc94b4083d08b764bdf676f63c7756bdbc0530023dc1fbf43c90ffbb3cc097523b7b69ea3ca90736bc6981bc3c5ab0fdbcd2f0a33c8a5b76bc1caab03c2b6ca43cf022953c81d5a5bac9373e3d626923bb3645953cf4dd58bdfafe7b3c2e1d30bc0dd8893ced6f7d3c6a1bcd3c1ff5543ccbeec83c1834f73caf07cd3cf0065dbdeccab73c856485bc0957c33c806ab63c38359f3c60ba3a3b3042253d8eaa443cc0ca9f3c50463abd6ccf813c26430eba7da2913c09c38a3c6a92b03c1852ce3bbcbae73c768b84bcfcbbb03c7bab71bd17b19b3c5c4068bc4b6da63ce9859a3cdbb3eb3cab0b733b4cbb053d146391bcfe6ceb3ca8806cbd100dd33cfeb3b0bcbf29e03c48f9d33ce397a43cc819043c0102583db3ab2fbc04c4a43c963166bd0319933ce3085abc9d119f3c5e04953cfcd6183d0831663c9cca383d6a098bbc8830193dccdf8fbded6e083dca0eaabca265113d73d9073d33b1373dcc15fb3cc9e0363c1692963df9a7373d8b2993bde8a9293d8abf11bdf1c7303d11d4283df46f653c1aab2bbcdbc1573d40e0e4babece673ccdbc5cbdcca1353c5092b2bb5d8a503c55ce323c0049d43c3a96863c4b83cd3c55c24d3d70b5d43cf82395bd9359bb3c3cd575bc2748c83c06d6ba3cdd90a63cbc29fd3b9fca203d928e5ebcd6d0a63cfc7b61bd96478e3cde524fbca0e99a3c67b18c3cf720153df7f3c63c58cde33c91acc0bc6815153de88348bd93ec073de7c2d7bcede90e3dc719073dfc32dcba0cccd8ba7f520f3b705e9ebdad49f9ba6f3624bc9c576abb2e7a213c846338bbb17f67bb9c86ebbb2e80c1bc7528853dcc27f6bd9108ebbb0db074bcc59127bc06ea863cdc230ebc1aa31fbcaef2f03cbdb8433cf0f3fb3cbc398dbce23df13c294209bd57edd63cd970a3bc5f74e43c5f97d53cc6e0f43b263ca03b333a563df14285bde690f53bfa138abb2adfb13bcf476fbd80ccd43b9cb0b03bdc17a23ef5c88f3efff784bf69036abe752da23e15b0863d8f6e883e32d259be758b953e431d873edc94b13efa2e9a3e72c780bfeb4281be3f9cb13ecf97363d9abb973e3ae279be51f9a43ea3b4983e4b5dab3e1ef4a03e18c185bf09666fbe89a1ac3e4846a63de1ef933e533e6dbe0a45a23e67bb933e7dcfb33efb11623e2b7384bfeebd54be3dd7b73ed5da9a3d0995973e0d055abe6469a93ef0e9993ec156a03eb186a33e1d4586bfd30f4ebe68999f3e821cd73d6ec3883e844d56be4742953e4ecc873e0d9d9a3e034f9c3e8a148cbfae2543bef7979e3e80d30a3e191f7d3e130448bed044903e62cd843e9721ab3ed76b913e2a5b83bf4ea18ebe5221ab3e6ba97a3de55c903e5cf566be3e6b9d3e78ae8e3e118c933e9fe9af3eb02f89bfd9d23cbe0cd9923ee37a073ec27f7c3e629338be808b893ef4c9823eedd4883e646f8d3e256995bfa1c925be98e1883e7eb2193e4e7f693ef5a325be5d467e3e009e6e3e32b4963ea7d58b3e0b758cbfcaa23bbe4b09953e186ae43d63f7783ec1613abe51bf873eee75753e2df1b73e59b59c3e00ed77bf176881be2fcfb73e8aa5023d55fe9b3ec86c7cbe9c84aa3e77be9a3e11f1a53ef114703eace587bf6d174bbe8258ac3e6a80f73db853893ee53154be25599c3e9281913e1a16983ecfd8ae3ecd138cbf996a47beddbb983efc4ff73d81387f3e681e49be65048c3e69b67c3ea8cadf3c28886e3c1307163d12f153bc72f3df3c4a7411bd18b9c53c287593bca1fdd23c4829c73c4a6e863cfaea7fbbdd75153da83e8bbbe749863cebb61dbd4300593c517fddbb0f98733c7af6583c0643e93c9dc787bb7be7553d801e88bc6611e93c8091a3bd098cd13c5bbda5bc684cdf3cc0dbd23c5d2c2c3d69548d3c0781c53c500ba33cc20a2c3d0393bdbda3421c3d70a1e1bc4bb6243dc4751b3d064a9b3c78d86a3a64dc1a3d7659193dfc439b3c5ae2a4bd5235813c4e7c0bbc5bbd8e3cdb707f3c25bcf23c49910b3c7d24253dc8acb4bcf699f23c2d5da7bd31a7d83c2912a2bc5437e73c7852db3c77f0c13cd0691d3b8678fb3c6094a33b33ecc13c4ec984bd3eeba53c14ed72bc013cb53c53b6a53cb0e92abb330ba8bcfb3a653ddc610ebe12dc29bb930b8839d9c7b2bba3eb373ca3787bbb902cb8bb43efd93c352ee83be083123dee1f8fbc66d7d93c57c392bc1001c23c36c58ebc0c95cf3c3e72c03c63b0ae3c889ecd3b4c7f143ddd0d40bccd99ae3cc33668bd25c6943c407656bc44e0a13c7e39933c0b68f83ce7dc593c7010f23cab366dbc4059f83c74bd8dbda48bde3ce4708dbcac03ec3ca118dd3cbb37c43c7473503cda1ded3c38583cbcd168c43cb401d7bc0550ac3cd0107bbc347db83c6ed9b33cca52a7bafadb3fbc4fa884bc8ab8953ccd17a8ba5a4d8fbcdb5694bb8895723c216b3dbb035c9abb1817ae3cd69cef3a4086193d7eae30ba6d33ae3c4dd42cbd0c80923c48ecbdbbf87ca03c7bb1903c1c9e813c68c040bc13403c3d74af21bda092813c584f23bd697d553ced45bbbbe61f6d3c0ed4523ce9d0c43c4944153c78c6103d209eedbb62e7c43c6ce52ebda354a83c510045bc745cb73c0685a73cd8a3823c1a4cb7bb733f333d30f0f13a44ee833c8cb91dbdf4885a3c54a6c0bba98f6f3c5a3a5a3cf714f13c10568f3c19b8393c8e53b83c249cf03ceeb8b8bded0cdc3c046facbceca5e63c59cada3c2dee9f3cb6e7ab3ad28c2d3dbd9f29bce43aa03c6d824dbdb36d883cd7f238bcf200973c8bec883c4dbee53c205f273b946af43cc5fe443b6ca3e53caa017dbdacbfc93ce4f38fbc0a3bd93c34d5c83ce4e46d3cc4784aba4a8e323d779a253b86e76d3c60f15bbd5dd0393c213e9dbbd1ba543c35dc373c6aaa293db865963c2eee063dc46f17bdfcce293df449c7bd8ab91e3d330905bd0985243dc79b1e3d6d82b33effd4973eafb089bf0c185bbed15cb43eab30603d6623993eb08774beee85a73e1581993e172e9d3eda51f53d0d938fbfcdda97bedad49b3eaedbec3d684d803e12de45bee7918e3ef8eb7d3ec828973ebd9f973ea62b8bbfa44749be47d3953e8b70073eefaf863ea4233dbeb66f953e8177843e6f06a83e0861a63ef8d587bff78551be175da93efb5dae3d14258d3efc625fbe2db69c3e688a8b3e2230a33e1a7b8f3e864289bfd23960be6f91a33e8f737c3d14c5893ef3245dbe3cca973e70d1883e5b4d9c3e2066953e4f0c8bbf4f3557be77639e3ea49cac3d9c05833e0d9351bea7b0913e31e1813e3edba43ebfd38d3e262284bf163b6bbe03daa43e878a5c3d3c8e8a3e61395abe780e983e6268893ec55bb33e540d9c3e3b8281bfcaba81beb853b33e40af323d37cc993eb6847dbe41b7a63e78c7983e2414993ef5f892bec46d94bf75fa1bbe10e59a3e7c9a7b3ea53a7f3e354107beed0fa63e1253933e638ba23ed9a78d3e7a8789bf98845fbe9a6ca23e0d587f3dd78e873e0eef53bead09953e26ee853eade2bb3ec607a13e6a437cbff23186bef9e9bb3e1204ed3cd250a03e4b9b82be7d7fae3ef81b9f3e3137b43e0c4d9c3ee8ce81bf636081be0f22b43ebb53263d0f4c9a3eabc37cbec4dda73e52e0993ee3d4b13e9ba9873eb3bd7cbf99a772be6eb7b13e20e7443d66d3953e3bdd6abe492ea43eb922943eb8170d3d72c6a73c9126893dbdc6bfbcdf0d0d3dee9b4fbda53c003d6c50d9bc9dc2063d54ecfe3c0aa2ce3c99dc5f3cd2dfa93c894494bcf151cf3c0b713bbd1753c03c8bc49ebcde53c83cf8bcbf3c827ecd3c8cb95e3caee7f53c00795c3d3d8acd3cb4ef8bbd5002b33c65d719bc3063c43c849cb83c55bdf83c4199863c7347633de43eb2bcfda6f83c79d4acbd9c5fe03c6c1ca1bc0008ed3c818ce03c2a63e23c0cc7a93c2588243d4809e43bf840e23c72d2cfbde883cc3c10eea3bcb72bd83c312ccb3c0449dc3c0f33f93b13cd243d419c45bc51f2db3c1c3d6fbd25b5c13cbc5e80bcf463cf3ce12cc03cd2199f3cb9a19d3b4482163d6c2bd0bbbc039f3cdf3306bd045b873c320a40bc024a933c5054863c9a5f9e3cca664d3bd7d3833d867369bd68de9e3c444a15bd4664863c67ad64bdb24a9b3cd9188d3ccae4a83cf4dbd13b06e4283d8a06fa3890cba83c949384bd2a1e8f3c05734cbc91689f3c96cb903cd1b4a93c94e4b33b7fd80f3d107f1bbcbb3baa3c68f10cbda64b923c592b56bca5849e3ca5de903c0334c13cce18b93b9f3f673dfa8631bca040c13c7daf86bd1ad4a63cf01572bcc302b53c6d42a53c2f36df3c9e22083cb016543d6d4c5cbcde08df3c42599dbdbc80c53c625f6ebc0591d23c4dd9c33cae59aa3cb2fc4e3b762c253d5761d03b7052aa3c3e618ebd4475913c859144bcc44a9e3c8df88f3c09aa863c47492b390b0fad3c17ce0b3d4656863cc1715ebde2766b3ce3cb05bc2905773c2235693c4e8ac63c0b59873c52cac93c5624b0bc8ab7c63cd514e2bc159fb23c646693bc63e9bc3c9409b33cb999833c0c33d93a95c2383df7b702bd367f833c061ddcbc0cca533c9ae4a5bb22fa6c3c695e513ccf92c83c9331823c65f1633dd4252fbc4f91c83cc1dd66bddd28ac3ce66a6abc93b2ba3ce865aa3c7050e33c31e34c3ce11c2c3d588d80bc4780e33c35f740bdd66bca3ca64099bcda44d73cae0ac93c0e3eb13cad4de33bebcc033d0a95b7bbb52fb13cbac576bd6831963ce05f09bca6f7a33c01ba943cc31f8a3cfc90e23bf2b0373dab405d39740b8a3cf67968bdb619603c647ee5bba66b7b3cc13d5d3ccd4ebabbd308b5bce3fe7d3d5385debd4859babb20e1cbbbbeb613bcbe229b3c4e8ae8bb26b811bc5d2fa13c032a2e3c0932063db9098ebcd252a13c6dbe5bbdd1e5903c48455bbc9e599b3cdbec8f3c88a39b3e65cf8a3eab9388bfa45656beb5739c3e26f6d43db006833e655e54be46568f3e0a13823e66d9a23e4a06943e1f0688bf09014bbecca1a23e89d19d3d34e3883e303b46befc1d963e7a31893e5835ad3eda26943e4e2884bfd92171be1235ad3e30d8133d1d47923e18446bbe2469a03ef82b913e26fdab3e23af963ecb9880bfaa8178bedaffab3eeb355f3d2a76913e28fc69be2c9b9e3e75a98f3eb27cb13ee4d1983e6f3581bf45917abe437eb13e3952293dcdbd963e6e1d74bed99ca43ee083953e3643a53e6b4e9f3e76e187bfc68e38be1fb9a23eda5abe3d5585893e977658be86e0963e2ccc8b3e3f47a33e92508d3eba0d88bf421a3abed58ea43e1bc1cd3d1015863e9e263fbeadef973e0f9d863e0875a53e23963a3e15e289bf9e524dbe4068a73e9f73e33d41028a3eaa9b4dbe8f6e973e614c8b3e7aa18a3ec72da33e84148fbfdc6e26beefca8e3eff033a3ef00d6f3eef7d28be002f803ec78c753e9a969b3e7619933e8e1b88bffca361be72929c3e64c3bc3d9dae823e435a4fbe25168f3ef835833e196bb13e6b369b3e198e84bf3d9180bed558b13e1453423d598e963e699974be397aa43e57d2953e5776a63e1be09f3e20ef80bfcda98cbee320a73e78e49a3d3e348f3e8e2964be6b1d9f3e69fd903e5109b93e36f5a03e069f7dbf872683be0203b93ed9d3a33c853a9e3ec0d380be58abab3ecfa79d3eb1eccb3c7bf5c53badcf1b3dfdf334bc3ee4cb3ca96835bd490bb23cf3ed79bc995bbf3c5279b03c5e1cb33cf6fb1e3a396f6b3dea2909bd1332b33c0de8cfbc1db79a3c5c0913bd1a3ca73cd736993ceca63c3c0c594abb63f8113d3d3b67bb14cf3c3c544b1bbd0b6e173c05b17cbb1aa62a3c3daa163cfc91093d7771953c37be013d3301e53b86ae093daa78c6bdd65cfd3c157ed1bccb9d043d5850fc3cb72106bc838eedbcfe987e3dbf3905beab7206bc638e69bbdf963abc6a8b963c04811ebca9ba3cbceae4f33cc580443c506d253d2ff2c1bb67f8f33c21bcb6bdbe22de3c43d8bcbc003ceb3c55aee43c05598b3caf968ebae5445a3d54c920bc2a2c8b3c0db078bd4896663c06d315bcca697f3c6903653c81ba553cbed6e83b2a243c3c5c26fc3b6c514a3c6340f2bc85cd233c9ecfd83954ca433cc67e2a3ce5bbac3c679e803b0900fa3c1a5f3ebc6eb3ac3c6df84ebd5ff5963c71cd6ebc23f9a13c41a5953c27929e3cc454133c9ea5093d77c040bc56ad9e3ce8acebbc2f72883c290a48bc484c943c845a873c91fdc03c46d3513c5b383a3d84fb79bc240ec13cbcf554bd7d10a73ce28d55bca73ab43c7686a53cebbfdb3cfe20e53bd2ff573de6ac73bca6bddb3c21d78ebd3d06bf3c4bba7abcea96cd3c0b54bd3cd449013cb35c20bc98b8023c3963b13bd39f013c04c9dcbcb219a83b37f0823bd29dd53b8b80a23be52df53cb222773b5f5b1d3d6ed672bce134f53c668f85bd5393da3cc44aa9bc9f62e83c1135da3c687a433d84fdf93c422c9f3cf78cac3d6d7f433d6009bdbdf7b0363d29111fbd456e3d3d58a0363d9255dc3ca950523bb778dc3c95428fbc794adc3c68cd46bd5314c13c1e81c0bbd319cf3c808ac03c1c6a8b3c39817e3a474e2e3dfa0f173caa568b3c2f2aaabd9d6c693c61ef07bcbf43803c9fad663c3faabf3c4a3d163c74e3f73c3d8c3dbb84a9bf3c83c79dbdd605a73c4a5e7fbc0c06b43c0cf9a53c2dc4da3cab84673c7056263d788f7dbc85a6da3c275122bd36cdc03cdfab94bc7cdbcd3c7141bf3c6edcf83b0712a6ba17098c3c483a0c3cd2f7fc3b0a50c6bc64deb73b5549193c21b3d83b5f52b43b78dec53c1403173c8fe6053d619053bc4aa0c53cd2bb41bdc518ac3c25ac7bbca3abb93ced81ab3cfda5e63ca2a9483cace2063dc2aa83bc2094e63cbaa92dbd3360cf3c1c7fa4bc9f76db3c3a8dce3c760a00404305a83fd2f79ebe24efc6bf787c004029cbdbbfae13e33f0c8ab3bfcb70f33f5832e13f1ff80140400cac3f9ce15dbea00ec9bf13c10240de3eefbf6f9de93fdb33c1bfe38ff83fce98e83fae8c064079bfac3fa3fc31be2af4c1bfd2ef06400bf0f3bfb7f4eb3f2aeebcbfab92fd3f0814ea3fe37673bfee12e2bed21e0f33fbc90f3f452076bfd3531a3f75943cbf80b40b3f71ef63bf163540bfcb0c8dbf5bde10bfd63996335731d83e56598abfa866263fd2ed4abffbbf0c3f39c278bfea2b5ebf503473bfc52da5be4f8d0837740d063f679f63bfaf30103fb6743fbfe75bfd3eba575dbf9d933abf5ffe77bf4abd08bf68351734e60ed93eb95b6cbfb45b073f8c2940bfbb28f03e49a74fbf0b6b36bf38ed89bfc856a8be7bf03234db750b3f2b707dbfc4831c3f3c3949bff020043faad57cbf11ff5bbf2a1651bf483afcbe862ec633063ce63ec33f55bf22a7083fb47f28bfae94f53e10fe4dbf690c33bfb3aa65bfb58025bf72474e34b17be83e6f5f81bf514a183f88b341bf01df013f36055cbf275a3ebfdf8d8e40dc717140d84d39c1f13b4bc06d6a8e40910d63c0accc7040928e45c01ab483401b416e40865ed3bf71c0a8bf29b7a53f8bcdfd3f034cd8bf4fb7e23f674cc9bf8eb0ff3fa92cd5bfa593cebff54f50bf +\ No newline at end of file +diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc +index ee94723fc..e88038a16 100644 +--- a/gcc/opts-common.cc ++++ b/gcc/opts-common.cc +@@ -993,90 +993,6 @@ opts_concat (const char *first, ...) + return newstr; + } + +-static int +-handle_lto_option (unsigned int lang_mask, +- unsigned int num_decoded_options, +- unsigned int argc, +- const char **argv, +- struct cl_decoded_option *&opt_array) +-{ +- int ret = 0; +- char *compiler = xstrdup (argv[0]); +- char *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] = { +- global_options.x_param_simultaneous_prefetches, +- global_options.x_param_l1_cache_size, +- global_options.x_param_l1_cache_line_size, +- global_options.x_param_l2_cache_size, +- global_options.x_param_prefetch_latency, +- global_options.x_param_ipa_prefetch_distance_factor}; +- int64_t output_pred = get_optimize_decision_from_optimizer ( +- 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 +@@ -1218,9 +1134,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); +- + if (lto_skip_stat == NEED_TO_SKIP) + { + const char * nolto = "-fno-lto"; +diff --git a/gcc/opts-global.cc b/gcc/opts-global.cc +index 843ace666..79c8a963d 100644 +--- a/gcc/opts-global.cc ++++ b/gcc/opts-global.cc +@@ -311,7 +311,9 @@ decode_options (struct gcc_options *opts, struct gcc_options *opts_set, + global_options.x_param_l2_cache_size, + global_options.x_param_prefetch_latency, + global_options.x_param_ipa_prefetch_distance_factor); ++ + const char *tune_native = getenv ("GCC_AI4C_TUNE_INFO"); ++ + if (tune_native != nullptr) + { + prepare_native_tune_str (tune_native); +@@ -350,6 +352,31 @@ decode_options (struct gcc_options *opts, struct gcc_options *opts_set, + } + } + ++/* handle lto options according to model inference result */ ++void handle_lto_options(struct gcc_options *opts, char* compiler) ++{ ++ const char *model_infer_level = getenv ("AI_INFER_LEVEL"); ++ if (model_infer_level) ++ { ++ char *lan = strrchr (compiler, '/'); ++ if (lan != NULL) ++ lan ++; ++ else ++ lan = compiler; ++ if (strstr (lan, "cc1") != NULL || strstr (lan, "lto1") != NULL) ++ { ++ global_options.x_flag_generate_lto = 1; ++ global_options.x_flag_lto_partition = LTO_PARTITION_ONE; ++ global_options.x_flag_lto = "8"; ++ } ++ else if (strstr (lan, "gfortran") || strstr (lan, "cc1plus") || strstr (lan, "f951")) ++ { ++ global_options.x_flag_generate_lto = 1; ++ global_options.x_flag_lto = "8"; ++ } ++ } ++} ++ + /* Hold command-line options associated with stack limitation. */ + const char *opt_fstack_limit_symbol_arg = NULL; + int opt_fstack_limit_register_no = -1; +diff --git a/gcc/opts.h b/gcc/opts.h +index a43ce66cf..04ee995da 100644 +--- a/gcc/opts.h ++++ b/gcc/opts.h +@@ -386,6 +386,7 @@ extern void decode_options (struct gcc_options *opts, + location_t loc, + diagnostic_context *dc, + void (*target_option_override_hook) (void)); ++extern void handle_lto_options(struct gcc_options *opts, char* compiler); + extern int option_enabled (int opt_idx, unsigned lang_mask, void *opts); + + extern bool get_option_state (struct gcc_options *, int, +diff --git a/gcc/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/struct-reorg.exp b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +index 34606d025..452a8c606 100644 +--- a/gcc/testsuite/gcc.dg/struct/struct-reorg.exp ++++ b/gcc/testsuite/gcc.dg/struct/struct-reorg.exp +@@ -77,6 +77,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/toplev.cc b/gcc/toplev.cc +index bdbd4de63..9cda18601 100644 +--- a/gcc/toplev.cc ++++ b/gcc/toplev.cc +@@ -88,7 +88,7 @@ along with GCC; see the file COPYING3. If not see + #include "ipa-modref.h" + #include "ipa-param-manipulation.h" + #include "dbgcnt.h" +- ++#include "ai4c-infer.h" + #include "selftest.h" + + #ifdef HAVE_isl +@@ -2250,6 +2250,9 @@ toplev::main (int argc, char **argv) + UNKNOWN_LOCATION, global_dc, + targetm.target_option.override); + ++ char *compiler = xstrdup (argv[0]); ++ handle_lto_options(&global_options, compiler); ++ + handle_common_deferred_options (); + + init_local_tick (); diff --git a/gcc.spec b/gcc.spec index d68dffc8dc42dc1c6b0c39527e39219922cb0617..19dd2634e5ec022093c09dcff0e25d0bf204980a 100644 --- a/gcc.spec +++ b/gcc.spec @@ -2,7 +2,7 @@ %global gcc_major 12 # Note, gcc_release must be integer, if you want to add suffixes to # %%{release}, append them after %%{gcc_release} on Release: line. -%global gcc_release 81 +%global gcc_release 82 %global _unpackaged_files_terminate_build 0 %global _performance_build 1 @@ -485,6 +485,7 @@ Patch372: 0372-oeAware-Add-.GCC4OE_oeAware-section-for-optimization.patch Patch373: 0373-Include-insn-opinit.h-in-PLUGIN_H-PR110610.patch Patch374: 0374-Add-hip12-instructions-pipeline.patch Patch375: 0375-SVE-Fix-gcc-cross-compile-error.patch +Patch376: 0376-Struct-dynamic-field-compression-optimization.patch # Part 1001-1999 %ifarch sw_64 @@ -1646,6 +1647,7 @@ not stable, so plugins must be rebuilt any time GCC is updated. %patch -P373 -p1 %patch -P374 -p1 %patch -P375 -p1 +%patch -P376 -p1 %ifarch sw_64 %patch -P1001 -p1 @@ -4273,6 +4275,10 @@ end %doc rpm.doc/changelogs/libcc1/ChangeLog* %changelog +* Wed May 14 2025 huzife <634763349@qq.com> - 12.3.1-82 +- Type: Sync +- DESC: Add struct dynamic-field-compression optimization + * Thu May 8 2025 zhangjingwang - 12.3.1-81 - Type: Bugfix - DESC: Fix gcc-cross compile error.